imapext-2007
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/osdep/nt/ssl_nt.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,721 @@ 1.4 +/* ======================================================================== 1.5 + * Copyright 1988-2008 University of Washington 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * 1.14 + * ======================================================================== 1.15 + */ 1.16 + 1.17 +/* 1.18 + * Program: SSL authentication/encryption module for Windows 9x and NT 1.19 + * 1.20 + * Author: Mark Crispin 1.21 + * Networks and Distributed Computing 1.22 + * Computing & Communications 1.23 + * University of Washington 1.24 + * Administration Building, AG-44 1.25 + * Seattle, WA 98195 1.26 + * Internet: MRC@CAC.Washington.EDU 1.27 + * 1.28 + * Date: 22 September 1998 1.29 + * Last Edited: 13 January 2008 1.30 + */ 1.31 + 1.32 +#define SECURITY_WIN32 1.33 +#include <sspi.h> 1.34 +#include <schannel.h> 1.35 + 1.36 + 1.37 +#define SSLBUFLEN 8192 1.38 + 1.39 + 1.40 +/* SSL I/O stream */ 1.41 + 1.42 +typedef struct ssl_stream { 1.43 + TCPSTREAM *tcpstream; /* TCP stream */ 1.44 + CredHandle cred; /* SSL credentials */ 1.45 + CtxtHandle context; /* SSL context */ 1.46 + /* stream encryption sizes */ 1.47 + SecPkgContext_StreamSizes sizes; 1.48 + size_t bufsize; 1.49 + int ictr; /* input counter */ 1.50 + char *iptr; /* input pointer */ 1.51 + int iextractr; /* extra input counter */ 1.52 + char *iextraptr; /* extra input pointer */ 1.53 + char *ibuf; /* input buffer */ 1.54 + char *obuf; /* output buffer */ 1.55 +} SSLSTREAM; 1.56 + 1.57 +#include "sslio.h" 1.58 + 1.59 + 1.60 +/* Function prototypes */ 1.61 + 1.62 +static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags); 1.63 +static char *ssl_analyze_status (SECURITY_STATUS err,char *buf); 1.64 +static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size, 1.65 + long *contd); 1.66 +static long ssl_abort (SSLSTREAM *stream); 1.67 + 1.68 +/* Secure Sockets Layer network driver dispatch */ 1.69 + 1.70 +static struct ssl_driver ssldriver = { 1.71 + ssl_open, /* open connection */ 1.72 + ssl_aopen, /* open preauthenticated connection */ 1.73 + ssl_getline, /* get a line */ 1.74 + ssl_getbuffer, /* get a buffer */ 1.75 + ssl_soutr, /* output pushed data */ 1.76 + ssl_sout, /* output string */ 1.77 + ssl_close, /* close connection */ 1.78 + ssl_host, /* return host name */ 1.79 + ssl_remotehost, /* return remote host name */ 1.80 + ssl_port, /* return port number */ 1.81 + ssl_localhost /* return local host name */ 1.82 +}; 1.83 + 1.84 + /* security function table */ 1.85 +static SecurityFunctionTable *sft = NIL; 1.86 +static unsigned long ssltsz = 0;/* SSL maximum token length */ 1.87 + 1.88 + 1.89 +/* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */ 1.90 + 1.91 +typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD); 1.92 +typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME, 1.93 + HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID, 1.94 + PCCERT_CHAIN_CONTEXT *); 1.95 +typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT, 1.96 + PCERT_CHAIN_POLICY_PARA, 1.97 + PCERT_CHAIN_POLICY_STATUS); 1.98 +typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT); 1.99 +typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT); 1.100 + 1.101 +static CNTS certNameToStr = NIL; 1.102 +static CGCC certGetCertificateChain = NIL; 1.103 +static CVCCP certVerifyCertificateChainPolicy = NIL; 1.104 +static CFCC certFreeCertificateChain = NIL; 1.105 +static CFCCX certFreeCertificateContext = NIL; 1.106 + 1.107 +/* One-time SSL initialization */ 1.108 + 1.109 +static int sslonceonly = 0; 1.110 + 1.111 +void ssl_onceonlyinit (void) 1.112 +{ 1.113 + if (!sslonceonly++) { /* only need to call it once */ 1.114 + HINSTANCE lib; 1.115 + FARPROC pi; 1.116 + ULONG np; 1.117 + SecPkgInfo *pp; 1.118 + int i; 1.119 + /* get security library */ 1.120 + if (((lib = LoadLibrary ("schannel.dll")) || 1.121 + (lib = LoadLibrary ("security.dll"))) && 1.122 + (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) && 1.123 + (sft = (SecurityFunctionTable *) pi ()) && 1.124 + !(sft->EnumerateSecurityPackages (&np,&pp))) { 1.125 + /* look for an SSL package */ 1.126 + for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) { 1.127 + /* note maximum token size and name */ 1.128 + ssltsz = pp[i].cbMaxToken; 1.129 + /* apply runtime linkage */ 1.130 + mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver); 1.131 + mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start); 1.132 + if ((lib = LoadLibrary ("crypt32.dll")) && 1.133 + (certGetCertificateChain = (CGCC) 1.134 + GetProcAddress (lib,"CertGetCertificateChain")) && 1.135 + (certVerifyCertificateChainPolicy = (CVCCP) 1.136 + GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) && 1.137 + (certFreeCertificateChain = (CFCC) 1.138 + GetProcAddress (lib,"CertFreeCertificateChain")) && 1.139 + (certFreeCertificateContext = (CFCCX) 1.140 + GetProcAddress (lib,"CertFreeCertificateContext"))) 1.141 + certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA"); 1.142 + return; /* all done */ 1.143 + } 1.144 + } 1.145 + } 1.146 +} 1.147 + 1.148 +/* SSL open 1.149 + * Accepts: host name 1.150 + * contact service name 1.151 + * contact port number 1.152 + * Returns: SSL stream if success else NIL 1.153 + */ 1.154 + 1.155 +SSLSTREAM *ssl_open (char *host,char *service,unsigned long port) 1.156 +{ 1.157 + TCPSTREAM *stream = tcp_open (host,service,port); 1.158 + return stream ? ssl_start (stream,host,port) : NIL; 1.159 +} 1.160 + 1.161 + 1.162 +/* SSL authenticated open 1.163 + * Accepts: host name 1.164 + * service name 1.165 + * returned user name buffer 1.166 + * Returns: SSL stream if success else NIL 1.167 + */ 1.168 + 1.169 +SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf) 1.170 +{ 1.171 + return NIL; /* don't use this mechanism with SSL */ 1.172 +} 1.173 + 1.174 +/* Start SSL/TLS negotiations 1.175 + * Accepts: open TCP stream of session 1.176 + * user's host name 1.177 + * flags 1.178 + * Returns: SSL stream if success else NIL 1.179 + */ 1.180 + 1.181 +static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags) 1.182 +{ 1.183 + SECURITY_STATUS e; 1.184 + ULONG a; 1.185 + TimeStamp t; 1.186 + SecBuffer ibuf[2],obuf[1]; 1.187 + SecBufferDesc ibufs,obufs; 1.188 + SCHANNEL_CRED tlscred; 1.189 + CERT_CONTEXT *cert = NIL; 1.190 + CERT_CHAIN_PARA chparam; 1.191 + CERT_CHAIN_CONTEXT *chain; 1.192 + SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy; 1.193 + CERT_CHAIN_POLICY_PARA polparam; 1.194 + CERT_CHAIN_POLICY_STATUS status; 1.195 + char tmp[MAILTMPLEN],certname[256]; 1.196 + char *reason = NIL; 1.197 + ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | 1.198 + ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | 1.199 + ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | 1.200 + ISC_REQ_MANUAL_CRED_VALIDATION; 1.201 + LPSTR usage[] = { 1.202 + szOID_PKIX_KP_SERVER_AUTH, 1.203 + szOID_SERVER_GATED_CRYPTO, 1.204 + szOID_SGC_NETSCAPE 1.205 + }; 1.206 + PWSTR whost = NIL; 1.207 + char *buf = (char *) fs_get (ssltsz); 1.208 + unsigned long size = 0; 1.209 + sslcertificatequery_t scq = 1.210 + (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL); 1.211 + sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL); 1.212 + SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, 1.213 + sizeof (SSLSTREAM)); 1.214 + stream->tcpstream = tstream; /* bind TCP stream */ 1.215 + /* initialize TLS credential */ 1.216 + memset (&tlscred,0,sizeof (SCHANNEL_CRED)); 1.217 + tlscred.dwVersion = SCHANNEL_CRED_VERSION; 1.218 + tlscred.grbitEnabledProtocols = SP_PROT_TLS1; 1.219 + 1.220 + /* acquire credentials */ 1.221 + if (sft->AcquireCredentialsHandle 1.222 + (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ? 1.223 + &tlscred : NIL,NIL,NIL,&stream->cred,&t) 1.224 + != SEC_E_OK) reason = "Acquire credentials handle failed"; 1.225 + else while (!reason) { /* negotiate security context */ 1.226 + /* initialize buffers */ 1.227 + ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf; 1.228 + ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL; 1.229 + obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL; 1.230 + ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; 1.231 + ibuf[1].BufferType = SECBUFFER_EMPTY; 1.232 + /* initialize buffer descriptors */ 1.233 + ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; 1.234 + ibufs.cBuffers = 2; obufs.cBuffers = 1; 1.235 + ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; 1.236 + /* negotiate security */ 1.237 + e = sft->InitializeSecurityContext 1.238 + (&stream->cred,size ? &stream->context : NIL,host,req,0, 1.239 + SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t); 1.240 + /* have an output buffer we need to send? */ 1.241 + if (obuf[0].pvBuffer && obuf[0].cbBuffer) { 1.242 + if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer)) 1.243 + reason = "Unexpected TCP output disconnect"; 1.244 + /* free the buffer */ 1.245 + sft->FreeContextBuffer (obuf[0].pvBuffer); 1.246 + } 1.247 + if (!reason) switch (e) { /* negotiation state */ 1.248 + case SEC_I_INCOMPLETE_CREDENTIALS: 1.249 + break; /* server wants client auth */ 1.250 + case SEC_I_CONTINUE_NEEDED: 1.251 + if (size) { /* continue, read any data? */ 1.252 + /* yes, anything regurgiated back to us? */ 1.253 + if (ibuf[1].BufferType == SECBUFFER_EXTRA) { 1.254 + /* yes, set this as the new data */ 1.255 + memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); 1.256 + size = ibuf[1].cbBuffer; 1.257 + break; 1.258 + } 1.259 + size = 0; /* otherwise, read more stuff from server */ 1.260 + } 1.261 + case SEC_E_INCOMPLETE_MESSAGE: 1.262 + /* need to read more data from server */ 1.263 + if (!tcp_getdata (stream->tcpstream)) 1.264 + reason = "Unexpected TCP input disconnect"; 1.265 + else { 1.266 + memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr); 1.267 + size += stream->tcpstream->ictr; 1.268 + /* empty it from TCP's buffers */ 1.269 + stream->tcpstream->iptr += stream->tcpstream->ictr; 1.270 + stream->tcpstream->ictr = 0; 1.271 + } 1.272 + break; 1.273 + 1.274 + case SEC_E_OK: /* success, any data to be regurgitated? */ 1.275 + if (ibuf[1].BufferType == SECBUFFER_EXTRA) { 1.276 + /* yes, set this as the new data */ 1.277 + memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf, 1.278 + buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer); 1.279 + stream->tcpstream->ictr = ibuf[1].cbBuffer; 1.280 + } 1.281 + if (certNameToStr && !(flags & NET_NOVALIDATECERT)) { 1.282 + /* need validation, make wchar of host */ 1.283 + if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) && 1.284 + (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) && 1.285 + MultiByteToWideChar (CP_ACP,0,host,-1,whost,size))) 1.286 + fatal ("Can't make wchar of host name!"); 1.287 + /* get certificate */ 1.288 + if ((sft->QueryContextAttributes 1.289 + (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) != 1.290 + SEC_E_OK) || !cert) { 1.291 + reason = "*Unable to get certificate"; 1.292 + strcpy (certname,"<no certificate>"); 1.293 + } 1.294 + else { /* get certificate subject name */ 1.295 + (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 1.296 + &cert->pCertInfo->Subject,CERT_X500_NAME_STR, 1.297 + certname,255); 1.298 + /* build certificate chain */ 1.299 + memset (&chparam,0,sizeof (chparam)); 1.300 + chparam.cbSize = sizeof (chparam); 1.301 + chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; 1.302 + chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage; 1.303 + chparam.RequestedUsage.Usage.cUsageIdentifier = 1.304 + sizeof (usage) / sizeof (LPSTR); 1.305 + if (!(*certGetCertificateChain) 1.306 + (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain)) 1.307 + reason = ssl_analyze_status (GetLastError (),tmp); 1.308 + else { /* validate certificate chain */ 1.309 + memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA)); 1.310 + policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA); 1.311 + policy.dwAuthType = AUTHTYPE_SERVER; 1.312 + policy.fdwChecks = NIL; 1.313 + policy.pwszServerName = whost; 1.314 + memset (&polparam,0,sizeof (polparam)); 1.315 + polparam.cbSize = sizeof (polparam); 1.316 + polparam.pvExtraPolicyPara = &policy; 1.317 + memset (&status,0,sizeof (status)); 1.318 + status.cbSize = sizeof (status); 1.319 + if (!(*certVerifyCertificateChainPolicy) 1.320 + (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status)) 1.321 + reason = ssl_analyze_status (GetLastError (),tmp); 1.322 + else if (status.dwError) 1.323 + reason = ssl_analyze_status (status.dwError,tmp); 1.324 + (*certFreeCertificateChain) (chain); 1.325 + } 1.326 + (*certFreeCertificateContext) (cert); 1.327 + } 1.328 + if (whost) fs_give ((void **) &whost); 1.329 + 1.330 + if (reason) { /* got an error? */ 1.331 + /* application callback */ 1.332 + if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason, 1.333 + host,certname) ? NIL : ""; 1.334 + else if (*certname) { /* error message to return via mm_log() */ 1.335 + sprintf (buf,"*%.128s: %.255s", 1.336 + (*reason == '*') ? reason + 1 : reason,certname); 1.337 + reason = buf; 1.338 + } 1.339 + } 1.340 + } 1.341 + if (reason || 1.342 + (reason = ssl_analyze_status 1.343 + (sft->QueryContextAttributes 1.344 + (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))) 1.345 + break; /* error in certificate or getting sizes */ 1.346 + fs_give ((void **) &buf); /* flush temporary buffer */ 1.347 + /* make maximum-sized buffers */ 1.348 + stream->bufsize = stream->sizes.cbHeader + 1.349 + stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer; 1.350 + if (stream->sizes.cbMaximumMessage < SSLBUFLEN) 1.351 + fatal ("cbMaximumMessage is less than SSLBUFLEN!"); 1.352 + else if (stream->sizes.cbMaximumMessage < 16384) { 1.353 + sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384", 1.354 + (long) stream->sizes.cbMaximumMessage); 1.355 + mm_log (tmp,NIL); 1.356 + } 1.357 + stream->ibuf = (char *) fs_get (stream->bufsize); 1.358 + stream->obuf = (char *) fs_get (stream->bufsize); 1.359 + return stream; 1.360 + default: 1.361 + reason = ssl_analyze_status (e,buf); 1.362 + } 1.363 + } 1.364 + ssl_close (stream); /* failed to do SSL */ 1.365 + stream = NIL; /* no stream returned */ 1.366 + switch (*reason) { /* analyze reason */ 1.367 + case '*': /* certificate failure */ 1.368 + ++reason; /* skip over certificate failure indication */ 1.369 + /* pass to error callback */ 1.370 + if (sf) (*sf) (host,reason,flags); 1.371 + else { /* no error callback, build error message */ 1.372 + sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason); 1.373 + mm_log (tmp,ERROR); 1.374 + } 1.375 + case '\0': /* user answered no to certificate callback */ 1.376 + if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */ 1.377 + stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0, 1.378 + sizeof (SSLSTREAM)); 1.379 + break; 1.380 + default: /* non-certificate failure */ 1.381 + if (flags & NET_TRYSSL); /* no error output if tryssl */ 1.382 + /* pass to error callback */ 1.383 + else if (sf) (*sf) (host,reason,flags); 1.384 + else { /* no error callback, build error message */ 1.385 + sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason); 1.386 + mm_log (tmp,ERROR); 1.387 + } 1.388 + break; 1.389 + } 1.390 + fs_give ((void **) &buf); /* flush temporary buffer */ 1.391 + return stream; 1.392 +} 1.393 + 1.394 +/* Generate error text from SSL error code 1.395 + * Accepts: SSL status 1.396 + * scratch buffer 1.397 + * Returns: text if error status, else NIL 1.398 + */ 1.399 + 1.400 +static char *ssl_analyze_status (SECURITY_STATUS err,char *buf) 1.401 +{ 1.402 + switch (err) { 1.403 + case SEC_E_OK: /* no error */ 1.404 + case SEC_I_CONTINUE_NEEDED: 1.405 + case SEC_I_INCOMPLETE_CREDENTIALS: 1.406 + case SEC_E_INCOMPLETE_MESSAGE: 1.407 + return NIL; 1.408 + case SEC_E_NO_AUTHENTICATING_AUTHORITY: 1.409 + mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL); 1.410 + return "*No authority could be contacted for authentication"; 1.411 + case SEC_E_WRONG_PRINCIPAL: 1.412 + mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL); 1.413 + case CERT_E_CN_NO_MATCH: 1.414 + return "*Server name does not match certificate"; 1.415 + case SEC_E_UNTRUSTED_ROOT: 1.416 + mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL); 1.417 + case CERT_E_UNTRUSTEDROOT: 1.418 + return "*Self-signed certificate or untrusted authority"; 1.419 + case SEC_E_CERT_EXPIRED: 1.420 + mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL); 1.421 + case CERT_E_EXPIRED: 1.422 + return "*Certificate has expired"; 1.423 + case CERT_E_REVOKED: 1.424 + return "*Certificate revoked"; 1.425 + case SEC_E_INVALID_TOKEN: 1.426 + return "Invalid token, probably not an SSL server"; 1.427 + case SEC_E_UNSUPPORTED_FUNCTION: 1.428 + return "SSL not supported on this machine - upgrade your system software"; 1.429 + } 1.430 + sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err); 1.431 + return buf; 1.432 +} 1.433 + 1.434 +/* SSL receive line 1.435 + * Accepts: SSL stream 1.436 + * Returns: text line string or NIL if failure 1.437 + */ 1.438 + 1.439 +char *ssl_getline (SSLSTREAM *stream) 1.440 +{ 1.441 + unsigned long n,contd; 1.442 + char *ret = ssl_getline_work (stream,&n,&contd); 1.443 + if (ret && contd) { /* got a line needing continuation? */ 1.444 + STRINGLIST *stl = mail_newstringlist (); 1.445 + STRINGLIST *stc = stl; 1.446 + do { /* collect additional lines */ 1.447 + stc->text.data = (unsigned char *) ret; 1.448 + stc->text.size = n; 1.449 + stc = stc->next = mail_newstringlist (); 1.450 + ret = ssl_getline_work (stream,&n,&contd); 1.451 + } while (ret && contd); 1.452 + if (ret) { /* stash final part of line on list */ 1.453 + stc->text.data = (unsigned char *) ret; 1.454 + stc->text.size = n; 1.455 + /* determine how large a buffer we need */ 1.456 + for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size; 1.457 + ret = fs_get (n + 1); /* copy parts into buffer */ 1.458 + for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next) 1.459 + memcpy (ret + n,stc->text.data,stc->text.size); 1.460 + ret[n] = '\0'; 1.461 + } 1.462 + mail_free_stringlist (&stl);/* either way, done with list */ 1.463 + } 1.464 + return ret; 1.465 +} 1.466 + 1.467 +/* SSL receive line or partial line 1.468 + * Accepts: SSL stream 1.469 + * pointer to return size 1.470 + * pointer to return continuation flag 1.471 + * Returns: text line string, size and continuation flag, or NIL if failure 1.472 + */ 1.473 + 1.474 +static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size, 1.475 + long *contd) 1.476 +{ 1.477 + unsigned long n; 1.478 + char *s,*ret,c,d; 1.479 + *contd = NIL; /* assume no continuation */ 1.480 + /* make sure have data */ 1.481 + if (!ssl_getdata (stream)) return NIL; 1.482 + for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) { 1.483 + d = *stream->iptr++; /* slurp another character */ 1.484 + if ((c == '\015') && (d == '\012')) { 1.485 + ret = (char *) fs_get (n--); 1.486 + memcpy (ret,s,*size = n); /* copy into a free storage string */ 1.487 + ret[n] = '\0'; /* tie off string with null */ 1.488 + return ret; 1.489 + } 1.490 + } 1.491 + /* copy partial string from buffer */ 1.492 + memcpy ((ret = (char *) fs_get (n)),s,*size = n); 1.493 + /* get more data from the net */ 1.494 + if (!ssl_getdata (stream)) fs_give ((void **) &ret); 1.495 + /* special case of newline broken by buffer */ 1.496 + else if ((c == '\015') && (*stream->iptr == '\012')) { 1.497 + stream->iptr++; /* eat the line feed */ 1.498 + stream->ictr--; 1.499 + ret[*size = --n] = '\0'; /* tie off string with null */ 1.500 + } 1.501 + else *contd = LONGT; /* continuation needed */ 1.502 + return ret; 1.503 +} 1.504 + 1.505 +/* SSL receive buffer 1.506 + * Accepts: SSL stream 1.507 + * size in bytes 1.508 + * buffer to read into 1.509 + * Returns: T if success, NIL otherwise 1.510 + */ 1.511 + 1.512 +long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer) 1.513 +{ 1.514 + unsigned long n; 1.515 + while (size > 0) { /* until request satisfied */ 1.516 + if (!ssl_getdata (stream)) return NIL; 1.517 + n = min (size,stream->ictr);/* number of bytes to transfer */ 1.518 + /* do the copy */ 1.519 + memcpy (buffer,stream->iptr,n); 1.520 + buffer += n; /* update pointer */ 1.521 + stream->iptr += n; 1.522 + size -= n; /* update # of bytes to do */ 1.523 + stream->ictr -= n; 1.524 + } 1.525 + buffer[0] = '\0'; /* tie off string */ 1.526 + return T; 1.527 +} 1.528 + 1.529 +/* SSL receive data 1.530 + * Accepts: TCP/IP stream 1.531 + * Returns: T if success, NIL otherwise 1.532 + */ 1.533 + 1.534 +long ssl_getdata (SSLSTREAM *stream) 1.535 +{ 1.536 + while (stream->ictr < 1) { /* decrypted buffer empty? */ 1.537 + SECURITY_STATUS status; 1.538 + SecBuffer buf[4]; 1.539 + SecBufferDesc msg; 1.540 + size_t i; 1.541 + size_t n = 0; /* initially no bytes to decrypt */ 1.542 + do { /* yes, make sure have data from TCP */ 1.543 + if (stream->iextractr) { /* have previous unread data? */ 1.544 + memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr); 1.545 + n += stream->iextractr; /* update number of bytes read */ 1.546 + stream->iextractr = 0; /* no more extra data */ 1.547 + } 1.548 + else { /* read from TCP */ 1.549 + if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream); 1.550 + /* maximum amount of data to copy */ 1.551 + if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr))) 1.552 + fatal ("incomplete SecBuffer exceeds maximum buffer size"); 1.553 + /* do the copy */ 1.554 + memcpy (stream->ibuf + n,stream->tcpstream->iptr,i); 1.555 + stream->tcpstream->iptr += i; 1.556 + stream->tcpstream->ictr -= i; 1.557 + n += i; /* update number of bytes to decrypt */ 1.558 + } 1.559 + buf[0].cbBuffer = n; /* first SecBuffer gets data */ 1.560 + buf[0].pvBuffer = stream->ibuf; 1.561 + buf[0].BufferType = SECBUFFER_DATA; 1.562 + /* subsequent ones are for spares */ 1.563 + buf[1].BufferType = buf[2].BufferType = buf[3].BufferType = 1.564 + SECBUFFER_EMPTY; 1.565 + msg.ulVersion = SECBUFFER_VERSION; 1.566 + msg.cBuffers = 4; /* number of SecBuffers */ 1.567 + msg.pBuffers = buf; /* first SecBuffer */ 1.568 + 1.569 + } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4) 1.570 + (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE); 1.571 + switch (status) { 1.572 + case SEC_E_OK: /* won */ 1.573 + case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */ 1.574 + /* hunt for a buffer */ 1.575 + for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++); 1.576 + if (i < 4) { /* found a buffer? */ 1.577 + /* yes, set up pointer and counter */ 1.578 + stream->iptr = buf[i].pvBuffer; 1.579 + stream->ictr = buf[i].cbBuffer; 1.580 + /* any unprocessed data? */ 1.581 + while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) { 1.582 + /* yes, note for next time around */ 1.583 + stream->iextraptr = buf[i].pvBuffer; 1.584 + stream->iextractr = buf[i].cbBuffer; 1.585 + } 1.586 + } 1.587 + break; 1.588 + default: /* anything else means we've lost */ 1.589 + return ssl_abort (stream); 1.590 + } 1.591 + } 1.592 + return LONGT; 1.593 +} 1.594 + 1.595 +/* SSL send string as record 1.596 + * Accepts: SSL stream 1.597 + * string pointer 1.598 + * Returns: T if success else NIL 1.599 + */ 1.600 + 1.601 +long ssl_soutr (SSLSTREAM *stream,char *string) 1.602 +{ 1.603 + return ssl_sout (stream,string,(unsigned long) strlen (string)); 1.604 +} 1.605 + 1.606 + 1.607 +/* SSL send string 1.608 + * Accepts: SSL stream 1.609 + * string pointer 1.610 + * byte count 1.611 + * Returns: T if success else NIL 1.612 + */ 1.613 + 1.614 +long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size) 1.615 +{ 1.616 + SecBuffer buf[4]; 1.617 + SecBufferDesc msg; 1.618 + char *s; 1.619 + size_t n; 1.620 + if (!stream->tcpstream) return NIL; 1.621 + /* until request satisfied */ 1.622 + for (s = stream->ibuf,n = 0; size;) { 1.623 + /* header */ 1.624 + buf[0].BufferType = SECBUFFER_STREAM_HEADER; 1.625 + memset (buf[0].pvBuffer = stream->obuf,0, 1.626 + buf[0].cbBuffer = stream->sizes.cbHeader); 1.627 + /* message (up to maximum size) */ 1.628 + buf[1].BufferType = SECBUFFER_DATA; 1.629 + memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string, 1.630 + buf[1].cbBuffer = min (size,SSLBUFLEN)); 1.631 + /* trailer */ 1.632 + buf[2].BufferType = SECBUFFER_STREAM_TRAILER; 1.633 + memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0, 1.634 + buf[2].cbBuffer = stream->sizes.cbTrailer); 1.635 + /* spare */ 1.636 + buf[3].BufferType = SECBUFFER_EMPTY; 1.637 + msg.ulVersion = SECBUFFER_VERSION; 1.638 + msg.cBuffers = 4; /* number of SecBuffers */ 1.639 + msg.pBuffers = buf; /* first SecBuffer */ 1.640 + string += buf[1].cbBuffer; 1.641 + size -= buf[1].cbBuffer; /* this many bytes processed */ 1.642 + /* encrypt and send message */ 1.643 + if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3) 1.644 + (&stream->context,0,&msg,NIL) != SEC_E_OK) || 1.645 + !tcp_sout (stream->tcpstream,stream->obuf, 1.646 + buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer)) 1.647 + return ssl_abort (stream);/* encryption or sending failed */ 1.648 + } 1.649 + return LONGT; 1.650 +} 1.651 + 1.652 +/* SSL close 1.653 + * Accepts: SSL stream 1.654 + */ 1.655 + 1.656 +void ssl_close (SSLSTREAM *stream) 1.657 +{ 1.658 + ssl_abort (stream); /* nuke the stream */ 1.659 + fs_give ((void **) &stream); /* flush the stream */ 1.660 +} 1.661 + 1.662 + 1.663 +/* SSL abort stream 1.664 + * Accepts: SSL stream 1.665 + * Returns: NIL always 1.666 + */ 1.667 + 1.668 +static long ssl_abort (SSLSTREAM *stream) 1.669 +{ 1.670 + if (stream->tcpstream) { /* close TCP stream */ 1.671 + sft->DeleteSecurityContext (&stream->context); 1.672 + sft->FreeCredentialHandle (&stream->cred); 1.673 + tcp_close (stream->tcpstream); 1.674 + stream->tcpstream = NIL; 1.675 + } 1.676 + if (stream->ibuf) fs_give ((void **) &stream->ibuf); 1.677 + if (stream->obuf) fs_give ((void **) &stream->obuf); 1.678 + return NIL; 1.679 +} 1.680 + 1.681 +/* SSL get host name 1.682 + * Accepts: SSL stream 1.683 + * Returns: host name for this stream 1.684 + */ 1.685 + 1.686 +char *ssl_host (SSLSTREAM *stream) 1.687 +{ 1.688 + return tcp_host (stream->tcpstream); 1.689 +} 1.690 + 1.691 + 1.692 +/* SSL get remote host name 1.693 + * Accepts: SSL stream 1.694 + * Returns: host name for this stream 1.695 + */ 1.696 + 1.697 +char *ssl_remotehost (SSLSTREAM *stream) 1.698 +{ 1.699 + return tcp_remotehost (stream->tcpstream); 1.700 +} 1.701 + 1.702 + 1.703 +/* SSL return port for this stream 1.704 + * Accepts: SSL stream 1.705 + * Returns: port number for this stream 1.706 + */ 1.707 + 1.708 +unsigned long ssl_port (SSLSTREAM *stream) 1.709 +{ 1.710 + return tcp_port (stream->tcpstream); 1.711 +} 1.712 + 1.713 + 1.714 +/* SSL get local host name 1.715 + * Accepts: SSL stream 1.716 + * Returns: local host name 1.717 + */ 1.718 + 1.719 +char *ssl_localhost (SSLSTREAM *stream) 1.720 +{ 1.721 + return tcp_localhost (stream->tcpstream); 1.722 +} 1.723 + 1.724 +#include "ssl_none.c" /* currently no server support */