summaryrefslogtreecommitdiff
path: root/imap/src/osdep/nt/ssl_nt.c
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2020-01-04 20:08:32 -0700
committerEduardo Chappa <chappa@washington.edu>2020-01-04 20:08:32 -0700
commitf398f615b6df385aec2b3553310cc237b29e068a (patch)
tree5af79c6a9a180c72c58a9d9cd2d79a1d7657d152 /imap/src/osdep/nt/ssl_nt.c
parent77191bf3e4e049603fb6a0547876259c29c71dbd (diff)
downloadalpine-f398f615b6df385aec2b3553310cc237b29e068a.tar.xz
* The feature that stopped alpine from saving passwords in the password
file prevented users from actually saving their passwords in Windows and MAC OS. Fix the code so that passwords will be saved. Also, update the documentation of this feature. * Fix a buffer overflow bug in the XOAUTH2 code (off by one error). * Update PC-Alpine to work with Libressl version 3.0.2 instead of version 2.5.5 (update build.bat and lib files from the LibreSSL build). * Erase SSLXXXXXX file. * ssl_nt.c actually directs the code to ssl_libressl.c or ssl_win.c. The file ssl_libressl.c is the file ssl_unix.c from the unix osdep directory. The file ssl_win.c is the native SSL windows code. The Unix side provides S/MIME support for Alpine and the latest encryption protocols support for Alpine when connecting to a secure server, while the windows side provide TLSv1_3 support for Alpine, but not S/MIME support. In order to provide unix code for TLSv1_3 (once LibreSSL supports it) edit the file os_nt.c and remove the comments on the #ifdef section. This would provide both TLSv1_3 and S/MIME support with unix code. On the other hand, when we provide TLSv1_3 with the Windows code we need to undefine DF_ENCRYPTION_RANGE, and this is done in the file include/config.wnt.h. The way this is done as of this moment is by commenting an #else directive that preceedes this #undefine. * Update makefile.nt and friends in the windows side to account for the addition of XOAUTH2, and the use of only ssl_nt.c when dealing with Alpine. * Define SMIME_SSLCERTS as c:\libressl\ssl\certs, so that these certificates be considered while checking a digital S/MIME signature. * Improvements to the SMARTTIME24 token to account for changes in year.
Diffstat (limited to 'imap/src/osdep/nt/ssl_nt.c')
-rw-r--r--imap/src/osdep/nt/ssl_nt.c1764
1 files changed, 5 insertions, 1759 deletions
diff --git a/imap/src/osdep/nt/ssl_nt.c b/imap/src/osdep/nt/ssl_nt.c
index d39fdf08..5bc04ab8 100644
--- a/imap/src/osdep/nt/ssl_nt.c
+++ b/imap/src/osdep/nt/ssl_nt.c
@@ -23,1762 +23,8 @@
* http://www.apache.org/licenses/LICENSE-2.0
*
*/
-#ifdef ENABLE_WINDOWS_LIBRESSL
-#define crypt ssl_private_crypt
-#define STRING OPENSSL_STRING
-#include <x509v3.h>
-#include <ssl.h>
-#include <err.h>
-#include <pem.h>
-#include <buffer.h>
-#include <bio.h>
-#include <crypto.h>
-#include <rand.h>
-#ifdef OPENSSL_1_1_0
-#include <rsa.h>
-#include <bn.h>
-#endif /* OPENSSL_1_1_0 */
-#undef STRING
-#undef crypt
-
-#define SSLBUFLEN 8192
-
-/*
- * PCI auditing compliance, disable:
- * SSLv2
- * anonymous D-H (no certificate
- * export encryption ciphers (40 and 56 bits)
- * low encryption cipher suites (40 and 56 bits, excluding export)
- * null encryption (disabling implied by "ALL")
- *
- * UW imapd just disables low-grade and null ("ALL:!LOW"). This setting
- * will break clients that attempt to use the newly-prohibited mechanisms.
- *
- * I question the value of disabling SSLv2, as opposed to disabling the SSL
- * ports (e.g., 993 for IMAP, 995 for POP3) and using TLS exclusively.
- */
-
-#define SSLCIPHERLIST "ALL:!SSLv2:!ADH:!EXP:!LOW"
-
-/* SSL I/O stream */
-
-typedef struct ssl_stream {
- TCPSTREAM *tcpstream; /* TCP stream */
- SSL_CTX *context; /* SSL context */
- SSL *con; /* SSL connection */
- int ictr; /* input counter */
- char *iptr; /* input pointer */
- char ibuf[SSLBUFLEN]; /* input buffer */
-} SSLSTREAM;
-
-#include "sslio.h"
-
-/* Function prototypes */
-const SSL_METHOD *ssl_connect_mthd(int flag);
-static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
-static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
-static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
-static char *ssl_validate_cert (X509 *cert,char *host);
-static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
-static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
- long *contd);
-static long ssl_abort (SSLSTREAM *stream);
-
-#ifdef OPENSSL_1_1_0
-#define SSL_CTX_TYPE SSL_CTX
-#else
-#define SSL_CTX_TYPE SSL
-#endif /* OPENSSL_1_1_0 */
-
-static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength);
-
-typedef struct ssl_versions_s {
- char *name;
- int version;
-} SSL_VERSIONS_S;
-
-int
-pith_ssl_encryption_version(char *s)
-{
- SSL_VERSIONS_S ssl_versions[] = {
- { "no_min", 0 },
- { "ssl3", SSL3_VERSION },
- { "tls1", TLS1_VERSION },
- { "tls1_1", TLS1_1_VERSION },
- { "tls1_2", TLS1_2_VERSION },
-#ifdef TLS1_3_VERSION
- { "tls1_3", TLS1_3_VERSION },
-#endif /* TLS1_3_VERSION */
- { "no_max", 0 }, /* set this last in the list */
- { NULL, 0 },
- };
- int i;
-
- if (s == NULL || *s == '\0')
- return -1;
-
- for (i = 0; ssl_versions[i].name != NULL; i++)
- if (strcmp(ssl_versions[i].name, s) == 0)
- break;
-
- if (strcmp(s, "no_max") == 0) i--;
-
- return ssl_versions[i].name != NULL ? ssl_versions[i].version : -1;
-}
-
-/* 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 */
-};
- /* non-NIL if doing SSL primary I/O */
-static SSLSTDIOSTREAM *sslstdio = NIL;
-static char *start_tls = NIL; /* non-NIL if start TLS requested */
-
-/* One-time SSL initialization */
-
-static int sslonceonly = 0;
-
-void ssl_onceonlyinit (void)
-{
- if (!sslonceonly++) { /* only need to call it once */
- int fd;
- char tmp[MAILTMPLEN];
- struct stat sbuf;
- /* if system doesn't have /dev/urandom */
- if (stat ("/dev/urandom",&sbuf)) {
- strcpy(tmp, "SSLXXXXXX");
- fd = fopen(tmp,"a");
- unlink (tmp); /* don't need the file */
- fstat (fd,&sbuf); /* get information about the file */
- close (fd); /* flush descriptor */
- /* not great but it'll have to do */
- sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
- tcp_serveraddr (),(unsigned long) tcp_serverport (),
- tcp_clientaddr (),(unsigned long) tcp_clientport (),
- (unsigned long) sbuf.st_ino,(unsigned long) time (0),
- (unsigned long) gethostid (),(unsigned long) getpid ());
- RAND_seed (tmp,strlen (tmp));
- }
- /* apply runtime linkage */
- mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
- mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
-#ifdef OPENSSL_1_1_0
- OPENSSL_init_ssl(0, NULL);
-#else
- SSL_library_init (); /* add all algorithms */
-#endif /* OPENSSL_1_1_0 */
- }
-}
-
-/* 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 */
-}
-
-typedef struct ssl_disable_s {
- int version;
- int disable_code;
-} SSL_DISABLE_S;
-
-SSL_DISABLE_S ssl_disable[] = {
- { SSL2_VERSION, SSL_OP_NO_SSLv2 },
-{ SSL3_VERSION, SSL_OP_NO_SSLv3 },
-{ TLS1_VERSION, SSL_OP_NO_TLSv1 },
-{ TLS1_1_VERSION, SSL_OP_NO_TLSv1_1 },
-{ TLS1_2_VERSION, SSL_OP_NO_TLSv1_2 },
-#ifdef TLS1_3_VERSION
-{ TLS1_3_VERSION, SSL_OP_NO_TLSv1_3 },
-#endif /* TLS1_3_VERSION */
-{ 0, 0 }
-};
-
-#define NUMBER_SSL_VERSIONS (sizeof(ssl_disable)/sizeof(ssl_disable[0]) - 1)
-
-/* returns the mask to disable a specific version.
-* If version not found, returns 0.
-*
-* Arguments: version, and direction.
-* If direction is -1, returns mask to disable versions less than given version.
-* If direction is +1, returns mask to disable versions bigger than given version.
-*/
-int ssl_disable_mask(int ssl_version, int direction)
-{
- int rv = 0;
- int i;
- for (i = 0; ssl_disable[i].version != 0
- && ssl_disable[i].version != ssl_version; i++);
- if (i == 0
- || i == NUMBER_SSL_VERSIONS - 1
- || ssl_disable[i].version == 0)
- return rv;
- i += direction; /* move in the direction */
- for (; i >= 0 && i <= NUMBER_SSL_VERSIONS - 1; i += direction)
- rv |= ssl_disable[i].disable_code;
- return rv;
-}
-
-
-/* ssl_connect_mthd: returns a context pointer to the connection to
- * a ssl server
- */
-const SSL_METHOD *ssl_connect_mthd(int flag, int *min, int *max)
-{
- int client_request;
- client_request = (flag & NET_TRYTLS1) ? TLS1_VERSION
- : (flag & NET_TRYTLS1_1) ? TLS1_1_VERSION
- : (flag & NET_TRYTLS1_2) ? TLS1_2_VERSION
-#ifdef TLS1_3_VERSION
- : (flag & NET_TRYTLS1_3) ? TLS1_3_VERSION
-#else
- : (flag & NET_TRYTLS1_3) ? -2
-#endif
- : 0;
-
- *min = *(int *)mail_parameters(NULL, GET_ENCRYPTION_RANGE_MIN, NULL);
- *max = *(int *)mail_parameters(NULL, GET_ENCRYPTION_RANGE_MAX, NULL);
-
- /*
- * if no special request, negotiate the maximum the client is configured
- * to negotiate
- */
- if (client_request == 0)
- client_request = *max;
-
- if (client_request < *min || client_request > *max)
- return NIL; /* out of range? bail out */
-
- /* Some Linux distributors seem to believe that it is ok to disable some of
- * these methods for their users, so we have to test that every requested
- * method has actually been compiled in into their openssl/libressl library.
- * Oh well...
- */
-#ifndef OPENSSL_1_1_0
- if (client_request == SSL3_VERSION)
-#ifndef OPENSSL_NO_SSL3_METHOD
- return SSLv3_client_method();
-#else
- return NIL;
-#endif /* OPENSSL_NO_SSL3_METHOD */
- else if (client_request == TLS1_VERSION)
-#ifndef OPENSSL_NO_TLS1_METHOD
- return TLSv1_client_method();
-#else
- return NIL;
-#endif /* OPENSSL_NO_TLS1_METHOD */
- else if (client_request == TLS1_1_VERSION)
-#ifndef OPENSSL_NO_TLS1_1_METHOD
- return TLSv1_1_client_method();
-#else
- return NIL;
-#endif /* OPENSSL_NO_TLS1_1_METHOD */
- else if (client_request == TLS1_2_VERSION)
-#ifndef OPENSSL_NO_TLS1_2_METHOD
- return TLSv1_2_client_method();
-#else
- return NIL;
-#endif /* OPENSSL_NO_TLS1_2_METHOD */
-#ifdef TLS1_3_VERSION /* this is only reachable if TLS1_3 support exists */
- else if (client_request == TLS1_3_VERSION)
-#ifndef OPENSSL_NO_TLS1_3_METHOD
- return TLS_client_method();
-#else
- return NIL;
-#endif /* #ifndef OPENSSL_NO_TLS1_2_METHOD */
-#endif /* TLS1_3_VERSION */
-#endif /* ifndef OPENSSL_1_1_0 */
-
- return SSLv23_client_method();
-}
-
-/* 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)
-{
- char *reason,tmp[MAILTMPLEN];
- sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
- blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
- void *data = (*bn) (BLOCK_SENSITIVE,NIL);
- SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
- sizeof (SSLSTREAM));
- stream->tcpstream = tstream; /* bind TCP stream */
- /* do the work */
- reason = ssl_start_work (stream,host,flags);
- (*bn) (BLOCK_NONSENSITIVE,data);
- if (reason) { /* failed? */
- 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;
- }
- }
- return stream;
-}
-
-/* Start SSL/TLS negotiations worker routine
- * Accepts: SSL stream
- * user's host name
- * flags
- * Returns: NIL if success, else error reason
- */
-
- /* evil but I had no choice */
-static char *ssl_last_error = NIL;
-static char *ssl_last_host = NIL;
-
-
-static char *ssl_start_work(SSLSTREAM *stream, char *host, unsigned long flags)
-{
- BIO *bio;
- X509 *cert;
- unsigned long sl, tl;
- int min, max;
- int masklow, maskhigh;
- char *s, *t, *err, tmp[MAILTMPLEN], buf[256];
- sslcertificatequery_t scq =
- (sslcertificatequery_t)mail_parameters(NIL, GET_SSLCERTIFICATEQUERY, NIL);
- sslclientcert_t scc =
- (sslclientcert_t)mail_parameters(NIL, GET_SSLCLIENTCERT, NIL);
- sslclientkey_t sck =
- (sslclientkey_t)mail_parameters(NIL, GET_SSLCLIENTKEY, NIL);
- if (ssl_last_error) fs_give((void **)&ssl_last_error);
- ssl_last_host = host;
- if (!(stream->context = SSL_CTX_new(ssl_connect_mthd(flags, &min, &max))))
- return "SSL context failed";
- SSL_CTX_set_options(stream->context, 0);
- masklow = ssl_disable_mask(min, -1);
- maskhigh = ssl_disable_mask(max, 1);
- SSL_CTX_set_options(stream->context, masklow | maskhigh);
- /* disable certificate validation? */
- if (flags & NET_NOVALIDATECERT)
- SSL_CTX_set_verify(stream->context, SSL_VERIFY_NONE, NIL);
- else SSL_CTX_set_verify(stream->context, SSL_VERIFY_PEER, ssl_open_verify);
- /* set default paths to CAs... */
- SSL_CTX_set_default_verify_paths(stream->context);
- /* ...unless a non-standard path desired */
- if ((s = (char *)mail_parameters(NIL, GET_SSLCAPATH, NIL)) != NULL)
- SSL_CTX_load_verify_locations(stream->context, NIL, s);
- /* want to send client certificate? */
- if (scc && (s = (*scc) ()) && (sl = strlen(s))) {
- if ((cert = PEM_read_bio_X509(bio = BIO_new_mem_buf(s, sl), NIL, NIL, NIL)) != NULL) {
- SSL_CTX_use_certificate(stream->context, cert);
- X509_free(cert);
- }
- BIO_free(bio);
- if (!cert) return "SSL client certificate failed";
- /* want to supply private key? */
- if ((t = (sck ? (*sck) () : s)) && (tl = strlen(t))) {
- EVP_PKEY *key;
- if ((key = PEM_read_bio_PrivateKey(bio = BIO_new_mem_buf(t, tl),
- NIL, NIL, "")) != NULL) {
- SSL_CTX_use_PrivateKey(stream->context, key);
- EVP_PKEY_free(key);
- }
- BIO_free(bio);
- memset(t, 0, tl); /* erase key */
- }
- if (s != t) memset(s, 0, sl);/* erase certificate if different from key */
- }
-
- /* create connection */
- if (!(stream->con = (SSL *)SSL_new(stream->context)))
- return "SSL connection failed";
- if (host && !SSL_set_tlsext_host_name(stream->con, host)) {
- return "Server Name Identification (SNI) failed";
- }
- bio = BIO_new_socket(stream->tcpstream->tcpsi, BIO_NOCLOSE);
- SSL_set_bio(stream->con, bio, bio);
- SSL_set_connect_state(stream->con);
- if (SSL_in_init(stream->con)) SSL_total_renegotiations(stream->con);
- /* now negotiate SSL */
- if (SSL_write(stream->con, "", 0) < 0)
- return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
- /* need to validate host names? */
- if (!(flags & NET_NOVALIDATECERT) &&
- (err = ssl_validate_cert(cert = SSL_get_peer_certificate(stream->con),
- host))) {
- /* application callback */
- X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
- if (scq) return (*scq) (err, host, cert ? buf : "???") ? NIL : "";
- /* error message to return via mm_log() */
- sprintf(tmp, "*%.128s: %.255s", err, cert ? buf : "???");
- return ssl_last_error = cpystr(tmp);
- }
- return NIL;
-}
-
-/* SSL certificate verification callback
- * Accepts: error flag
- * X509 context
- * Returns: error flag
- */
-
-static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
-{
- char *err,cert[256],tmp[MAILTMPLEN];
- sslcertificatequery_t scq =
- (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
- if (!ok) { /* in case failure */
- err = (char *) X509_verify_cert_error_string
- (X509_STORE_CTX_get_error (ctx));
- X509_NAME_oneline (X509_get_subject_name
- (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
- if (!scq) { /* mm_log() error message if no callback */
- sprintf (tmp,"*%.128s: %.255s",err,cert);
- ssl_last_error = cpystr (tmp);
- }
- /* ignore error if application says to */
- else if ((*scq) (err,ssl_last_host,cert)) ok = T;
- /* application wants punt */
- else ssl_last_error = cpystr ("");
- }
- return ok;
-}
-
-
-/* SSL validate certificate
- * Accepts: certificate
- * host to validate against
- * Returns: NIL if validated, else string of error message
- */
-
-static char *ssl_validate_cert (X509 *cert,char *host)
-{
- int i,j,n, m = 0;;
- char *s=NULL,*t,*ret = NIL;
- void *ext;
- GENERAL_NAME *name;
- X509_NAME *cname;
- X509_NAME_ENTRY *e;
- char buf[256];
- /* make sure have a certificate */
- if (!cert) return "No certificate from server";
- /* Method 1: locate CN */
-#ifndef OPENSSL_1_1_0
- if (cert->name == NIL)
- ret = "No name in certificate";
- else if ((s = strstr (cert->name,"/CN=")) != NIL) {
- m++; /* count that we tried this method */
- if (t = strchr (s += 4,'/')) *t = '\0';
- /* host name matches pattern? */
- ret = ssl_compare_hostnames (host,s) ? NIL :
- "Server name does not match certificate";
- if (t) *t = '/'; /* restore smashed delimiter */
- /* if mismatch, see if in extensions */
- if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
- (n = sk_GENERAL_NAME_num (ext)))
- /* older versions of OpenSSL use "ia5" instead of dNSName */
- for (i = 0; ret && (i < n); i++)
- if ((name = sk_GENERAL_NAME_value (ext,i)) &&
- (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
- ssl_compare_hostnames (host,s)) ret = NIL;
- }
-#endif /* OPENSSL_1_1_0 */
- /* Method 2, use Cname */
- if(m == 0 || ret != NIL){
- cname = X509_get_subject_name(cert);
- for(j = 0, ret = NIL; j < X509_NAME_entry_count(cname) && ret == NIL; j++){
- if((e = X509_NAME_get_entry(cname, j)) != NULL){
- X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
- s = (char *) buf;
- }
- else s = NIL;
- if (s != NIL) {
- /* host name matches pattern? */
- ret = ssl_compare_hostnames (host,s) ? NIL :
- "Server name does not match certificate";
- /* if mismatch, see if in extensions */
- if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
- (n = sk_GENERAL_NAME_num (ext)))
- /* older versions of OpenSSL use "ia5" instead of dNSName */
- for (i = 0; ret && (i < n); i++)
- if ((name = sk_GENERAL_NAME_value (ext,i)) &&
- (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
- ssl_compare_hostnames (host,s)) ret = NIL;
- }
- }
- }
-
- if (ret == NIL
-#ifndef OPENSSL_1_1_0
- && !cert->name
-#endif /* OPENSSL_1_1_9 */
- && !X509_get_subject_name(cert))
- ret = "No name in certificate";
-
- if (ret == NIL && s == NIL)
- ret = "Unable to locate common name in certificate";
-
- return ret;
-}
-
-/* Case-independent wildcard pattern match
- * Accepts: base string
- * pattern string
- * Returns: T if pattern matches base, else NIL
- */
-
-static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
-{
- long ret = NIL;
- switch (*pat) {
- case '*': /* wildcard */
- if (pat[1]) { /* there must be a pattern suffix */
- /* there is, scan base against it */
- do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
- while (!ret && (*s != '.') && *s++);
- }
- break;
- case '\0': /* end of pattern */
- if (!*s) ret = LONGT; /* success if base is also at end */
- break;
- default: /* non-wildcard, recurse if match */
- if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
- break;
- }
- return ret;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* SSL receive data
- * Accepts: TCP/IP stream
- * Returns: T if success, NIL otherwise
- */
-
-#ifdef FD_SETSIZE
-#undef FD_SETSIZE
-#endif /* FD_SETSIZE */
-#define FD_SETSIZE 16384
-
-long ssl_getdata (SSLSTREAM *stream)
-{
- int i,sock;
- fd_set fds,efds;
- struct timeval tmo;
- tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
- long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
- time_t t = time (0);
- blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
- if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
- /* tcp_unix should have prevented this */
- if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
- (*bn) (BLOCK_TCPREAD,NIL);
- while (stream->ictr < 1) { /* if nothing in the buffer */
- time_t tl = time (0); /* start of request */
- time_t now = tl;
- int ti = ttmo_read ? now + ttmo_read : 0;
- if (SSL_pending (stream->con)) i = 1;
- else {
- if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
- tmo.tv_usec = 0;
- FD_ZERO (&fds); /* initialize selection vector */
- FD_ZERO (&efds); /* handle errors too */
- FD_SET (sock,&fds); /* set bit in selection vector */
- FD_SET (sock,&efds); /* set bit in error selection vector */
- errno = NIL; /* block and read */
- do { /* block under timeout */
- tmo.tv_sec = ti ? ti - now : 0;
- i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
- now = time (0); /* fake timeout if interrupt & time expired */
- if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
- } while ((i < 0) && (errno == EINTR));
- }
- if (i) { /* non-timeout result from select? */
- errno = 0; /* just in case */
- if (i > 0) /* read what we can */
- while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
- ((errno == EINTR) ||
- (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
- if (i <= 0) { /* error seen? */
- if (tcpdebug) {
- char *s,tmp[MAILTMPLEN];
- if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
- errno,SSL_get_error (stream->con,i));
- else s = "SSL data read end of file";
- mm_log (s,TCPDEBUG);
- }
- return ssl_abort (stream);
- }
- stream->iptr = stream->ibuf;/* point at TCP buffer */
- stream->ictr = i; /* set new byte count */
- if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
- }
- /* timeout, punt unless told not to */
- else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
- if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
- return ssl_abort (stream);
- }
- }
- (*bn) (BLOCK_NONE,NIL);
- return T;
-}
-
-/* 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)
-{
- long i;
- blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
- if (!stream->con) return NIL;
- (*bn) (BLOCK_TCPWRITE,NIL);
- if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
- /* until request satisfied */
- for (i = 0; size > 0; string += i,size -= i)
- /* write as much as we can */
- if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
- if (tcpdebug) {
- char tmp[MAILTMPLEN];
- sprintf (tmp,"SSL data write I/O error %d SSL error %d",
- errno,SSL_get_error (stream->con,i));
- mm_log (tmp,TCPDEBUG);
- }
- return ssl_abort (stream);/* write failed */
- }
- if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
- (*bn) (BLOCK_NONE,NIL);
- return LONGT; /* all done */
-}
-
-/* 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)
-{
- blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
- if (stream->con) { /* close SSL connection */
- SSL_shutdown (stream->con);
- SSL_free (stream->con);
- stream->con = NIL;
- }
- if (stream->context) { /* clean up context */
- SSL_CTX_free (stream->context);
- stream->context = NIL;
- }
- if (stream->tcpstream) { /* close TCP stream */
- tcp_close (stream->tcpstream);
- stream->tcpstream = NIL;
- }
- (*bn) (BLOCK_NONE,NIL);
- return NIL;
-}
-
-/* SSL get host name
- * Accepts: SSL stream
- * Returns: host name for this stream
- */
-
-char *ssl_host (SSLSTREAM *stream)
-{
- return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
-}
-
-
-/* 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);
-}
-
-#ifdef SSL_CERT_DIRECTORY
-#undef SSL_CERT_DIRECTORY
-#endif /* SSL_CERT_DIRECTORY */
-#define SSL_CERT_DIRECTORY "C:\\\\libressl\\ssl\\certs"
-
-#ifdef SSL_KEY_DIRECTORY
-#undef SSL_KEY_DIRECTORY
-#endif /* SSL_KEY_DIRECTORY */
-#define SSL_KEY_DIRECTORY "C:\\\\libressl\\ssl\\private"
-
-
-/* Start TLS
- * Accepts: /etc/services service name
- * Returns: cpystr'd error string if TLS failed, else NIL for success
- */
-char *ssl_start_tls (char *server)
-{
- char tmp[MAILTMPLEN];
- struct stat sbuf;
- if (sslstdio) return cpystr ("Already in an SSL session");
- if (start_tls) return cpystr ("TLS already started");
- if (server) { /* build specific certificate/key file name */
- sprintf (tmp,"%s\\%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
- if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
- sprintf (tmp,"%s\\%s.pem",SSL_CERT_DIRECTORY,server);
- if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
- }
- start_tls = server; /* switch to STARTTLS mode */
- }
- return NIL;
-}
-
-/* Init server for SSL
- * Accepts: server name
- */
-
-void ssl_server_init (char *server)
-{
- char cert[MAILTMPLEN],key[MAILTMPLEN];
- unsigned long i;
- struct stat sbuf;
- SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
- sizeof (SSLSTREAM));
- ssl_onceonlyinit (); /* make sure algorithms added */
-#ifdef OPENSSL_1_1_0
- OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
- OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
-#else
- ERR_load_crypto_strings ();
- SSL_load_error_strings ();
-#endif /* OPENSSL_1_1_0 */
- /* build specific certificate/key file names */
- sprintf (cert,"%s\\%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
- sprintf (key,"%s\\%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
- /* use non-specific name if no specific cert */
- if (stat (cert,&sbuf)) sprintf (cert,"%s\\%s.pem",SSL_CERT_DIRECTORY,server);
- if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
- sprintf (key,"%s\\%s.pem",SSL_KEY_DIRECTORY,server);
- /* use cert file as fallback for key */
- if (stat (key,&sbuf)) strcpy (key,cert);
- }
- /* create context */
-#ifdef OPENSSL_1_1_0
- if (!(stream->context = SSL_CTX_new (start_tls ?
- TLS_server_method () :
- SSLv23_server_method ())))
-#else
- if (!(stream->context = SSL_CTX_new (start_tls ?
- TLSv1_server_method () :
- SSLv23_server_method ())))
-#endif /* OPENSSL_1_1_0 */
- syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
- tcp_clienthost ());
- else { /* set context options */
- SSL_CTX_set_options (stream->context,SSL_OP_ALL);
- /* set cipher list */
- if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
- syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
- SSLCIPHERLIST,tcp_clienthost ());
- /* load certificate */
- else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
- syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
- cert,tcp_clienthost ());
- /* load key */
- else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
- SSL_FILETYPE_PEM)))
- syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
- key,tcp_clienthost ());
-
- else { /* generate key if needed */
-#ifdef OPENSSL_1_1_0
- if (0)
- ssl_genkey(stream->context, 0, 0);
-#else
- if (SSL_CTX_need_tmp_RSA (stream->context))
- SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
-#endif /* OPENSSL_1_1_0 */
- /* create new SSL connection */
- if (!(stream->con = SSL_new (stream->context)))
- syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
- tcp_clienthost ());
- else { /* set file descriptor */
- SSL_set_fd (stream->con,0);
- /* all OK if accepted */
- if (SSL_accept (stream->con) < 0)
- syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
- tcp_clienthost ());
- else { /* server set up */
- sslstdio = (SSLSTDIOSTREAM *)
- memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
- sslstdio->sslstream = stream;
- /* available space in output buffer */
- sslstdio->octr = SSLBUFLEN;
- /* current output buffer pointer */
- sslstdio->optr = sslstdio->obuf;
- /* allow plaintext if disable value was 2 */
- if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
- mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
- /* unhide PLAIN SASL authenticator */
- mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
- mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
- return;
- }
- }
- }
- }
- while ((i = ERR_get_error ()) != 0L) /* SSL failure */
- syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
- ssl_close (stream); /* punt stream */
- exit (1); /* punt this program too */
-}
-
-/* Generate one-time key for server
- * Accepts: SSL connection
- * export flag
- * keylength
- * Returns: generated key, always
- */
-
-static RSA *ssl_genkey (SSL_CTX_TYPE *con,int export,int keylength)
-{
- unsigned long i;
- static RSA *key = NIL;
- if (!key) { /* if don't have a key already */
- /* generate key */
-#ifdef OPENSSL_1_1_0
- BIGNUM *e = BN_new();
- if (!RSA_generate_key_ex (key, export ? keylength : 1024, e,NIL)) {
-#else
- if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
-#endif /* OPENSSL_1_1_0 */
- syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
- tcp_clienthost ());
- while ((i = ERR_get_error ()) != 0L)
- syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
- exit (1);
- }
-#ifdef OPENSSL_1_1_0
- BN_free(e);
- e = NULL;
-#endif /* OPENSSL_1_1_0 */
- }
- return key;
-}
-
-/* Wait for stdin input
- * Accepts: timeout in seconds
- * Returns: T if have input on stdin, else NIL
- */
-
-long ssl_server_input_wait (long seconds)
-{
- int i,sock;
- fd_set fds,efd;
- struct timeval tmo;
- SSLSTREAM *stream;
- if (!sslstdio) return server_input_wait (seconds);
- /* input available in buffer */
- if (((stream = sslstdio->sslstream)->ictr > 0) ||
- !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
- /* sock ought to be 0 always */
- if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
- /* input available from SSL */
- if (SSL_pending (stream->con) &&
- ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
- stream->iptr = stream->ibuf;/* point at TCP buffer */
- stream->ictr = i; /* set new byte count */
- return LONGT;
- }
- FD_ZERO (&fds); /* initialize selection vector */
- FD_ZERO (&efd); /* initialize selection vector */
- FD_SET (sock,&fds); /* set bit in selection vector */
- FD_SET (sock,&efd); /* set bit in selection vector */
- tmo.tv_sec = seconds; tmo.tv_usec = 0;
- /* see if input available from the socket */
- return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
-}
-
-#include "sslstdio.c"
-#else /* ENABLE_WINDOWS_LIBRESSL */
-#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);
-
-/* 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;
-
-/* 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 */
- }
- }
- }
-}
-
-/* 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 */
-}
-
-/* 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;
-
- /* 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;
-
- 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);
-
- 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;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* 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 */
- if (!stream->tcpstream) return NIL;
- 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;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-
-/* SSL get host name
- * Accepts: SSL stream
- * Returns: host name for this stream
- */
-
-char *ssl_host (SSLSTREAM *stream)
-{
- return stream ? tcp_host (stream->tcpstream) : "UNKNOWN";
-}
-
-
-/* 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 */
-
-#endif /* ENABLE_WINDOWS_LIBRESSL */
+//#ifdef ENABLE_WINDOWS_LIBRESSL
+//#include "ssl_libressl.c"
+//#else
+#include "ssl_win.c"
+//#endif /* ENABLE_WINDOWS_LIBRESSL */