From 9449b23bbca71471a64f914b4bc7ec7d810e587f Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 12 Aug 2018 22:34:43 -0600 Subject: * Several changes to the compilation of Alpine in Windows to use LibreSSL in connecting to external servers. This complements the changes to support S/MIME. In particular, we add support for validation of certificates by using C:\\libressl\ssl\certs as the place to save CA certificates. In order to help users, some certificates are distributed. TODO: Kerberos port, w2k. --- imap/src/c-client/imap4r1.c | 5 +- imap/src/c-client/mail.c | 2 - imap/src/imapd/makefile.nt | 2 +- imap/src/imapd/makefile.ntk | 2 +- imap/src/ipopd/makefile.nt | 2 +- imap/src/ipopd/makefile.ntk | 2 +- imap/src/mailutil/mailutil.c | 3 +- imap/src/mailutil/makefile.nt | 6 +- imap/src/mailutil/makefile.ntk | 4 +- imap/src/mtest/makefile.nt | 2 +- imap/src/mtest/makefile.ntk | 2 +- imap/src/osdep/nt/makefile.nt | 21 +- imap/src/osdep/nt/makefile.ntk | 18 +- imap/src/osdep/nt/makefile.old | 2 +- imap/src/osdep/nt/makefile.w2k | 15 +- imap/src/osdep/nt/proc.c | 80 ++++ imap/src/osdep/nt/ssl_nt.c | 912 +++++++++++++++++++++++++++++++++++++++++ imap/src/osdep/nt/ssl_old.c | 4 - imap/src/osdep/nt/sslstdio.c | 181 ++++++++ 19 files changed, 1225 insertions(+), 40 deletions(-) create mode 100644 imap/src/osdep/nt/proc.c create mode 100644 imap/src/osdep/nt/sslstdio.c (limited to 'imap/src') diff --git a/imap/src/c-client/imap4r1.c b/imap/src/c-client/imap4r1.c index b20453ee..abaddfe4 100644 --- a/imap/src/c-client/imap4r1.c +++ b/imap/src/c-client/imap4r1.c @@ -3363,13 +3363,12 @@ IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s, long imap_search_x_gm_ext1 (MAILSTREAM *stream, char *charset, SEARCHPGM *pgm, long flags) { char *cmd = (flags & SE_UID) ? "UID SEARCH X-GM-RAW" : "SEARCH X-GM-RAW"; - char *p, *t, s[MAILTMPLEN+1], u[MAILTMPLEN], v[MAILTMPLEN]; - IMAPARG *args[4],apgm,aatt,achs;; + char *t, s[MAILTMPLEN+1], u[MAILTMPLEN]; + IMAPARG *args[4],apgm; IMAPPARSEDREPLY *reply; unsigned long i,j,k; MESSAGECACHE *elt; size_t remain = sizeof(s) - 1, len; - NETMBX mb; u[0] = s[0] = '\0'; t = s; diff --git a/imap/src/c-client/mail.c b/imap/src/c-client/mail.c index 3dd45eb5..43db47aa 100644 --- a/imap/src/c-client/mail.c +++ b/imap/src/c-client/mail.c @@ -2793,8 +2793,6 @@ BODY *mail_body_section (BODY *b, unsigned char *section) BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,unsigned char *section) { BODY *b = NIL; - PART *pt; - unsigned long i; /* make sure have a body */ if (section && *section && mail_fetchstructure (stream,msgno,&b) && b) return mail_body_section(b, section); diff --git a/imap/src/imapd/makefile.nt b/imap/src/imapd/makefile.nt index 41216701..9831ffea 100644 --- a/imap/src/imapd/makefile.nt +++ b/imap/src/imapd/makefile.nt @@ -34,7 +34,7 @@ NNTP=\\imapd.nntp C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" diff --git a/imap/src/imapd/makefile.ntk b/imap/src/imapd/makefile.ntk index 5c5500e6..31d24d9f 100644 --- a/imap/src/imapd/makefile.ntk +++ b/imap/src/imapd/makefile.ntk @@ -37,7 +37,7 @@ CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" diff --git a/imap/src/ipopd/makefile.nt b/imap/src/ipopd/makefile.nt index ef32819b..88460f7b 100644 --- a/imap/src/ipopd/makefile.nt +++ b/imap/src/ipopd/makefile.nt @@ -28,7 +28,7 @@ C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) diff --git a/imap/src/ipopd/makefile.ntk b/imap/src/ipopd/makefile.ntk index 4a90a7a0..00782e4b 100644 --- a/imap/src/ipopd/makefile.ntk +++ b/imap/src/ipopd/makefile.ntk @@ -30,7 +30,7 @@ CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) diff --git a/imap/src/mailutil/mailutil.c b/imap/src/mailutil/mailutil.c index 15ae636b..d8faefa4 100644 --- a/imap/src/mailutil/mailutil.c +++ b/imap/src/mailutil/mailutil.c @@ -456,9 +456,8 @@ int main (int argc,char *argv[]) char *mailutil_string_sequence(MAILSTREAM *m) { char *rv = NULL; - unsigned long i, j, count; + unsigned long i, j; size_t len = 0; - MESSAGECACHE elt; if(m == NULL || m->nmsgs == 0L) return NULL; diff --git a/imap/src/mailutil/makefile.nt b/imap/src/mailutil/makefile.nt index 1a6eb515..97324350 100644 --- a/imap/src/mailutil/makefile.nt +++ b/imap/src/mailutil/makefile.nt @@ -27,9 +27,9 @@ C = ..\c-client CCLIENTLIB = $C\cclient.lib -LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib -CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS) -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib $(LIBRESSLLIBS) +CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0501 -nologo $(EXTRACFLAGS) +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) diff --git a/imap/src/mailutil/makefile.ntk b/imap/src/mailutil/makefile.ntk index 42ae5185..f4cd28d4 100644 --- a/imap/src/mailutil/makefile.ntk +++ b/imap/src/mailutil/makefile.ntk @@ -29,8 +29,8 @@ C = ..\c-client CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib -LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib $(LIBRESSLLIBS) +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) diff --git a/imap/src/mtest/makefile.nt b/imap/src/mtest/makefile.nt index 9486e42b..b3fcfeba 100644 --- a/imap/src/mtest/makefile.nt +++ b/imap/src/mtest/makefile.nt @@ -28,7 +28,7 @@ C = ..\c-client CCLIENTLIB = $C\cclient.lib LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) diff --git a/imap/src/mtest/makefile.ntk b/imap/src/mtest/makefile.ntk index 91508162..0ddb50f5 100644 --- a/imap/src/mtest/makefile.ntk +++ b/imap/src/mtest/makefile.ntk @@ -30,7 +30,7 @@ CCLIENTLIB = $C\cclient.lib K5 = \k5\lib K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) diff --git a/imap/src/osdep/nt/makefile.nt b/imap/src/osdep/nt/makefile.nt index a30dfc40..b56ed36c 100644 --- a/imap/src/osdep/nt/makefile.nt +++ b/imap/src/osdep/nt/makefile.nt @@ -1,4 +1,5 @@ # ======================================================================== +# Copyright 2018 Eduardo Chappa # Copyright 1988-2007 University of Washington # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +23,7 @@ # # Date: 11 May 1989 # Last Edited: 23 May 2007 +# Last Edited 1 Aug 2018 EXTRAAUTHENTICATORS = @@ -31,7 +33,7 @@ AUTHENTICATORS = ext md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS) CC = cl @@ -44,15 +46,22 @@ all: $(CCLIENTLIB) osdep.h: os_nt.h copy os_nt.h osdep.h - drivers $(EXTRADRIVERS) $(DRIVERS) dummy - setproto $(CREATEDRIVER) $(APPENDDRIVER) - echo ssl_onceonlyinit (); >> linkage.c - mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) - echo mail_versioncheck (CCLIENTVERSION); >> linkage.c + if exist linkage.c del linkage.c + if exist linkage.h del linkage.h + if exist auths.c del auths.c + $(CC) -c $(CFLAGS) proc.c + link /subsystem:console /out:proc.exe proc.obj + proc drivers $(EXTRADRIVERS) $(DRIVERS) dummy + proc setproto $(CREATEDRIVER) $(APPENDDRIVER) + proc sslinit + proc mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) + proc version ip_nt.c: ip4_nt.c copy ip4_nt.c ip_nt.c +proc.obj: proc.c + mail.obj: mail.h misc.h osdep.h mail.c misc.obj: mail.h misc.h misc.c diff --git a/imap/src/osdep/nt/makefile.ntk b/imap/src/osdep/nt/makefile.ntk index 507d7541..8ac5055e 100644 --- a/imap/src/osdep/nt/makefile.ntk +++ b/imap/src/osdep/nt/makefile.ntk @@ -22,6 +22,7 @@ # # Date: 11 May 1989 # Last Edited: 23 May 2007 +# Last Edited: 1 Aug 2018 EXTRAAUTHENTICATORS = @@ -31,7 +32,7 @@ AUTHENTICATORS = ext gss md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 /I\k5\include +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 /I\k5\include VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS) CC = cl @@ -44,11 +45,16 @@ all: $(CCLIENTLIB) osdep.h: os_nt.h copy os_nt.h osdep.h - drivers $(EXTRADRIVERS) $(DRIVERS) dummy - setproto $(CREATEDRIVER) $(APPENDDRIVER) - echo ssl_onceonlyinit (); >> linkage.c - mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) - echo mail_versioncheck (CCLIENTVERSION); >> linkage.c + if exist linkage.c del linkage.c + if exist linkage.h del linkage.h + if exist auths.c del auths.c + $(CC) -c $(CFLAGS) proc.c + link /subsystem:console /out:proc.exe proc.obj + proc drivers $(EXTRADRIVERS) $(DRIVERS) dummy + proc setproto $(CREATEDRIVER) $(APPENDDRIVER) + proc sslinit + proc mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) + proc version ip_nt.c: ip4_nt.c copy ip4_nt.c ip_nt.c diff --git a/imap/src/osdep/nt/makefile.old b/imap/src/osdep/nt/makefile.old index 028c98ba..5da00539 100644 --- a/imap/src/osdep/nt/makefile.old +++ b/imap/src/osdep/nt/makefile.old @@ -31,7 +31,7 @@ AUTHENTICATORS = ext md5 pla log DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix -OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 +OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0501 VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS) CC = cl diff --git a/imap/src/osdep/nt/makefile.w2k b/imap/src/osdep/nt/makefile.w2k index a58f0274..ba4a55fa 100644 --- a/imap/src/osdep/nt/makefile.w2k +++ b/imap/src/osdep/nt/makefile.w2k @@ -45,11 +45,16 @@ all: $(CCLIENTLIB) osdep.h: os_nt.h copy os_nt.h osdep.h - drivers $(EXTRADRIVERS) $(DRIVERS) dummy - setproto $(CREATEDRIVER) $(APPENDDRIVER) - echo ssl_onceonlyinit (); >> linkage.c - mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) - echo mail_versioncheck (CCLIENTVERSION); >> linkage.c + if exist linkage.c del linkage.c + if exist linkage.h del linkage.h + if exist auths.c del auths.c + $(CC) -c $(CFLAGS) proc.c + link /subsystem:console /out:proc.exe proc.obj + proc drivers $(EXTRADRIVERS) $(DRIVERS) dummy + proc setproto $(CREATEDRIVER) $(APPENDDRIVER) + proc sslinit + proc mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS) + proc version ip_nt.c: ip$(IP)_nt.c copy ip$(IP)_nt.c ip_nt.c diff --git a/imap/src/osdep/nt/proc.c b/imap/src/osdep/nt/proc.c new file mode 100644 index 00000000..8fc3c4ee --- /dev/null +++ b/imap/src/osdep/nt/proc.c @@ -0,0 +1,80 @@ +/* + * Copyright 2018 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include +#include +#include +#include + +int main(int, char **); + +int +main(int argc, char *argv[]) +{ + int rv = 0, i; + FILE *fph, *fpc, *fpa; + char *opt; + + fph = fpc = fpa = NULL; + if(argc < 2){ + fprintf(stdout, "Not enough arguments.\n"); + fprintf(stdout, "Usage: %s opt ...\n", argv[0]); + fprintf(stdout, "opt can be drivers, mkauths, version, setproto or sslinit\n"); + exit(1); + } + + opt = argv[1]; + if(!strcmp(opt, "drivers")){ + fph = fopen("linkage.h", "w"); + fpc = fopen("linkage.c", "w"); + for (i = 2; i < argc; i++){ + fprintf(fph, "extern DRIVER %sdriver;\n", argv[i]); + fprintf(fpc, " mail_link (&%sdriver); /* link in the %s driver */\n", + argv[i], argv[i]); + } + } + else if(!strcmp(opt, "mkauths")){ + fph = fopen("linkage.h", "a"); + fpc = fopen("linkage.c", "a"); + fpa = fopen("auths.c", "w"); + for (i = 2; i < argc; i++){ + fprintf(fph, "extern AUTHENTICATOR auth_%s;\n", argv[i]); + fprintf(fpc, " auth_link (&auth_%s); /* link in the %s authenticator */\n", + argv[i], argv[i]); + fprintf(fpa, "#include \"auth_%s.c\"\n", argv[i]); + } + } + else if(!strcmp(opt, "setproto")){ + if(argc != 4){ + fprintf(stdout, "setproto requires two additional arguments\n"); + exit(1); + } + fph = fopen("linkage.h", "a"); + fprintf(fph, "#define CREATEPROTO %sproto\n", argv[2]); + fprintf(fph, "#define APPENDPROTO %sproto\n", argv[3]); + } + else if(!strcmp(opt, "sslinit")){ + fpc = fopen("linkage.c", "a"); + fprintf(fpc, "%s\n", "ssl_onceonlyinit();"); + } + else if(!strcmp(opt, "version")){ + fpc = fopen("linkage.c", "a"); + fprintf(fpc, "%s\n", "mail_versioncheck(CCLIENTVERSION);"); + } + else { + fprintf(stdout, "Try: \"drivers\", \"mkauths\", \"setproto\", \"sslinit\", or \"version\".\n"); + exit(1); + } + if(fpa != NULL) fclose(fpa); + if(fpc != NULL) fclose(fpc); + if(fph != NULL) fclose(fph); + exit(0); +} diff --git a/imap/src/osdep/nt/ssl_nt.c b/imap/src/osdep/nt/ssl_nt.c index 7c2248d1..d352980e 100644 --- a/imap/src/osdep/nt/ssl_nt.c +++ b/imap/src/osdep/nt/ssl_nt.c @@ -1,4 +1,5 @@ /* ======================================================================== + * Copyright 2018 Eduardo Chappa * Copyright 2008-2009 Mark Crispin * ======================================================================== */ @@ -22,7 +23,916 @@ * http://www.apache.org/licenses/LICENSE-2.0 * */ +#ifdef ENABLE_WINDOWS_LIBRESSL +#define crypt ssl_private_crypt +#define STRING OPENSSL_STRING +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef OPENSSL_1_1_0 +#include +#include +#ifdef TLSv1_1_client_method +#undef TLSv1_1_client_method +#endif /* TLSv1_1_client_method */ +#ifdef TLSv1_2_client_method +#undef TLSv1_2_client_method +#endif /* TLSv1_2_client_method */ +#define TLSv1_1_client_method TLS_client_method +#define TLSv1_2_client_method TLS_client_method +#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); + + +/* 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 */ +} + +/* ssl_connect_mthd: returns a context pointer to the connection to + * a ssl server + */ +const SSL_METHOD *ssl_connect_mthd(int flag) +{ +#ifdef OPENSSL_1_1_0 + if(flag & NET_TRYTLS1) + return TLS_client_method(); +#else + if(flag & NET_TRYTLS1) + return TLSv1_client_method(); +#endif /* OPENSSL_1_1_0 */ +#ifdef TLSV1_2 + else if(flag & NET_TRYTLS1_1) + return TLSv1_1_client_method(); + else if(flag & NET_TRYTLS1_2) + return TLSv1_2_client_method(); +#endif /* TLSV1_2 */ +#ifdef OPENSSL_1_1_0 + else if(flag & NET_TRYDTLS1) + return DTLS_client_method(); +#else + else if(flag & NET_TRYDTLS1) + return DTLSv1_client_method(); +#endif /* OPENSSL_1_1_0 */ + else 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; + 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)))) + return "SSL context failed"; + SSL_CTX_set_options (stream->context,0); + /* 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"; + 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,n; + char *s=NULL,*t,*ret; + void *ext; + GENERAL_NAME *name; + X509_NAME *cname; + X509_NAME_ENTRY *e; + char buf[256]; + /* make sure have a certificate */ + if (!cert) ret = "No certificate from server"; + /* and that it has a name */ + else if (!(cname = X509_get_subject_name(cert))) ret = "No name in certificate"; + /* locate CN */ + else{ + if((e = X509_NAME_get_entry(cname, X509_NAME_entry_count(cname)-1)) != NULL){ + X509_NAME_get_text_by_OBJ(cname, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf)); + s = (char *) buf; + } + else s = NULL; + } + if (s != NULL) { + /* 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; + } + else 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 #include @@ -717,3 +1627,5 @@ char *ssl_localhost (SSLSTREAM *stream) } #include "ssl_none.c" /* currently no server support */ + +#endif /* ENABLE_WINDOWS_LIBRESSL */ diff --git a/imap/src/osdep/nt/ssl_old.c b/imap/src/osdep/nt/ssl_old.c index 5d00b48b..73a360df 100644 --- a/imap/src/osdep/nt/ssl_old.c +++ b/imap/src/osdep/nt/ssl_old.c @@ -25,12 +25,8 @@ #define SECURITY_WIN32 #include -#if(_WIN32_WINNT < 0x0400) -typedef unsigned int ALG_ID; -#else #include ALGIDDEF -#endif #include #include diff --git a/imap/src/osdep/nt/sslstdio.c b/imap/src/osdep/nt/sslstdio.c new file mode 100644 index 00000000..3d3b1e06 --- /dev/null +++ b/imap/src/osdep/nt/sslstdio.c @@ -0,0 +1,181 @@ +/* ======================================================================== + * Copyright 2008 Mark Crispin + * ======================================================================== + */ + +/* + * Program: SSL standard I/O routines for server use + * + * Author: Mark Crispin + * + * Date: 22 September 1998 + * Last Edited: 19 November 2008 + * + * Previous versions of this file were + * + * Copyright 1988-2006 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +/* Get character + * Returns: character or EOF + */ + +int PBIN (void) +{ + if (!sslstdio) { + int ret; + do { + clearerr (stdin); + ret = getchar (); + } while ((ret == EOF) && !feof (stdin) && ferror (stdin) && + (errno == EINTR)); + return ret; + } + if (!ssl_getdata (sslstdio->sslstream)) return EOF; + /* one last byte available */ + sslstdio->sslstream->ictr--; + return (int) *(sslstdio->sslstream->iptr)++; +} + + +/* Get string + * Accepts: destination string pointer + * number of bytes available + * Returns: destination string pointer or NIL if EOF + */ + +char *PSIN (char *s,int n) +{ + int i,c; + if (start_tls) { /* doing a start TLS? */ + ssl_server_init (start_tls);/* enter the mode */ + start_tls = NIL; /* don't do this again */ + } + if (!sslstdio) { + char *ret; + do { + clearerr (stdin); + ret = fgets (s,n,stdin); + } while (!ret && !feof (stdin) && ferror (stdin) && (errno == EINTR)); + return ret; + } + for (i = c = 0, n-- ; (c != '\n') && (i < n); sslstdio->sslstream->ictr--) { + if ((sslstdio->sslstream->ictr <= 0) && !ssl_getdata (sslstdio->sslstream)) + return NIL; /* read error */ + c = s[i++] = *(sslstdio->sslstream->iptr)++; + } + s[i] = '\0'; /* tie off string */ + return s; +} + + +/* Get record + * Accepts: destination string pointer + * number of bytes to read + * Returns: T if success, NIL otherwise + */ + +long PSINR (char *s,unsigned long n) +{ + unsigned long i; + if (start_tls) { /* doing a start TLS? */ + ssl_server_init (start_tls);/* enter the mode */ + start_tls = NIL; /* don't do this again */ + } + if (sslstdio) return ssl_getbuffer (sslstdio->sslstream,n,s); + /* non-SSL case */ + while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i; + return n ? NIL : LONGT; +} + + +/* Wait for stdin input + * Accepts: timeout in seconds + * Returns: T if have input on stdin, else NIL + */ + +long INWAIT (long seconds) +{ + return (sslstdio ? ssl_server_input_wait : server_input_wait) (seconds); +} + +/* Put character + * Accepts: character + * Returns: character written or EOF + */ + +int PBOUT (int c) +{ + if (!sslstdio) return putchar (c); + /* flush buffer if full */ + if (!sslstdio->octr && PFLUSH ()) return EOF; + sslstdio->octr--; /* count down one character */ + *sslstdio->optr++ = c; /* write character */ + return c; /* return that character */ +} + + +/* Put string + * Accepts: destination string pointer + * Returns: 0 or EOF if error + */ + +int PSOUT (char *s) +{ + if (!sslstdio) return fputs (s,stdout); + while (*s) { /* flush buffer if full */ + if (!sslstdio->octr && PFLUSH ()) return EOF; + *sslstdio->optr++ = *s++; /* write one more character */ + sslstdio->octr--; /* count down one character */ + } + return 0; /* success */ +} + +/* Put record + * Accepts: source sized text + * Returns: 0 or EOF if error + */ + +int PSOUTR (SIZEDTEXT *s) +{ + unsigned char *t = s->data; + unsigned long i = s->size; + unsigned long j; + if (sslstdio) while (i) { /* until request satisfied */ + /* flush buffer if full */ + if (!sslstdio->octr && PFLUSH ()) break; + /* blat as big a chucnk as we can */ + memcpy (sslstdio->optr,t,j = min (i,sslstdio->octr)); + sslstdio->optr += j; /* account for chunk */ + sslstdio->octr -= j; + t += j; + i -= j; + } + else while (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR))) + t += j,i -= j; + return i ? EOF : NIL; +} + + +/* Flush output + * Returns: 0 or EOF if error + */ + +int PFLUSH (void) +{ + if (!sslstdio) return fflush (stdout); + /* force out buffer */ + if (!ssl_sout (sslstdio->sslstream,sslstdio->obuf, + SSLBUFLEN - sslstdio->octr)) return EOF; + /* renew output buffer */ + sslstdio->optr = sslstdio->obuf; + sslstdio->octr = SSLBUFLEN; + return 0; /* success */ +} -- cgit v1.2.3-54-g00ecf