--- /dev/null
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+\f
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+\f
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+
+/* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */
+
+typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD);
+typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,
+ HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,
+ PCCERT_CHAIN_CONTEXT *);
+typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT,
+ PCERT_CHAIN_POLICY_PARA,
+ PCERT_CHAIN_POLICY_STATUS);
+typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT);
+typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT);
+
+static CNTS certNameToStr = NIL;
+static CGCC certGetCertificateChain = NIL;
+static CVCCP certVerifyCertificateChainPolicy = NIL;
+static CFCC certFreeCertificateChain = NIL;
+static CFCCX certFreeCertificateContext = NIL;
+\f
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ if ((lib = LoadLibrary ("crypt32.dll")) &&
+ (certGetCertificateChain = (CGCC)
+ GetProcAddress (lib,"CertGetCertificateChain")) &&
+ (certVerifyCertificateChainPolicy = (CVCCP)
+ GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) &&
+ (certFreeCertificateChain = (CFCC)
+ GetProcAddress (lib,"CertFreeCertificateChain")) &&
+ (certFreeCertificateContext = (CFCCX)
+ GetProcAddress (lib,"CertFreeCertificateContext")))
+ certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA");
+ return; /* all done */
+ }
+ }
+ }
+}
+\f
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+\f
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+\f
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+\f
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (certNameToStr && !(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!(*certGetCertificateChain)
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!(*certVerifyCertificateChainPolicy)
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ (*certFreeCertificateChain) (chain);
+ }
+ (*certFreeCertificateContext) (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+\f
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+\f
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+\f
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+\f
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+\f
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+\f
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+\f
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+\f
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+\f
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */