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