imapext-2007

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

UW-IMAP'd extensions by yuuji