diff options
Diffstat (limited to 'imap/src/osdep/nt/ssl_nt.c')
-rw-r--r-- | imap/src/osdep/nt/ssl_nt.c | 1764 |
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 */ |