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 */

UW-IMAP'd extensions by yuuji