imapext-2007

view src/osdep/nt/ssl_nt.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line source
1 /* ========================================================================
2 * Copyright 1988-2008 University of Washington
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *
11 * ========================================================================
12 */
14 /*
15 * Program: SSL authentication/encryption module for Windows 9x and NT
16 *
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
24 *
25 * Date: 22 September 1998
26 * Last Edited: 13 January 2008
27 */
29 #define SECURITY_WIN32
30 #include <sspi.h>
31 #include <schannel.h>
34 #define SSLBUFLEN 8192
37 /* SSL I/O stream */
39 typedef struct ssl_stream {
40 TCPSTREAM *tcpstream; /* TCP stream */
41 CredHandle cred; /* SSL credentials */
42 CtxtHandle context; /* SSL context */
43 /* stream encryption sizes */
44 SecPkgContext_StreamSizes sizes;
45 size_t bufsize;
46 int ictr; /* input counter */
47 char *iptr; /* input pointer */
48 int iextractr; /* extra input counter */
49 char *iextraptr; /* extra input pointer */
50 char *ibuf; /* input buffer */
51 char *obuf; /* output buffer */
52 } SSLSTREAM;
54 #include "sslio.h"
57 /* Function prototypes */
59 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
60 static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
61 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
62 long *contd);
63 static long ssl_abort (SSLSTREAM *stream);
65 /* Secure Sockets Layer network driver dispatch */
67 static struct ssl_driver ssldriver = {
68 ssl_open, /* open connection */
69 ssl_aopen, /* open preauthenticated connection */
70 ssl_getline, /* get a line */
71 ssl_getbuffer, /* get a buffer */
72 ssl_soutr, /* output pushed data */
73 ssl_sout, /* output string */
74 ssl_close, /* close connection */
75 ssl_host, /* return host name */
76 ssl_remotehost, /* return remote host name */
77 ssl_port, /* return port number */
78 ssl_localhost /* return local host name */
79 };
81 /* security function table */
82 static SecurityFunctionTable *sft = NIL;
83 static unsigned long ssltsz = 0;/* SSL maximum token length */
86 /* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */
88 typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD);
89 typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,
90 HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,
91 PCCERT_CHAIN_CONTEXT *);
92 typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT,
93 PCERT_CHAIN_POLICY_PARA,
94 PCERT_CHAIN_POLICY_STATUS);
95 typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT);
96 typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT);
98 static CNTS certNameToStr = NIL;
99 static CGCC certGetCertificateChain = NIL;
100 static CVCCP certVerifyCertificateChainPolicy = NIL;
101 static CFCC certFreeCertificateChain = NIL;
102 static CFCCX certFreeCertificateContext = NIL;
104 /* One-time SSL initialization */
106 static int sslonceonly = 0;
108 void ssl_onceonlyinit (void)
109 {
110 if (!sslonceonly++) { /* only need to call it once */
111 HINSTANCE lib;
112 FARPROC pi;
113 ULONG np;
114 SecPkgInfo *pp;
115 int i;
116 /* get security library */
117 if (((lib = LoadLibrary ("schannel.dll")) ||
118 (lib = LoadLibrary ("security.dll"))) &&
119 (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
120 (sft = (SecurityFunctionTable *) pi ()) &&
121 !(sft->EnumerateSecurityPackages (&np,&pp))) {
122 /* look for an SSL package */
123 for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
124 /* note maximum token size and name */
125 ssltsz = pp[i].cbMaxToken;
126 /* apply runtime linkage */
127 mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
128 mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
129 if ((lib = LoadLibrary ("crypt32.dll")) &&
130 (certGetCertificateChain = (CGCC)
131 GetProcAddress (lib,"CertGetCertificateChain")) &&
132 (certVerifyCertificateChainPolicy = (CVCCP)
133 GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) &&
134 (certFreeCertificateChain = (CFCC)
135 GetProcAddress (lib,"CertFreeCertificateChain")) &&
136 (certFreeCertificateContext = (CFCCX)
137 GetProcAddress (lib,"CertFreeCertificateContext")))
138 certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA");
139 return; /* all done */
140 }
141 }
142 }
143 }
145 /* SSL open
146 * Accepts: host name
147 * contact service name
148 * contact port number
149 * Returns: SSL stream if success else NIL
150 */
152 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
153 {
154 TCPSTREAM *stream = tcp_open (host,service,port);
155 return stream ? ssl_start (stream,host,port) : NIL;
156 }
159 /* SSL authenticated open
160 * Accepts: host name
161 * service name
162 * returned user name buffer
163 * Returns: SSL stream if success else NIL
164 */
166 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
167 {
168 return NIL; /* don't use this mechanism with SSL */
169 }
171 /* Start SSL/TLS negotiations
172 * Accepts: open TCP stream of session
173 * user's host name
174 * flags
175 * Returns: SSL stream if success else NIL
176 */
178 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
179 {
180 SECURITY_STATUS e;
181 ULONG a;
182 TimeStamp t;
183 SecBuffer ibuf[2],obuf[1];
184 SecBufferDesc ibufs,obufs;
185 SCHANNEL_CRED tlscred;
186 CERT_CONTEXT *cert = NIL;
187 CERT_CHAIN_PARA chparam;
188 CERT_CHAIN_CONTEXT *chain;
189 SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
190 CERT_CHAIN_POLICY_PARA polparam;
191 CERT_CHAIN_POLICY_STATUS status;
192 char tmp[MAILTMPLEN],certname[256];
193 char *reason = NIL;
194 ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
195 ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
196 ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
197 ISC_REQ_MANUAL_CRED_VALIDATION;
198 LPSTR usage[] = {
199 szOID_PKIX_KP_SERVER_AUTH,
200 szOID_SERVER_GATED_CRYPTO,
201 szOID_SGC_NETSCAPE
202 };
203 PWSTR whost = NIL;
204 char *buf = (char *) fs_get (ssltsz);
205 unsigned long size = 0;
206 sslcertificatequery_t scq =
207 (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
208 sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
209 SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
210 sizeof (SSLSTREAM));
211 stream->tcpstream = tstream; /* bind TCP stream */
212 /* initialize TLS credential */
213 memset (&tlscred,0,sizeof (SCHANNEL_CRED));
214 tlscred.dwVersion = SCHANNEL_CRED_VERSION;
215 tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
217 /* acquire credentials */
218 if (sft->AcquireCredentialsHandle
219 (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
220 &tlscred : NIL,NIL,NIL,&stream->cred,&t)
221 != SEC_E_OK) reason = "Acquire credentials handle failed";
222 else while (!reason) { /* negotiate security context */
223 /* initialize buffers */
224 ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
225 ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
226 obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
227 ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
228 ibuf[1].BufferType = SECBUFFER_EMPTY;
229 /* initialize buffer descriptors */
230 ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
231 ibufs.cBuffers = 2; obufs.cBuffers = 1;
232 ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
233 /* negotiate security */
234 e = sft->InitializeSecurityContext
235 (&stream->cred,size ? &stream->context : NIL,host,req,0,
236 SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
237 /* have an output buffer we need to send? */
238 if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
239 if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
240 reason = "Unexpected TCP output disconnect";
241 /* free the buffer */
242 sft->FreeContextBuffer (obuf[0].pvBuffer);
243 }
244 if (!reason) switch (e) { /* negotiation state */
245 case SEC_I_INCOMPLETE_CREDENTIALS:
246 break; /* server wants client auth */
247 case SEC_I_CONTINUE_NEEDED:
248 if (size) { /* continue, read any data? */
249 /* yes, anything regurgiated back to us? */
250 if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
251 /* yes, set this as the new data */
252 memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
253 size = ibuf[1].cbBuffer;
254 break;
255 }
256 size = 0; /* otherwise, read more stuff from server */
257 }
258 case SEC_E_INCOMPLETE_MESSAGE:
259 /* need to read more data from server */
260 if (!tcp_getdata (stream->tcpstream))
261 reason = "Unexpected TCP input disconnect";
262 else {
263 memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
264 size += stream->tcpstream->ictr;
265 /* empty it from TCP's buffers */
266 stream->tcpstream->iptr += stream->tcpstream->ictr;
267 stream->tcpstream->ictr = 0;
268 }
269 break;
271 case SEC_E_OK: /* success, any data to be regurgitated? */
272 if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
273 /* yes, set this as the new data */
274 memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
275 buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
276 stream->tcpstream->ictr = ibuf[1].cbBuffer;
277 }
278 if (certNameToStr && !(flags & NET_NOVALIDATECERT)) {
279 /* need validation, make wchar of host */
280 if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
281 (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
282 MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
283 fatal ("Can't make wchar of host name!");
284 /* get certificate */
285 if ((sft->QueryContextAttributes
286 (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
287 SEC_E_OK) || !cert) {
288 reason = "*Unable to get certificate";
289 strcpy (certname,"<no certificate>");
290 }
291 else { /* get certificate subject name */
292 (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
293 &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
294 certname,255);
295 /* build certificate chain */
296 memset (&chparam,0,sizeof (chparam));
297 chparam.cbSize = sizeof (chparam);
298 chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
299 chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
300 chparam.RequestedUsage.Usage.cUsageIdentifier =
301 sizeof (usage) / sizeof (LPSTR);
302 if (!(*certGetCertificateChain)
303 (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
304 reason = ssl_analyze_status (GetLastError (),tmp);
305 else { /* validate certificate chain */
306 memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
307 policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
308 policy.dwAuthType = AUTHTYPE_SERVER;
309 policy.fdwChecks = NIL;
310 policy.pwszServerName = whost;
311 memset (&polparam,0,sizeof (polparam));
312 polparam.cbSize = sizeof (polparam);
313 polparam.pvExtraPolicyPara = &policy;
314 memset (&status,0,sizeof (status));
315 status.cbSize = sizeof (status);
316 if (!(*certVerifyCertificateChainPolicy)
317 (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
318 reason = ssl_analyze_status (GetLastError (),tmp);
319 else if (status.dwError)
320 reason = ssl_analyze_status (status.dwError,tmp);
321 (*certFreeCertificateChain) (chain);
322 }
323 (*certFreeCertificateContext) (cert);
324 }
325 if (whost) fs_give ((void **) &whost);
327 if (reason) { /* got an error? */
328 /* application callback */
329 if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
330 host,certname) ? NIL : "";
331 else if (*certname) { /* error message to return via mm_log() */
332 sprintf (buf,"*%.128s: %.255s",
333 (*reason == '*') ? reason + 1 : reason,certname);
334 reason = buf;
335 }
336 }
337 }
338 if (reason ||
339 (reason = ssl_analyze_status
340 (sft->QueryContextAttributes
341 (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
342 break; /* error in certificate or getting sizes */
343 fs_give ((void **) &buf); /* flush temporary buffer */
344 /* make maximum-sized buffers */
345 stream->bufsize = stream->sizes.cbHeader +
346 stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
347 if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
348 fatal ("cbMaximumMessage is less than SSLBUFLEN!");
349 else if (stream->sizes.cbMaximumMessage < 16384) {
350 sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
351 (long) stream->sizes.cbMaximumMessage);
352 mm_log (tmp,NIL);
353 }
354 stream->ibuf = (char *) fs_get (stream->bufsize);
355 stream->obuf = (char *) fs_get (stream->bufsize);
356 return stream;
357 default:
358 reason = ssl_analyze_status (e,buf);
359 }
360 }
361 ssl_close (stream); /* failed to do SSL */
362 stream = NIL; /* no stream returned */
363 switch (*reason) { /* analyze reason */
364 case '*': /* certificate failure */
365 ++reason; /* skip over certificate failure indication */
366 /* pass to error callback */
367 if (sf) (*sf) (host,reason,flags);
368 else { /* no error callback, build error message */
369 sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
370 mm_log (tmp,ERROR);
371 }
372 case '\0': /* user answered no to certificate callback */
373 if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
374 stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
375 sizeof (SSLSTREAM));
376 break;
377 default: /* non-certificate failure */
378 if (flags & NET_TRYSSL); /* no error output if tryssl */
379 /* pass to error callback */
380 else if (sf) (*sf) (host,reason,flags);
381 else { /* no error callback, build error message */
382 sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
383 mm_log (tmp,ERROR);
384 }
385 break;
386 }
387 fs_give ((void **) &buf); /* flush temporary buffer */
388 return stream;
389 }
391 /* Generate error text from SSL error code
392 * Accepts: SSL status
393 * scratch buffer
394 * Returns: text if error status, else NIL
395 */
397 static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
398 {
399 switch (err) {
400 case SEC_E_OK: /* no error */
401 case SEC_I_CONTINUE_NEEDED:
402 case SEC_I_INCOMPLETE_CREDENTIALS:
403 case SEC_E_INCOMPLETE_MESSAGE:
404 return NIL;
405 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
406 mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
407 return "*No authority could be contacted for authentication";
408 case SEC_E_WRONG_PRINCIPAL:
409 mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
410 case CERT_E_CN_NO_MATCH:
411 return "*Server name does not match certificate";
412 case SEC_E_UNTRUSTED_ROOT:
413 mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
414 case CERT_E_UNTRUSTEDROOT:
415 return "*Self-signed certificate or untrusted authority";
416 case SEC_E_CERT_EXPIRED:
417 mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
418 case CERT_E_EXPIRED:
419 return "*Certificate has expired";
420 case CERT_E_REVOKED:
421 return "*Certificate revoked";
422 case SEC_E_INVALID_TOKEN:
423 return "Invalid token, probably not an SSL server";
424 case SEC_E_UNSUPPORTED_FUNCTION:
425 return "SSL not supported on this machine - upgrade your system software";
426 }
427 sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
428 return buf;
429 }
431 /* SSL receive line
432 * Accepts: SSL stream
433 * Returns: text line string or NIL if failure
434 */
436 char *ssl_getline (SSLSTREAM *stream)
437 {
438 unsigned long n,contd;
439 char *ret = ssl_getline_work (stream,&n,&contd);
440 if (ret && contd) { /* got a line needing continuation? */
441 STRINGLIST *stl = mail_newstringlist ();
442 STRINGLIST *stc = stl;
443 do { /* collect additional lines */
444 stc->text.data = (unsigned char *) ret;
445 stc->text.size = n;
446 stc = stc->next = mail_newstringlist ();
447 ret = ssl_getline_work (stream,&n,&contd);
448 } while (ret && contd);
449 if (ret) { /* stash final part of line on list */
450 stc->text.data = (unsigned char *) ret;
451 stc->text.size = n;
452 /* determine how large a buffer we need */
453 for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
454 ret = fs_get (n + 1); /* copy parts into buffer */
455 for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
456 memcpy (ret + n,stc->text.data,stc->text.size);
457 ret[n] = '\0';
458 }
459 mail_free_stringlist (&stl);/* either way, done with list */
460 }
461 return ret;
462 }
464 /* SSL receive line or partial line
465 * Accepts: SSL stream
466 * pointer to return size
467 * pointer to return continuation flag
468 * Returns: text line string, size and continuation flag, or NIL if failure
469 */
471 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
472 long *contd)
473 {
474 unsigned long n;
475 char *s,*ret,c,d;
476 *contd = NIL; /* assume no continuation */
477 /* make sure have data */
478 if (!ssl_getdata (stream)) return NIL;
479 for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
480 d = *stream->iptr++; /* slurp another character */
481 if ((c == '\015') && (d == '\012')) {
482 ret = (char *) fs_get (n--);
483 memcpy (ret,s,*size = n); /* copy into a free storage string */
484 ret[n] = '\0'; /* tie off string with null */
485 return ret;
486 }
487 }
488 /* copy partial string from buffer */
489 memcpy ((ret = (char *) fs_get (n)),s,*size = n);
490 /* get more data from the net */
491 if (!ssl_getdata (stream)) fs_give ((void **) &ret);
492 /* special case of newline broken by buffer */
493 else if ((c == '\015') && (*stream->iptr == '\012')) {
494 stream->iptr++; /* eat the line feed */
495 stream->ictr--;
496 ret[*size = --n] = '\0'; /* tie off string with null */
497 }
498 else *contd = LONGT; /* continuation needed */
499 return ret;
500 }
502 /* SSL receive buffer
503 * Accepts: SSL stream
504 * size in bytes
505 * buffer to read into
506 * Returns: T if success, NIL otherwise
507 */
509 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
510 {
511 unsigned long n;
512 while (size > 0) { /* until request satisfied */
513 if (!ssl_getdata (stream)) return NIL;
514 n = min (size,stream->ictr);/* number of bytes to transfer */
515 /* do the copy */
516 memcpy (buffer,stream->iptr,n);
517 buffer += n; /* update pointer */
518 stream->iptr += n;
519 size -= n; /* update # of bytes to do */
520 stream->ictr -= n;
521 }
522 buffer[0] = '\0'; /* tie off string */
523 return T;
524 }
526 /* SSL receive data
527 * Accepts: TCP/IP stream
528 * Returns: T if success, NIL otherwise
529 */
531 long ssl_getdata (SSLSTREAM *stream)
532 {
533 while (stream->ictr < 1) { /* decrypted buffer empty? */
534 SECURITY_STATUS status;
535 SecBuffer buf[4];
536 SecBufferDesc msg;
537 size_t i;
538 size_t n = 0; /* initially no bytes to decrypt */
539 do { /* yes, make sure have data from TCP */
540 if (stream->iextractr) { /* have previous unread data? */
541 memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
542 n += stream->iextractr; /* update number of bytes read */
543 stream->iextractr = 0; /* no more extra data */
544 }
545 else { /* read from TCP */
546 if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
547 /* maximum amount of data to copy */
548 if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
549 fatal ("incomplete SecBuffer exceeds maximum buffer size");
550 /* do the copy */
551 memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
552 stream->tcpstream->iptr += i;
553 stream->tcpstream->ictr -= i;
554 n += i; /* update number of bytes to decrypt */
555 }
556 buf[0].cbBuffer = n; /* first SecBuffer gets data */
557 buf[0].pvBuffer = stream->ibuf;
558 buf[0].BufferType = SECBUFFER_DATA;
559 /* subsequent ones are for spares */
560 buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
561 SECBUFFER_EMPTY;
562 msg.ulVersion = SECBUFFER_VERSION;
563 msg.cBuffers = 4; /* number of SecBuffers */
564 msg.pBuffers = buf; /* first SecBuffer */
566 } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
567 (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
568 switch (status) {
569 case SEC_E_OK: /* won */
570 case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
571 /* hunt for a buffer */
572 for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
573 if (i < 4) { /* found a buffer? */
574 /* yes, set up pointer and counter */
575 stream->iptr = buf[i].pvBuffer;
576 stream->ictr = buf[i].cbBuffer;
577 /* any unprocessed data? */
578 while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
579 /* yes, note for next time around */
580 stream->iextraptr = buf[i].pvBuffer;
581 stream->iextractr = buf[i].cbBuffer;
582 }
583 }
584 break;
585 default: /* anything else means we've lost */
586 return ssl_abort (stream);
587 }
588 }
589 return LONGT;
590 }
592 /* SSL send string as record
593 * Accepts: SSL stream
594 * string pointer
595 * Returns: T if success else NIL
596 */
598 long ssl_soutr (SSLSTREAM *stream,char *string)
599 {
600 return ssl_sout (stream,string,(unsigned long) strlen (string));
601 }
604 /* SSL send string
605 * Accepts: SSL stream
606 * string pointer
607 * byte count
608 * Returns: T if success else NIL
609 */
611 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
612 {
613 SecBuffer buf[4];
614 SecBufferDesc msg;
615 char *s;
616 size_t n;
617 if (!stream->tcpstream) return NIL;
618 /* until request satisfied */
619 for (s = stream->ibuf,n = 0; size;) {
620 /* header */
621 buf[0].BufferType = SECBUFFER_STREAM_HEADER;
622 memset (buf[0].pvBuffer = stream->obuf,0,
623 buf[0].cbBuffer = stream->sizes.cbHeader);
624 /* message (up to maximum size) */
625 buf[1].BufferType = SECBUFFER_DATA;
626 memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
627 buf[1].cbBuffer = min (size,SSLBUFLEN));
628 /* trailer */
629 buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
630 memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
631 buf[2].cbBuffer = stream->sizes.cbTrailer);
632 /* spare */
633 buf[3].BufferType = SECBUFFER_EMPTY;
634 msg.ulVersion = SECBUFFER_VERSION;
635 msg.cBuffers = 4; /* number of SecBuffers */
636 msg.pBuffers = buf; /* first SecBuffer */
637 string += buf[1].cbBuffer;
638 size -= buf[1].cbBuffer; /* this many bytes processed */
639 /* encrypt and send message */
640 if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
641 (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
642 !tcp_sout (stream->tcpstream,stream->obuf,
643 buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
644 return ssl_abort (stream);/* encryption or sending failed */
645 }
646 return LONGT;
647 }
649 /* SSL close
650 * Accepts: SSL stream
651 */
653 void ssl_close (SSLSTREAM *stream)
654 {
655 ssl_abort (stream); /* nuke the stream */
656 fs_give ((void **) &stream); /* flush the stream */
657 }
660 /* SSL abort stream
661 * Accepts: SSL stream
662 * Returns: NIL always
663 */
665 static long ssl_abort (SSLSTREAM *stream)
666 {
667 if (stream->tcpstream) { /* close TCP stream */
668 sft->DeleteSecurityContext (&stream->context);
669 sft->FreeCredentialHandle (&stream->cred);
670 tcp_close (stream->tcpstream);
671 stream->tcpstream = NIL;
672 }
673 if (stream->ibuf) fs_give ((void **) &stream->ibuf);
674 if (stream->obuf) fs_give ((void **) &stream->obuf);
675 return NIL;
676 }
678 /* SSL get host name
679 * Accepts: SSL stream
680 * Returns: host name for this stream
681 */
683 char *ssl_host (SSLSTREAM *stream)
684 {
685 return tcp_host (stream->tcpstream);
686 }
689 /* SSL get remote host name
690 * Accepts: SSL stream
691 * Returns: host name for this stream
692 */
694 char *ssl_remotehost (SSLSTREAM *stream)
695 {
696 return tcp_remotehost (stream->tcpstream);
697 }
700 /* SSL return port for this stream
701 * Accepts: SSL stream
702 * Returns: port number for this stream
703 */
705 unsigned long ssl_port (SSLSTREAM *stream)
706 {
707 return tcp_port (stream->tcpstream);
708 }
711 /* SSL get local host name
712 * Accepts: SSL stream
713 * Returns: local host name
714 */
716 char *ssl_localhost (SSLSTREAM *stream)
717 {
718 return tcp_localhost (stream->tcpstream);
719 }
721 #include "ssl_none.c" /* currently no server support */

UW-IMAP'd extensions by yuuji