diff options
Diffstat (limited to 'src')
73 files changed, 3393 insertions, 1119 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 000000000..2d6e95a32 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +Local +build-* diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index fe24e7700..a965469cf 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -18,8 +18,12 @@ relatively small patches. Philip Hazel Lists created: 20 November 2002 -Last updated: 22 August 2007 +Last updated (by PH): 22 August 2007 + Note: at current time, Exim is maintained in git; the commit messages + typically credit sources, at the very least. Also the ChangeLog file + will record who provided patches. This file is not very up-to-date. + -Phil Pennock, 2012 THE OLD LIST diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index de387e027..281294558 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -312,7 +312,7 @@ OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \ os.o parse.o queue.o \ rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \ route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \ - store.o string.o tls.o tod.o transport.o tree.o verify.o \ + std-crypto.o store.o string.o tls.o tod.o transport.o tree.o verify.o \ $(OBJ_LOOKUPS) \ local_scan.o $(EXIM_PERL) $(OBJ_WITH_CONTENT_SCAN) \ $(OBJ_WITH_OLD_DEMIME) $(OBJ_EXPERIMENTAL) @@ -575,6 +575,7 @@ smtp_in.o: $(HDRS) smtp_in.c smtp_out.o: $(HDRS) smtp_out.c spool_in.o: $(HDRS) spool_in.c spool_out.o: $(HDRS) spool_out.c +std-crypto.o: $(HDRS) std-crypto.c store.o: $(HDRS) store.c string.o: $(HDRS) string.c tls.o: $(HDRS) tls.c tls-gnu.c tls-openssl.c diff --git a/src/OS/Makefile-SunOS4 b/src/OS/Makefile-SunOS4 index 63db9c5df..c876998e5 100644 --- a/src/OS/Makefile-SunOS4 +++ b/src/OS/Makefile-SunOS4 @@ -2,11 +2,6 @@ CFLAGS=-O -# Don't need -DSTRERROR_FROM_ERRLIST in PCRE_CFLAGS, because it is in os.h -# for SunOS4, which gets included for pcre. - -PCRE_CFLAGS=-DUSE_BCOPY - CHOWN_COMMAND=/usr/etc/chown HOSTNAME_COMMAND=/usr/bin/hostname EXIT_FAILURE=1 diff --git a/src/OS/Makefile-mips b/src/OS/Makefile-mips index dbc101cd6..ff3313937 100644 --- a/src/OS/Makefile-mips +++ b/src/OS/Makefile-mips @@ -8,7 +8,6 @@ LIBS=-liberty -lm XINCLUDE=-I/usr/X11R6/include CFLAGS=-O -PCRE_CFLAGS=-DUSE_BCOPY -DSTRERROR_FROM_ERRLIST EXIWHAT_PS_ARG=-ax EXIWHAT_EGREP_ARG='/exim( |$$)' diff --git a/src/OS/eximon.conf-Default b/src/OS/eximon.conf-Default index 9e0769831..2f874ef53 100644 --- a/src/OS/eximon.conf-Default +++ b/src/OS/eximon.conf-Default @@ -5,7 +5,7 @@ # The name of the eximon binary, usually the same as the eximon script, # with .bin stuck on the end. -EXIMON_BINARY=${EXIMON_BINARY-$0.bin} +EXIMON_BINARY="${EXIMON_BINARY-$0.bin}" # The remaining parameters are values likely to be changed to suit the # user's taste. They are documented in the EDITME file. diff --git a/src/OS/os.h-Darwin b/src/OS/os.h-Darwin index 888b9c759..af06bdcbc 100644 --- a/src/OS/os.h-Darwin +++ b/src/OS/os.h-Darwin @@ -35,4 +35,8 @@ updating Exim to use the newer interface. */ /* It's not .so for dynamic libraries on Darwin. */ #define DYNLIB_FN_EXT "dylib" +/* We currently need some assistance getting OFF_T_FMT correct on MacOS */ +#define OFF_T_FMT "%llu" +#define LONGLONG_T long int + /* End */ diff --git a/src/OS/os.h-HP-UX b/src/OS/os.h-HP-UX index 05fc88ffe..87e4dfc97 100644 --- a/src/OS/os.h-HP-UX +++ b/src/OS/os.h-HP-UX @@ -17,4 +17,11 @@ typedef struct flock flock_t; +typedef struct __res_state *res_state; + +#define LLONG_MIN LONG_LONG_MIN +#define LLONG_MAX LONG_LONG_MAX + +#define strtoll(a,b,c) strtoimax(a,b,c) + /* End */ diff --git a/src/OS/os.h-Linux b/src/OS/os.h-Linux index eb7036d0f..fe4eaa6c7 100644 --- a/src/OS/os.h-Linux +++ b/src/OS/os.h-Linux @@ -1,5 +1,12 @@ /* Exim: OS-specific C header file for Linux */ +/* Some old systems we've received bug-reports for have a <limits.h> which +does not pull in <features.h>. Best to just pull it in now and have done +with the issue. */ + +#include <features.h> + + #define CRYPT_H #define GLIBC_IP_OPTIONS #define HAVE_MMAP diff --git a/src/OS/os.h-OpenBSD b/src/OS/os.h-OpenBSD index da9e1bf35..55bade674 100644 --- a/src/OS/os.h-OpenBSD +++ b/src/OS/os.h-OpenBSD @@ -11,4 +11,6 @@ typedef struct flock flock_t; #define os_strsignal strsignal #define OS_STRSIGNAL +typedef struct __res_state *res_state; + /* End */ diff --git a/src/README b/src/README index c8ecaa0cb..972d417c8 100644 --- a/src/README +++ b/src/README @@ -331,7 +331,7 @@ also need to add a new alias definition: "alias utf8 utf-8". 2. For some strange reason make will fail at building "exim_dbmbuild" when called the first time. However simply calling make a second time will solve - the problem. Alternatively, run "make makfile" and then "make". + the problem. Alternatively, run "make makefile" and then "make". ******* IMPORTANT FOR ULTRIX USERS ******* diff --git a/src/README.UPDATING b/src/README.UPDATING index 12335eab8..d34dec1e1 100644 --- a/src/README.UPDATING +++ b/src/README.UPDATING @@ -26,9 +26,13 @@ The rest of this document contains information about changes in 4.xx releases that might affect a running system. -Exim version 4.78 +Exim version 4.80 ----------------- + * BEWARE backwards-incompatible changes in SSL libraries, thus the version + bump. See points below for details. + Also an LDAP data returned format change. + * The value of $tls_peerdn is now print-escaped when written to the spool file in a -tls_peerdn line, and unescaped when read back in. We received reports of values with embedded newlines, which caused spool file corruption. @@ -70,12 +74,96 @@ Exim version 4.78 security for compatibility. Exim is now defaulting to higher security and rewarding more modern clients. + If the option tls_dhparams is set and the parameters loaded from the file + have a bit-count greater than the new option tls_dh_max_bits, then the file + will now be ignored. If this affects you, raise the tls_dh_max_bits limit. + We suspect that most folks are using dated defaults and will not be affected. + * Ldap lookups returning multi-valued attributes now separate the attributes with only a comma, not a comma-space sequence. Also, an actual comma within a returned attribute is doubled. This makes it possible to parse the attribute as a comma-separated list. Note the distinction from multiple attributes being returned, where each one is a name=value pair. + If you are currently splitting the results from LDAP upon a comma, then you + should check carefully to see if adjustments are needed. + + This change lets cautious folks distinguish "comma used as separator for + joining values" from "comma inside the data". + + * accept_8bitmime now defaults on, which is not RFC compliant but is better + suited to today's Internet. See http://cr.yp.to/smtp/8bitmime.html for a + sane rationale. Those who wish to be strictly RFC compliant, or know that + they need to talk to servers that are not 8-bit-clean, now need to take + explicit configuration action to default this option off. This is not a + new option, you can safely force it off before upgrading, to decouple + configuration changes from the binary upgrade while remaining RFC compliant. + + * The GnuTLS support has been mostly rewritten, to use APIs which don't cause + deprecation warnings in GnuTLS 2.12.x. As part of this, these three options + are no longer supported: + + gnutls_require_kx + gnutls_require_mac + gnutls_require_protocols + + Their functionality is entirely subsumed into tls_require_ciphers. In turn, + tls_require_ciphers is no longer an Exim list and is not parsed by Exim, but + is instead given to gnutls_priority_init(3), which expects a priority string; + this behaviour is much closer to the OpenSSL behaviour. See: + + http://www.gnu.org/software/gnutls/manual/html_node/Priority-Strings.html + + for fuller documentation of the strings parsed. The three gnutls_require_* + options are still parsed by Exim and, for this release, silently ignored. + A future release will add warnings, before a later still release removes + parsing entirely and the presence of the options will be a configuration + error. + + Note that by default, GnuTLS will not accept RSA-MD5 signatures in chains. + A tls_require_ciphers value of NORMAL:%VERIFY_ALLOW_SIGN_RSA_MD5 may + re-enable support, but this is not supported by the Exim maintainers. + Our test suite no longer includes MD5-based certificates. + + This rewrite means that Exim will continue to build against GnuTLS in the + future, brings Exim closer to other GnuTLS applications and lets us add + support for SNI and other features more readily. We regret that it wasn't + feasible to retain the three dropped options. + + * If built with TLS support, then Exim will now validate the value of + the main section tls_require_ciphers option at start-up. Before, this + would cause a STARTTLS 4xx failure, now it causes a failure to start. + Running with a broken configuration which causes failures that may only + be left in the logs has been traded off for something more visible. This + change makes an existing problem more prominent, but we do not believe + anyone would deliberately be running with an invalid tls_require_ciphers + option. + + This also means that library linkage issues caused by conflicts of some + kind might take out the main daemon, not just the delivery or receiving + process. Conceivably some folks might prefer to continue delivering + mail plaintext when their binary is broken in this way, if there is a + server that is a candidate to receive such mails that does not advertise + STARTTLS. Note that Exim is typically a setuid root binary and given + broken linkage problems that cause segfaults, we feel it is safer to + fail completely. (The check is not done as root, to ensure that problems + here are not made worse by the check). + + * The "tls_dhparam" option has been updated, so that it can now specify a + path or an identifier for a standard DH prime from one of a few RFCs. + The default for OpenSSL is no longer to not use DH but instead to use + one of these standard primes. The default for GnuTLS is no longer to use + a file in the spool directory, but to use that same standard prime. + The option is now used by GnuTLS too. If it points to a path, then + GnuTLS will use that path, instead of a file in the spool directory; + GnuTLS will attempt to create it if it does not exist. + + To preserve the previous behaviour of generating files in the spool + directory, set "tls_dhparam = historic". Since prior releases of Exim + ignored tls_dhparam when using GnuTLS, this can safely be done before + the upgrade. + + Exim version 4.77 ----------------- @@ -86,6 +174,9 @@ Exim version 4.77 problem. Prior to this release, supported values were "TLS1" and "SSL3", so you should be able to update configuration prior to update. + [nb: gnutls_require_protocols removed in Exim 4.80, instead use + tls_require_ciphers to provide a priority string; see notes above] + * The match_<type>{string1}{string2} expansion conditions no longer subject string2 to string expansion, unless Exim was built with the new "EXPAND_LISTMATCH_RHS" option. Too many people have inadvertently created diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index 720549382..816d42d05 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -2,7 +2,7 @@ * Exim Monitor * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -214,7 +214,7 @@ BOOL timestamps_utc = FALSE; BOOL tls_certificate_verified = FALSE; uschar *tls_cipher = NULL; uschar *tls_peerdn = NULL; -#ifndef USE_GNUTLS +#ifdef SUPPORT_TLS uschar *tls_sni = NULL; #endif diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index baa4c8996..bd1d462bf 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -2,7 +2,7 @@ * Exim Monitor * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module contains code for scanning the main log, diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 7193640a5..d210a0717 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -2,7 +2,7 @@ * Exim Monitor * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -654,7 +654,9 @@ today.) */ if (log_file[0] != 0) { - (void)string_format(log_file_open, sizeof(log_file_open), "%s", CS log_file); + /* Do *not* use "%s" here, we need the %D datestamp in the log_file to + be expanded! */ + (void)string_format(log_file_open, sizeof(log_file_open), CS log_file); log_datestamping = string_datestamp_offset >= 0; LOG = fopen(CS log_file_open, "r"); diff --git a/src/exim_monitor/em_text.c b/src/exim_monitor/em_text.c index e39ec0298..3a3682959 100644 --- a/src/exim_monitor/em_text.c +++ b/src/exim_monitor/em_text.c @@ -2,7 +2,7 @@ * Exim Monitor * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/scripts/Configure-Makefile b/src/scripts/Configure-Makefile index 5ef0ff7f0..3e901e6a6 100755 --- a/src/scripts/Configure-Makefile +++ b/src/scripts/Configure-Makefile @@ -117,7 +117,7 @@ done >> $mft || exit 1 egrep "^[$st]*(AUTH|LOOKUP)_[A-Z0-9_]*[$st]*=[$st]*" $mft | \ sed "s/[$st]*=/='/" | \ sed "s/\$/'/" > $mftt -egrep "^[$st]*((USE_(OPENSSL|GNUTLS)_PC)|SUPPORT_TLS|USE_GNUTLS)[$st]*=[$st]*" $mft | \ +egrep "^[$st]*((USE_(OPENSSL|GNUTLS)_PC)|SUPPORT_TLS|USE_GNUTLS|PCRE_CONFIG)[$st]*=[$st]*" $mft | \ sed "s/[$st]*=/='/" | \ sed "s/\$/'/" >> $mftt if test -s $mftt @@ -173,6 +173,19 @@ then fi ;; + PCRE_CONFIG) + case $PCRE_CONFIG in + yes|YES|y|Y) + cflags=`pcre-config --cflags` + libs=`pcre-config --libs` + if [ ".$cflags" != "." ]; then + echo "INCLUDE += $cflags" + fi + echo "PCRE_LIBS=$libs" + ;; + esac + ;; + esac done echo "# End of pkg-config fixups" diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 166a25f88..62d248a3c 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -228,6 +228,7 @@ ln -s ../src/smtp_in.c smtp_in.c ln -s ../src/smtp_out.c smtp_out.c ln -s ../src/spool_in.c spool_in.c ln -s ../src/spool_out.c spool_out.c +ln -s ../src/std-crypto.c std-crypto.c ln -s ../src/store.c store.c ln -s ../src/string.c string.c ln -s ../src/tls.c tls.c diff --git a/src/src/EDITME b/src/src/EDITME index f247f44a9..95857c707 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -342,10 +342,13 @@ LOOKUP_DNSDB=yes # In either case you must specify the library link info here. If the # PCRE header files are not in the standard search path you must also # modify the INCLUDE path (above) -# The default setting of PCRE_LIBS should work on the vast majority of -# systems +# +# Use PCRE_CONFIG to query the pcre-config command (first found in $PATH) +# to find the include files and libraries, else use PCRE_LIBS and set INCLUDE +# too if needed. -PCRE_LIBS=-lpcre +PCRE_CONFIG=yes +# PCRE_LIBS=-lpcre #------------------------------------------------------------------------------ @@ -413,6 +416,11 @@ EXIM_MONITOR=eximon.bin # experimental-spec.txt. "Experimental" means that the way these features are # implemented may still change. Backward compatibility is not guaranteed. +# Uncomment the following line to add support for talking to dccifd. This +# defaults the socket path to /usr/local/dcc/var/dccifd. + +# EXPERIMENTAL_DCC=yes + # Uncomment the following lines to add SPF support. You need to have libspf2 # installed on your system (www.libspf2.org). Depending on where it is installed # you may have to edit the CFLAGS and LDFLAGS lines. @@ -439,6 +447,11 @@ EXIM_MONITOR=eximon.bin # CFLAGS += -I/opt/brightmail/bsdk-6.0/include # LDFLAGS += -lxml2_single -lbmiclient_single -L/opt/brightmail/bsdk-6.0/lib +# Uncomment the following line to add OCSP stapling support in TLS, if Exim +# was built using OpenSSL. + +# EXPERIMENTAL_OCSP=yes + ############################################################################### @@ -982,7 +995,7 @@ SYSTEM_ALIASES_FILE=/etc/aliases #------------------------------------------------------------------------------ # Uncomment this setting to include IPv6 support. -# HAVE_IPV6 +# HAVE_IPV6=yes ############################################################################### # THINGS YOU ALMOST NEVER NEED TO MENTION # diff --git a/src/src/acl.c b/src/src/acl.c index b93ac6965..5b5390d8d 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ diff --git a/src/src/auths/check_serv_cond.c b/src/src/auths/check_serv_cond.c index c10ff1be4..96c4b56c4 100644 --- a/src/src/auths/check_serv_cond.c +++ b/src/src/auths/check_serv_cond.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index e93d03845..f744a89ea 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index d454c7732..9b80f8d83 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* This code was originally contributed by Matthew Byng-Maddick */ @@ -205,7 +205,7 @@ uschar *debug = NULL; /* Stops compiler complaining */ sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}}; sasl_conn_t *conn; char *realm_expanded; -int rc, firsttime=1, clen, negotiated_ssf; +int rc, firsttime=1, clen, *negotiated_ssf_ptr=NULL, negotiated_ssf; unsigned int inlen, outlen; input=data; @@ -258,7 +258,7 @@ if( rc != SASL_OK ) if (tls_cipher) { - rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, &tls_bits); + rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_bits); if (rc != SASL_OK) { HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n", @@ -392,7 +392,7 @@ while(rc==SASL_CONTINUE) debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, auth_vars[0]); - rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf)); + rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)); if (rc != SASL_OK) { HDEBUG(D_auth) @@ -405,6 +405,7 @@ while(rc==SASL_CONTINUE) sasl_done(); return FAIL; } + negotiated_ssf = *negotiated_ssf_ptr; HDEBUG(D_auth) debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf); if (negotiated_ssf > 0) diff --git a/src/src/auths/cyrus_sasl.h b/src/src/auths/cyrus_sasl.h index 031e783ed..848105428 100644 --- a/src/src/auths/cyrus_sasl.h +++ b/src/src/auths/cyrus_sasl.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Copyright (c) A L Digital Ltd 2004 */ diff --git a/src/src/auths/get_no64_data.c b/src/src/auths/get_no64_data.c index ea5fd6f6d..d3ffe081e 100644 --- a/src/src/auths/get_no64_data.c +++ b/src/src/auths/get_no64_data.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index d69c2e4fe..1abd65781 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -202,6 +202,11 @@ auth_vars[0] = expand_nstring[1] = msgbuf; expand_nlength[1] = Ustrlen(msgbuf); expand_nmax = 1; +/* clean up globals which aren't referenced, but still shouldn't be left +pointing to stack memory */ +#define CLEANUP_RETURN(Code) do { auth_vars[0] = expand_nstring[1] = NULL; \ + expand_nlength[1] = expand_nmax = 0; return (Code); } while (0); + debug_print_string(ablock->server_debug_string); /* customized debug */ /* look up password */ @@ -213,13 +218,13 @@ if (clearpass == NULL) { DEBUG(D_auth) debug_printf("auth_spa_server(): forced failure while " "expanding spa_serverpassword\n"); - return FAIL; + CLEANUP_RETURN(FAIL); } else { DEBUG(D_auth) debug_printf("auth_spa_server(): error while expanding " "spa_serverpassword: %s\n", expand_string_message); - return DEFER; + CLEANUP_RETURN(DEFER); } } @@ -234,11 +239,14 @@ if (memcmp(ntRespData, ((unsigned char*)responseptr)+IVAL(&responseptr->ntResponse.offset,0), 24) == 0) /* success. we have a winner. */ + { + int rc = auth_check_serv_cond(ablock); + CLEANUP_RETURN(rc); + } /* Expand server_condition as an authorization check (PH) */ - return auth_check_serv_cond(ablock); -return FAIL; +CLEANUP_RETURN(FAIL); } diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index dfb449762..62114fc09 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -103,6 +103,7 @@ off_t test_off_t = 0; time_t test_time_t = 0; #if ! (__STDC_VERSION__ >= 199901L) size_t test_size_t = 0; +ssize_t test_ssize_t = 0; unsigned long test_ulong_t = 0L; #endif long test_long_t = 0; @@ -179,15 +180,22 @@ else fprintf(new, "#endif\n\n"); /* And for sizeof() results, size_t, which should with C99 be just %zu, deal -with C99 not being ubiquitous yet. Unfortunately. */ +with C99 not being ubiquitous yet. Unfortunately. Assume ssize_t is same +size as size_t on C99; if someone comes up with a version where it's not, fix +it then. */ #if __STDC_VERSION__ >= 199901L fprintf(new, "#define SIZE_T_FMT \"%%zu\"\n"); +fprintf(new, "#define SSIZE_T_FMT \"%%zd\"\n"); #else if (sizeof(test_size_t) > sizeof (test_ulong_t)) fprintf(new, "#define SIZE_T_FMT \"%%llu\"\n"); else fprintf(new, "#define SIZE_T_FMT \"%%lu\"\n"); +if (sizeof(test_ssize_t) > sizeof(test_long_t)) + fprintf(new, "#define SSIZE_T_FMT \"%%lld\"\n"); +else + fprintf(new, "#define SSIZE_T_FMT \"%%ld\"\n"); #endif /* Now search the makefile for certain settings */ @@ -332,6 +340,16 @@ while (fgets(buffer, sizeof(buffer), base) != NULL) while (*p == ' ' || *p == '\t') p++; + if (strncmp(p, "#ifdef ", 7) == 0 + || strncmp(p, "#ifndef ", 8) == 0 + || strncmp(p, "#if ", 4) == 0 + || strncmp(p, "#endif", 6) == 0 + ) + { + fputs(buffer, new); + continue; + } + if (strncmp(p, "#define ", 8) != 0) continue; p += 8; @@ -805,9 +823,50 @@ else if (isgroup) else if (strcmp(name, "TIMEZONE_DEFAULT") == 0|| strcmp(name, "TCP_WRAPPERS_DAEMON_NAME") == 0|| strcmp(name, "HEADERS_CHARSET") == 0|| - strcmp(name, "WHITELIST_D_MACROS") == 0) + strcmp(name, "WHITELIST_D_MACROS") == 0) fprintf(new, "\"%s\"\n", value); + /* GnuTLS constants; first is for debugging, others are tuning */ + + /* less than 0 is not-active; 0-9 are normal, API suggests higher + taken without problems */ + else if (strcmp(name, "EXIM_GNUTLS_LIBRARY_LOG_LEVEL") == 0) + { + long nv; + char *end; + nv = strtol(value, &end, 10); + if (end != value && *end == '\0' && nv >= -1 && nv <= 100) + { + fprintf(new, "%s\n", value); + } + else + { + printf("Value of %s should be -1..9\n", name); + return 1; + } + } + + /* how many bits Exim, as a client, demands must be in D-H */ + /* as of GnuTLS 2.12.x, we ask for "normal" for D-H PK; before that, we + specify the number of bits. We've stuck with the historical value, but + it can be overridden. */ + else if ((strcmp(name, "EXIM_CLIENT_DH_MIN_BITS") == 0) || + (strcmp(name, "EXIM_SERVER_DH_BITS_PRE2_12") == 0)) + { + long nv; + char *end; + nv = strtol(value, &end, 10); + if (end != value && *end == '\0' && nv >= 1000 && nv < 50000) + { + fprintf(new, "%s\n", value); + } + else + { + printf("Unreasonable value (%s) of \"%s\".\n", value, name); + return 1; + } + } + /* For others, quote any paths and don't quote anything else */ else diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index c082b9269..92a4cd348 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* The default settings for Exim configuration variables. A #define without @@ -49,6 +49,9 @@ it's a default value. */ #define EXIMDB_LOCK_TIMEOUT 60 #define EXIMDB_LOCKFILE_MODE 0640 #define EXIMDB_MODE 0640 +#define EXIM_CLIENT_DH_MIN_BITS +#define EXIM_GNUTLS_LIBRARY_LOG_LEVEL +#define EXIM_SERVER_DH_BITS_PRE2_12 #define EXIM_PERL /* Both uid and gid are triggered by this */ #define EXIM_UID @@ -158,10 +161,14 @@ it's a default value. */ #define WITH_OLD_CLAMAV_STREAM /* EXPERIMENTAL features */ -#define EXPERIMENTAL_SPF -#define EXPERIMENTAL_SRS #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DCC +#define EXPERIMENTAL_OCSP +#define EXPERIMENTAL_SPF +#define EXPERIMENTAL_SRS + +/* For developers */ +#define WANT_DEEPER_PRINTF_CHECKS /* Things that are not routinely changed but are nevertheless configurable just in case. */ @@ -171,4 +178,18 @@ just in case. */ #define ROOT_UID 0 #define ROOT_GID 0 +/* Sizes for integer arithmetic. Go for 64bit; can be overridden in OS/os.h-FOO */ +#ifndef int_eximarith_t + #define int_eximarith_t int64_t +#endif +#ifndef PR_EXIM_ARITH + #define PR_EXIM_ARITH "%" PRId64 /* C99 standard, printf %lld */ +#endif +#ifndef SC_EXIM_ARITH + #define SC_EXIM_ARITH "%" SCNi64 /* scanf incl. 0x prefix */ +#endif +#ifndef SC_EXIM_DEC + #define SC_EXIM_DEC "%" SCNd64 /* scanf decimal */ +#endif + /* End of config.h.defaults */ diff --git a/src/src/configure.default b/src/src/configure.default index a527b41a8..963ec1696 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -304,6 +304,13 @@ timeout_frozen_after = 7d # because of some popular, yet buggy, mail composition software. +# If you wish to be strictly RFC compliant, or if you know you'll be +# exchanging email with systems that are not 8-bit clean, then you may +# wish to disable advertising 8BITMIME. Uncomment this option to do so. + +# accept_8bitmime = false + + ###################################################################### # ACL CONFIGURATION # # Specifies access control lists for incoming SMTP mail # diff --git a/src/src/daemon.c b/src/src/daemon.c index 27b4cb265..9385a91f4 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with running Exim as a daemon */ @@ -913,12 +913,67 @@ struct passwd *pw; int *listen_sockets = NULL; int listen_socket_count = 0; ip_address_item *addresses = NULL; +time_t last_connection_time = (time_t)0; /* If any debugging options are set, turn on the D_pid bit so that all debugging lines get the pid added. */ DEBUG(D_any|D_v) debug_selector |= D_pid; +if (inetd_wait_mode) + { + int on = 1; + + listen_socket_count = 1; + listen_sockets = store_get(sizeof(int *)); + (void) close(3); + if (dup2(0, 3) == -1) + { + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "failed to dup inetd socket safely away: %s", strerror(errno)); + } + listen_sockets[0] = 3; + (void) close(0); + (void) close(1); + (void) close(2); + exim_nullstd(); + + if (debug_file == stderr) + { + /* need a call to log_write before call to open debug_file, so that + log.c:file_path has been initialised. This is unfortunate. */ + log_write(0, LOG_MAIN, "debugging Exim in inetd wait mode starting"); + + fclose(debug_file); + debug_file = NULL; + exim_nullstd(); /* re-open fd2 after we just closed it again */ + debug_logging_activate(US"-wait", NULL); + } + + DEBUG(D_any) debug_printf("running in inetd wait mode\n"); + + /* As per below, when creating sockets ourselves, we handle tcp_nodelay for + our own buffering; we assume though that inetd set the socket REUSEADDR. */ + + if (tcp_nodelay) setsockopt(3, IPPROTO_TCP, TCP_NODELAY, + (uschar *)(&on), sizeof(on)); + } + + +if (inetd_wait_mode || daemon_listen) + { + /* If any option requiring a load average to be available during the + reception of a message is set, call os_getloadavg() while we are root + for those OS for which this is necessary the first time it is called (in + order to perform an "open" on the kernel memory file). */ + + #ifdef LOAD_AVG_NEEDS_ROOT + if (queue_only_load >= 0 || smtp_load_reserve >= 0 || + (deliver_queue_load_max >= 0 && deliver_drop_privilege)) + (void)os_getloadavg(); + #endif + } + /* Do the preparation for setting up a listener on one or more interfaces, and possible on various ports. This is controlled by the combination of @@ -987,7 +1042,7 @@ The preparation code decodes options and sets up the relevant data. We do this first, so that we can return non-zero if there are any syntax errors, and also write to stderr. */ -if (daemon_listen) +if (daemon_listen && !inetd_wait_mode) { int *default_smtp_port; int sep; @@ -998,17 +1053,6 @@ if (daemon_listen) ip_address_item *ipa; ip_address_item **pipa; - /* If any option requiring a load average to be available during the - reception of a message is set, call os_getloadavg() while we are root - for those OS for which this is necessary the first time it is called (in - order to perform an "open" on the kernel memory file). */ - - #ifdef LOAD_AVG_NEEDS_ROOT - if (queue_only_load >= 0 || smtp_load_reserve >= 0 || - (deliver_queue_load_max >= 0 && deliver_drop_privilege)) - (void)os_getloadavg(); - #endif - /* If -oX was used, disable the writing of a pid file unless -oP was explicitly used to force it. Then scan the string given to -oX. Any items that contain neither a dot nor a colon are used to override daemon_smtp_port. @@ -1208,6 +1252,11 @@ if (daemon_listen) listen_socket_count++; listen_sockets = store_get(sizeof(int *) * listen_socket_count); + } /* daemon_listen but not inetd_wait_mode */ + +if (daemon_listen) + { + /* Do a sanity check on the max connects value just to save us from getting a huge amount of store. */ @@ -1233,7 +1282,8 @@ if (daemon_listen) /* The variable background_daemon is always false when debugging, but can also be forced false in order to keep a non-debugging daemon in the foreground. If background_daemon is true, close all open file descriptors that -we know about, but then re-open stdin, stdout, and stderr to /dev/null. +we know about, but then re-open stdin, stdout, and stderr to /dev/null. Also +do this for inetd_wait mode. This is protection against any called functions (in libraries, or in Perl, or whatever) that think they can write to stderr (or stdout). Before this @@ -1244,7 +1294,7 @@ Then disconnect from the controlling terminal, Most modern Unixes seem to have setsid() for getting rid of the controlling terminal. For any OS that doesn't, setsid() can be #defined as a no-op, or as something else. */ -if (background_daemon) +if (background_daemon || inetd_wait_mode) { log_close_all(); /* Just in case anything was logged earlier */ search_tidyup(); /* Just in case any were used in reading the config. */ @@ -1253,7 +1303,10 @@ if (background_daemon) (void)close(2); exim_nullstd(); /* Connect stdin/stdout/stderr to /dev/null */ log_stderr = NULL; /* So no attempt to copy paniclog output */ + } +if (background_daemon) + { /* If the parent process of this one has pid == 1, we are re-initializing the daemon as the result of a SIGHUP. In this case, there is no need to do anything, because the controlling terminal has long gone. Otherwise, fork, in @@ -1273,7 +1326,7 @@ if (background_daemon) /* We are now in the disconnected, daemon process (unless debugging). Set up the listening sockets if required. */ -if (daemon_listen) +if (daemon_listen && !inetd_wait_mode) { int sk; int on = 1; @@ -1513,7 +1566,25 @@ sigalrm_seen = (queue_interval > 0); /* Log the start up of a daemon - at least one of listening or queue running must be set up. */ -if (daemon_listen) +if (inetd_wait_mode) + { + uschar *p = big_buffer; + + if (inetd_wait_timeout >= 0) + sprintf(CS p, "terminating after %d seconds", inetd_wait_timeout); + else + sprintf(CS p, "with no wait timeout"); + + log_write(0, LOG_MAIN, + "exim %s daemon started: pid=%d, launched with listening socket, %s", + version_string, getpid(), big_buffer); + set_process_info("daemon: pre-listening socket"); + + /* set up the timeout logic */ + sigalrm_seen = 1; + } + +else if (daemon_listen) { int i, j; int smtp_ports = 0; @@ -1631,122 +1702,166 @@ for (;;) pid_t pid; /* This code is placed first in the loop, so that it gets obeyed at the - start, before the first wait. This causes the first queue-runner to be - started immediately. */ + start, before the first wait, for the queue-runner case, so that the first + one can be started immediately. + + The other option is that we have an inetd wait timeout specified to -bw. */ if (sigalrm_seen) { - DEBUG(D_any) debug_printf("SIGALRM received\n"); + if (inetd_wait_timeout > 0) + { + time_t resignal_interval = inetd_wait_timeout; + + if (last_connection_time == (time_t)0) + { + DEBUG(D_any) + debug_printf("inetd wait timeout expired, but still not seen first message, ignoring\n"); + } + else + { + time_t now = time(NULL); + if (now == (time_t)-1) + { + DEBUG(D_any) debug_printf("failed to get time: %s\n", strerror(errno)); + } + else + { + if ((now - last_connection_time) >= inetd_wait_timeout) + { + DEBUG(D_any) + debug_printf("inetd wait timeout %d expired, ending daemon\n", + inetd_wait_timeout); + log_write(0, LOG_MAIN, "exim %s daemon terminating, inetd wait timeout reached.\n", + version_string); + exit(EXIT_SUCCESS); + } + else + { + resignal_interval -= (now - last_connection_time); + } + } + } - /* Do a full queue run in a child process, if required, unless we already - have enough queue runners on the go. If we are not running as root, a - re-exec is required. */ + sigalrm_seen = FALSE; + alarm(resignal_interval); + } - if (queue_interval > 0 && - (queue_run_max <= 0 || queue_run_count < queue_run_max)) + else { - if ((pid = fork()) == 0) - { - int sk; + DEBUG(D_any) debug_printf("SIGALRM received\n"); - DEBUG(D_any) debug_printf("Starting queue-runner: pid %d\n", - (int)getpid()); + /* Do a full queue run in a child process, if required, unless we already + have enough queue runners on the go. If we are not running as root, a + re-exec is required. */ - /* Disable debugging if it's required only for the daemon process. We - leave the above message, because it ties up with the "child ended" - debugging messages. */ + if (queue_interval > 0 && + (queue_run_max <= 0 || queue_run_count < queue_run_max)) + { + if ((pid = fork()) == 0) + { + int sk; - if (debug_daemon) debug_selector = 0; + DEBUG(D_any) debug_printf("Starting queue-runner: pid %d\n", + (int)getpid()); - /* Close any open listening sockets in the child */ + /* Disable debugging if it's required only for the daemon process. We + leave the above message, because it ties up with the "child ended" + debugging messages. */ - for (sk = 0; sk < listen_socket_count; sk++) - (void)close(listen_sockets[sk]); + if (debug_daemon) debug_selector = 0; - /* Reset SIGHUP and SIGCHLD in the child in both cases. */ + /* Close any open listening sockets in the child */ - signal(SIGHUP, SIG_DFL); - signal(SIGCHLD, SIG_DFL); + for (sk = 0; sk < listen_socket_count; sk++) + (void)close(listen_sockets[sk]); - /* Re-exec if privilege has been given up, unless deliver_drop_ - privilege is set. Reset SIGALRM before exec(). */ + /* Reset SIGHUP and SIGCHLD in the child in both cases. */ - if (geteuid() != root_uid && !deliver_drop_privilege) - { - uschar opt[8]; - uschar *p = opt; - uschar *extra[5]; - int extracount = 1; + signal(SIGHUP, SIG_DFL); + signal(SIGCHLD, SIG_DFL); - signal(SIGALRM, SIG_DFL); - *p++ = '-'; - *p++ = 'q'; - if (queue_2stage) *p++ = 'q'; - if (queue_run_first_delivery) *p++ = 'i'; - if (queue_run_force) *p++ = 'f'; - if (deliver_force_thaw) *p++ = 'f'; - if (queue_run_local) *p++ = 'l'; - *p = 0; - extra[0] = opt; - - /* If -R or -S were on the original command line, ensure they get - passed on. */ - - if (deliver_selectstring != NULL) - { - extra[extracount++] = deliver_selectstring_regex? US"-Rr" : US"-R"; - extra[extracount++] = deliver_selectstring; - } + /* Re-exec if privilege has been given up, unless deliver_drop_ + privilege is set. Reset SIGALRM before exec(). */ - if (deliver_selectstring_sender != NULL) + if (geteuid() != root_uid && !deliver_drop_privilege) { - extra[extracount++] = deliver_selectstring_sender_regex? - US"-Sr" : US"-S"; - extra[extracount++] = deliver_selectstring_sender; + uschar opt[8]; + uschar *p = opt; + uschar *extra[5]; + int extracount = 1; + + signal(SIGALRM, SIG_DFL); + *p++ = '-'; + *p++ = 'q'; + if (queue_2stage) *p++ = 'q'; + if (queue_run_first_delivery) *p++ = 'i'; + if (queue_run_force) *p++ = 'f'; + if (deliver_force_thaw) *p++ = 'f'; + if (queue_run_local) *p++ = 'l'; + *p = 0; + extra[0] = opt; + + /* If -R or -S were on the original command line, ensure they get + passed on. */ + + if (deliver_selectstring != NULL) + { + extra[extracount++] = deliver_selectstring_regex? US"-Rr" : US"-R"; + extra[extracount++] = deliver_selectstring; + } + + if (deliver_selectstring_sender != NULL) + { + extra[extracount++] = deliver_selectstring_sender_regex? + US"-Sr" : US"-S"; + extra[extracount++] = deliver_selectstring_sender; + } + + /* Overlay this process with a new execution. */ + + (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, TRUE, extracount, + extra[0], extra[1], extra[2], extra[3], extra[4]); + + /* Control never returns here. */ } - /* Overlay this process with a new execution. */ - - (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, TRUE, extracount, - extra[0], extra[1], extra[2], extra[3], extra[4]); + /* No need to re-exec; SIGALRM remains set to the default handler */ - /* Control never returns here. */ + queue_run(NULL, NULL, FALSE); + _exit(EXIT_SUCCESS); } - /* No need to re-exec; SIGALRM remains set to the default handler */ - - queue_run(NULL, NULL, FALSE); - _exit(EXIT_SUCCESS); - } - - if (pid < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner " - "process failed: %s", strerror(errno)); - log_close_all(); - } - else - { - int i; - for (i = 0; i < queue_run_max; ++i) + if (pid < 0) { - if (queue_pid_slots[i] <= 0) + log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner " + "process failed: %s", strerror(errno)); + log_close_all(); + } + else + { + int i; + for (i = 0; i < queue_run_max; ++i) { - queue_pid_slots[i] = pid; - queue_run_count++; - break; + if (queue_pid_slots[i] <= 0) + { + queue_pid_slots[i] = pid; + queue_run_count++; + break; + } } + DEBUG(D_any) debug_printf("%d queue-runner process%s running\n", + queue_run_count, (queue_run_count == 1)? "" : "es"); } - DEBUG(D_any) debug_printf("%d queue-runner process%s running\n", - queue_run_count, (queue_run_count == 1)? "" : "es"); } - } - /* Reset the alarm clock */ + /* Reset the alarm clock */ - sigalrm_seen = FALSE; - alarm(queue_interval); - } + sigalrm_seen = FALSE; + alarm(queue_interval); + } + + } /* sigalrm_seen */ /* Sleep till a connection happens if listening, and handle the connection if @@ -1886,8 +2001,12 @@ for (;;) /* If select/accept succeeded, deal with the connection. */ if (accept_socket >= 0) + { + if (inetd_wait_timeout) + last_connection_time = time(NULL); handle_smtp_call(listen_sockets, listen_socket_count, accept_socket, (struct sockaddr *)&accepted); + } } } diff --git a/src/src/dcc.c b/src/src/dcc.c index 2ad451d0b..20bd75afa 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Wolfgang Breyha 2005-2009 +/* Copyright (c) Wolfgang Breyha 2005-2012 * Vienna University Computer Center * wbreyha@gmx.net * See the file NOTICE for conditions of use and distribution. @@ -19,7 +19,6 @@ #include "unistd.h" uschar dcc_header_str[256]; -uschar dcc_result_str[256]; int dcc_ok = 0; int dcc_rc = 0; @@ -68,7 +67,6 @@ int dcc_process(uschar **listptr) { uschar rcpt[128], from[128]; uschar sendbuf[4096]; uschar recvbuf[4096]; - uschar xhdr[256]; uschar dcc_return_text[1024]; uschar mbox_path[1024]; uschar message_subdir[2]; @@ -173,7 +171,7 @@ int dcc_process(uschar **listptr) { retval = DEFER; bzero(sendbuf,sizeof(sendbuf)); - bzero(xhdr,sizeof(xhdr)); + bzero(dcc_header_str,sizeof(dcc_header_str)); bzero(rcpt,sizeof(rcpt)); bzero(from,sizeof(from)); @@ -302,7 +300,7 @@ int dcc_process(uschar **listptr) { } } - /* a blank line separates header from body */ + /* a blank line seperates header from body */ Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); flushbuffer(sockfd, sendbuf); DEBUG(D_acl) @@ -353,7 +351,7 @@ int dcc_process(uschar **listptr) { line = 1; /* we start at the first line of the output */ j = 0; /* will be used as index for the recipients list */ - k = 0; /* initializing the index of the X-DCC header: xhdr[k] */ + k = 0; /* initializing the index of the X-DCC header: dcc_header_str[k] */ /* Let's read from the socket until there's nothing left to read */ bzero(recvbuf, sizeof(recvbuf)); @@ -441,16 +439,16 @@ int dcc_process(uschar **listptr) { } else if(line == 2) { /* On the second line we get a list of - * answer for each recipient. We don't care about - * it because we're in an acl and so just take the + * answers for each recipient. We don't care about + * it because we're in an acl and take the * global result. */ } else if(line > 2) { - /* The third and following lines is the X-DCC header, - * so we store it in xhdr. */ - /* check if we don't get more than what we can handle */ - if(k < sizeof(xhdr)) { /* xhdr has a length of 120 */ - xhdr[k] = recvbuf[i]; + /* The third and following lines are the X-DCC header, + * so we store it in dcc_header_str. */ + /* check if we don't get more than we can handle */ + if(k < sizeof(dcc_header_str)) { + dcc_header_str[k] = recvbuf[i]; k++; } else { @@ -470,26 +468,26 @@ int dcc_process(uschar **listptr) { } /* We have read everything from the socket */ - /* We need the terminate the X-DCC header with a '\n' character. This needs to be k-1 - * for xhdr[k] contains '\0'. */ - xhdr[k-1] = '\n'; + /* We need to terminate the X-DCC header with a '\n' character. This needs to be k-1 + * since dcc_header_str[k] contains '\0'. */ + dcc_header_str[k-1] = '\n'; /* Now let's sum up what we've got. */ DEBUG(D_acl) - debug_printf("\n--------------------------\nOverall result = %d\nX-DCC header: %sReturn message: %s\ndcc_result: %s\n", retval, xhdr, dcc_return_text, dcc_result); + debug_printf("\n--------------------------\nOverall result = %d\nX-DCC header: %sReturn message: %s\ndcc_result: %s\n", retval, dcc_header_str, dcc_return_text, dcc_result); /* We only add the X-DCC header if it starts with X-DCC */ - if(!(Ustrncmp(xhdr, "X-DCC", 5))){ - dcc_header = xhdr; + if(!(Ustrncmp(dcc_header_str, "X-DCC", 5))){ + dcc_header = dcc_header_str; if(dcc_direct_add_header) { - header_add(' ' , "%s", xhdr); + header_add(' ' , "%s", dcc_header_str); /* since the MIME ACL already writes the .eml file to disk without DCC Header we've to erase it */ unspool_mbox(); } } else { DEBUG(D_acl) - debug_printf("Wrong format of the X-DCC header: %s\n", xhdr); + debug_printf("Wrong format of the X-DCC header: %s\n", dcc_header_str); } /* check if we should add additional headers passed in acl_m_dcc_add_header */ diff --git a/src/src/deliver.c b/src/src/deliver.c index 10b63397e..d4ea2d868 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1213,7 +1213,7 @@ if (format != NULL) va_start(ap, format); if (!string_vformat(buffer, sizeof(buffer), CS format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "common_error expansion was longer than %d", sizeof(buffer)); + "common_error expansion was longer than " SIZE_T_FMT, sizeof(buffer)); va_end(ap); addr->message = string_copy(buffer); } diff --git a/src/src/dns.c b/src/src/dns.c index c903d0ba9..ae76e9e3f 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -415,6 +415,7 @@ switch(t) case T_AAAA: return US"AAAA"; case T_A6: return US"A6"; case T_TXT: return US"TXT"; + case T_SPF: return US"SPF"; case T_PTR: return US"PTR"; case T_SOA: return US"SOA"; case T_SRV: return US"SRV"; diff --git a/src/src/drtables.c b/src/src/drtables.c index 06f7e30ed..c1332ed0b 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/src/exim.c b/src/src/exim.c index 90ecd0629..76355afcc 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -976,20 +976,21 @@ DEBUG(D_any) do { } } + /* PCRE_PRERELEASE is either defined and empty or a bare sequence of + characters; unless it's an ancient version of PCRE in which case it + is not defined. */ +#ifndef PCRE_PRERELEASE +#define PCRE_PRERELEASE +#endif +#define QUOTE(X) #X +#define EXPAND_AND_QUOTE(X) QUOTE(X) fprintf(f, "Library version: PCRE: Compile: %d.%d%s\n" " Runtime: %s\n", PCRE_MAJOR, PCRE_MINOR, - /* PRE_PRERELEASE is either defined and empty or a string. - * unless its an ancient version of PCRE in which case it - * is not defined */ -#ifdef PCRE_PRERELEASE -# define STRINGIFY(x) #x - STRINGIFY(PCRE_PRERELEASE) "", -# undef STRINGIFY -#else - "", -#endif + EXPAND_AND_QUOTE(PCRE_PRERELEASE) "", pcre_version()); +#undef QUOTE +#undef EXPAND_AND_QUOTE init_lookup_list(); for (i = 0; i < lookup_list_count; i++) @@ -2060,6 +2061,24 @@ for (i = 1; i < argc; i++) show_whats_supported(stdout); } + /* -bw: inetd wait mode, accept a listening socket as stdin */ + + else if (*argrest == 'w') + { + inetd_wait_mode = TRUE; + background_daemon = FALSE; + daemon_listen = TRUE; + if (*(++argrest) != '\0') + { + inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE); + if (inetd_wait_timeout <= 0) + { + fprintf(stderr, "exim: bad time value %s: abandoned\n", argv[i]); + exit(EXIT_FAILURE); + } + } + } + else badarg = TRUE; break; @@ -3222,6 +3241,9 @@ if (( daemon_listen && queue_interval == 0 ) || ( + inetd_wait_mode && queue_interval >= 0 + ) || + ( list_options && (checking || smtp_input || extract_recipients || filter_test != FTEST_NONE || bi_option) @@ -4406,7 +4428,7 @@ returns. We leave this till here so that the originator_ fields are available for incoming messages via the daemon. The daemon cannot be run in mua_wrapper mode. */ -if (daemon_listen || queue_interval > 0) +if (daemon_listen || inetd_wait_mode || queue_interval > 0) { if (mua_wrapper) { diff --git a/src/src/exim.h b/src/src/exim.h index e6e72facc..32871660d 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -20,6 +20,13 @@ This call dates back at least as far as SUSv2. */ #define HAVE_SRANDOM +/* This is primarily for the Gnu C library; we define it before os.h so that +os.h has a chance to hurriedly undef it, Just In Case. We need C99 for some +64-bit math support, and defining _ISOC99_SOURCE breaks <resolv.h> and friends. +*/ + +#define _GNU_SOURCE 1 + /* First of all include the os-specific header, which might set things that are needed by any of the other headers, including system headers. */ @@ -48,7 +55,7 @@ making unique names. */ #define LOCALHOST_MAX 10 #endif -/* If not overriden by os.h, dynamic libraries have filenames ending .so */ +/* If not overridden by os.h, dynamic libraries have filenames ending .so */ #ifndef DYNLIB_FN_EXT # define DYNLIB_FN_EXT "so" #endif @@ -77,6 +84,10 @@ making unique names. */ #include <limits.h> #endif +/* C99 integer types, figure out how to undo this if needed for older systems */ + +#include <inttypes.h> + /* Just in case some aged system doesn't define them... */ #ifndef INT_MAX @@ -295,6 +306,12 @@ header files. I don't suppose they have T_SRV either. */ #define T_SRV 33 #endif +/* Many systems do not have T_SPF. */ + +#ifndef T_SPF +#define T_SPF 99 +#endif + /* It seems that some versions of arpa/nameser.h don't define *any* of the T_xxx macros, which seem to be non-standard nowadays. Just to be on the safe side, put in definitions for all the ones that Exim uses. */ diff --git a/src/src/eximon.src b/src/src/eximon.src index d8b3b3182..fac24208c 100644 --- a/src/src/eximon.src +++ b/src/src/eximon.src @@ -4,7 +4,7 @@ # The build process concatenates on the front of this various settings from # os-specific files and from the user's configuration file. -# Copyright (c) 2004 University of Cambridge. +# Copyright (c) 2004 - 2012 University of Cambridge. # See the file NOTICE for conditions of use and distribution. # Except when they appear in comments, the following placeholders in this @@ -19,6 +19,14 @@ # PROCESSED_FLAG +# See if caller wants to invoke gdb + +use_gdb='' + +case ${1:-foo} in + gdb*) use_gdb="$1"; shift ;; +esac + # Save arguments (can be the usual X parameters) cmd_args="$@" @@ -181,8 +189,13 @@ export EXIM_PATH LD_LIBRARY_PATH \ # Exec to the program we really want to run, thereby continuing in # just the one process, and let it run in parallel with whatever -# called this script. +# called this script (unless gdb was requested in original $1). -exec ${EXIMON_BINARY} $cmd_args & +if [ "${use_gdb:-}" = "" ] ; then + exec "${EXIMON_BINARY}" $cmd_args & +else + exec "$use_gdb" "${EXIMON_BINARY}" $cmd_args + # not backgrounded +fi # End diff --git a/src/src/expand.c b/src/src/expand.c index 22f7d9a66..84167b688 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -363,6 +363,7 @@ enum { /* local_scan()) */ vtype_todbsdin, /* value not used; generate BSD inbox tod */ vtype_tode, /* value not used; generate tod in epoch format */ + vtype_todel, /* value not used; generate tod in epoch/usec format */ vtype_todf, /* value not used; generate full tod */ vtype_todl, /* value not used; generate log tod */ vtype_todlf, /* value not used; generate log file datestamp tod */ @@ -615,11 +616,12 @@ static var_entry var_table[] = { { "tls_certificate_verified", vtype_int, &tls_certificate_verified }, { "tls_cipher", vtype_stringptr, &tls_cipher }, { "tls_peerdn", vtype_stringptr, &tls_peerdn }, -#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +#ifdef SUPPORT_TLS { "tls_sni", vtype_stringptr, &tls_sni }, #endif { "tod_bsdinbox", vtype_todbsdin, NULL }, { "tod_epoch", vtype_tode, NULL }, + { "tod_epoch_l", vtype_todel, NULL }, { "tod_full", vtype_todf, NULL }, { "tod_log", vtype_todl, NULL }, { "tod_logfile", vtype_todlf, NULL }, @@ -776,6 +778,7 @@ return rc; + /************************************************* * Pseudo-random number generation * *************************************************/ @@ -788,19 +791,23 @@ weirdness they'll twist this into. The result should ideally handle fork(). However, if we're stuck unable to provide this, then we'll fall back to appallingly bad randomness. -If SUPPORT_TLS is defined and OpenSSL is used, then this will not be used. -The GNUTLS randomness functions found do not seem amenable to extracting -random numbers outside of a TLS context. Any volunteers? +If SUPPORT_TLS is defined then this will not be used except as an emergency +fallback. Arguments: max range maximum Returns a random number in range [0, max-1] */ -#if !defined(SUPPORT_TLS) || defined(USE_GNUTLS) +#ifdef SUPPORT_TLS +# define vaguely_random_number vaguely_random_number_fallback +#endif int -pseudo_random_number(int max) +vaguely_random_number(int max) { +#ifdef SUPPORT_TLS +# undef vaguely_random_number +#endif static pid_t pid = 0; pid_t p2; #if defined(HAVE_SRANDOM) && !defined(HAVE_SRANDOMDEV) @@ -843,7 +850,8 @@ pseudo_random_number(int max) #endif } -#endif + + /************************************************* * Pick out a name from a string * @@ -1519,8 +1527,8 @@ while (last > first) domain = Ustrrchr(s, '@'); if (domain == NULL) return s; if (domain - s > sizeof(var_buffer) - 1) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than %d in " - "string expansion", sizeof(var_buffer)); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT + " in string expansion", sizeof(var_buffer)); Ustrncpy(var_buffer, s, domain - s); var_buffer[domain - s] = 0; return var_buffer; @@ -1583,6 +1591,9 @@ while (last > first) case vtype_tode: /* Unix epoch time of day */ return tod_stamp(tod_epoch); + case vtype_todel: /* Unix epoch/usec time of day */ + return tod_stamp(tod_epoch_l); + case vtype_todf: /* Full time of day */ return tod_stamp(tod_full); @@ -1781,7 +1792,7 @@ BOOL tempcond, combined_cond; BOOL *subcondptr; BOOL sub2_honour_dollar = TRUE; int i, rc, cond_type, roffset; -int num[2]; +int_eximarith_t num[2]; struct stat statbuf; uschar name[256]; uschar *sub[4]; @@ -3069,14 +3080,14 @@ Returns: on success: the value of the expression, with *error still NULL on failure: an undefined value, with *error = a message */ -static int eval_op_or(uschar **, BOOL, uschar **); +static int_eximarith_t eval_op_or(uschar **, BOOL, uschar **); -static int +static int_eximarith_t eval_expr(uschar **sptr, BOOL decimal, uschar **error, BOOL endket) { uschar *s = *sptr; -int x = eval_op_or(&s, decimal, error); +int_eximarith_t x = eval_op_or(&s, decimal, error); if (*error == NULL) { if (endket) @@ -3093,21 +3104,26 @@ return x; } -static int +static int_eximarith_t eval_number(uschar **sptr, BOOL decimal, uschar **error) { register int c; -int n; +int_eximarith_t n; uschar *s = *sptr; while (isspace(*s)) s++; c = *s; if (isdigit(c)) { int count; - (void)sscanf(CS s, (decimal? "%d%n" : "%i%n"), &n, &count); + (void)sscanf(CS s, (decimal? SC_EXIM_DEC "%n" : SC_EXIM_ARITH "%n"), &n, &count); s += count; - if (tolower(*s) == 'k') { n *= 1024; s++; } - else if (tolower(*s) == 'm') { n *= 1024*1024; s++; } + switch (tolower(*s)) + { + default: break; + case 'k': n *= 1024; s++; break; + case 'm': n *= 1024*1024; s++; break; + case 'g': n *= 1024*1024*1024; s++; break; + } while (isspace (*s)) s++; } else if (c == '(') @@ -3125,10 +3141,11 @@ return n; } -static int eval_op_unary(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_unary(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x; +int_eximarith_t x; while (isspace(*s)) s++; if (*s == '+' || *s == '-' || *s == '~') { @@ -3146,16 +3163,17 @@ return x; } -static int eval_op_mult(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_mult(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x = eval_op_unary(&s, decimal, error); +int_eximarith_t x = eval_op_unary(&s, decimal, error); if (*error == NULL) { while (*s == '*' || *s == '/' || *s == '%') { int op = *s++; - int y = eval_op_unary(&s, decimal, error); + int_eximarith_t y = eval_op_unary(&s, decimal, error); if (*error != NULL) break; /* SIGFPE both on div/mod by zero and on INT_MIN / -1, which would give * a value of INT_MAX+1. Note that INT_MIN * -1 gives INT_MIN for me, which @@ -3175,12 +3193,12 @@ if (*error == NULL) * can just let the other invalid results occur otherwise, as they have * until now. For this one case, we can coerce. */ - if (y == -1 && x == INT_MIN && op != '*') + if (y == -1 && x == LLONG_MIN && op != '*') { DEBUG(D_expand) - debug_printf("Integer exception dodging: %d%c-1 coerced to %d\n", - INT_MIN, op, INT_MAX); - x = INT_MAX; + debug_printf("Integer exception dodging: " PR_EXIM_ARITH "%c-1 coerced to " PR_EXIM_ARITH "\n", + LLONG_MIN, op, LLONG_MAX); + x = LLONG_MAX; continue; } if (op == '*') @@ -3205,16 +3223,17 @@ return x; } -static int eval_op_sum(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_sum(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x = eval_op_mult(&s, decimal, error); +int_eximarith_t x = eval_op_mult(&s, decimal, error); if (*error == NULL) { while (*s == '+' || *s == '-') { int op = *s++; - int y = eval_op_mult(&s, decimal, error); + int_eximarith_t y = eval_op_mult(&s, decimal, error); if (*error != NULL) break; if (op == '+') x += y; else x -= y; } @@ -3224,15 +3243,16 @@ return x; } -static int eval_op_shift(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_shift(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x = eval_op_sum(&s, decimal, error); +int_eximarith_t x = eval_op_sum(&s, decimal, error); if (*error == NULL) { while ((*s == '<' || *s == '>') && s[1] == s[0]) { - int y; + int_eximarith_t y; int op = *s++; s++; y = eval_op_sum(&s, decimal, error); @@ -3245,15 +3265,16 @@ return x; } -static int eval_op_and(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_and(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x = eval_op_shift(&s, decimal, error); +int_eximarith_t x = eval_op_shift(&s, decimal, error); if (*error == NULL) { while (*s == '&') { - int y; + int_eximarith_t y; s++; y = eval_op_shift(&s, decimal, error); if (*error != NULL) break; @@ -3265,15 +3286,16 @@ return x; } -static int eval_op_xor(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_xor(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x = eval_op_and(&s, decimal, error); +int_eximarith_t x = eval_op_and(&s, decimal, error); if (*error == NULL) { while (*s == '^') { - int y; + int_eximarith_t y; s++; y = eval_op_and(&s, decimal, error); if (*error != NULL) break; @@ -3285,15 +3307,16 @@ return x; } -static int eval_op_or(uschar **sptr, BOOL decimal, uschar **error) +static int_eximarith_t +eval_op_or(uschar **sptr, BOOL decimal, uschar **error) { uschar *s = *sptr; -int x = eval_op_xor(&s, decimal, error); +int_eximarith_t x = eval_op_xor(&s, decimal, error); if (*error == NULL) { while (*s == '|') { - int y; + int_eximarith_t y; s++; y = eval_op_xor(&s, decimal, error); if (*error != NULL) break; @@ -5693,7 +5716,7 @@ while (*s != 0) { uschar *save_sub = sub; uschar *error = NULL; - int n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE); + int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE); if (error != NULL) { expand_string_message = string_sprintf("error in expression " @@ -5701,7 +5724,7 @@ while (*s != 0) save_sub); goto EXPAND_FAILED; } - sprintf(CS var_buffer, "%d", n); + sprintf(CS var_buffer, PR_EXIM_ARITH, n); yield = string_cat(yield, &size, &ptr, var_buffer, Ustrlen(var_buffer)); continue; } @@ -5902,17 +5925,17 @@ while (*s != 0) continue; } - /* pseudo-random number less than N */ + /* vaguely random number less than N */ case EOP_RANDINT: { - int max; + int_eximarith_t max; uschar *s; max = expand_string_integer(sub, TRUE); if (expand_string_message != NULL) goto EXPAND_FAILED; - s = string_sprintf("%d", pseudo_random_number(max)); + s = string_sprintf("%d", vaguely_random_number((int)max)); yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); continue; } @@ -6103,10 +6126,10 @@ Returns: the integer value, or expand_string_message is set NULL for an OK integer */ -int +int_eximarith_t expand_string_integer(uschar *string, BOOL isplus) { -long int value; +int_eximarith_t value; uschar *s = expand_string(string); uschar *msg = US"invalid integer \"%s\""; uschar *endptr; @@ -6138,7 +6161,7 @@ if (isspace(*s)) } } -value = strtol(CS s, CSS &endptr, 10); +value = strtoll(CS s, CSS &endptr, 10); if (endptr == s) { @@ -6150,24 +6173,18 @@ else if (value < 0 && isplus) } else { - /* Ensure we can cast this down to an int */ - if (value > INT_MAX || value < INT_MIN) errno = ERANGE; - - if (errno != ERANGE) + if (tolower(*endptr) == 'k') { - if (tolower(*endptr) == 'k') - { - if (value > INT_MAX/1024 || value < INT_MIN/1024) errno = ERANGE; - else value *= 1024; - endptr++; - } + if (value > LLONG_MAX/1024 || value < LLONG_MIN/1024) errno = ERANGE; + else value *= 1024; + endptr++; + } else if (tolower(*endptr) == 'm') - { - if (value > INT_MAX/(1024*1024) || value < INT_MIN/(1024*1024)) - errno = ERANGE; - else value *= 1024*1024; - endptr++; - } + { + if (value > LLONG_MAX/(1024*1024) || value < LLONG_MIN/(1024*1024)) + errno = ERANGE; + else value *= 1024*1024; + endptr++; } if (errno == ERANGE) msg = US"absolute value of integer \"%s\" is too large (overflow)"; diff --git a/src/src/functions.h b/src/src/functions.h index 220235235..29e7db2bd 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -21,18 +21,23 @@ extern uschar *init_perl(uschar *); #ifdef SUPPORT_TLS +extern const char * + std_dh_prime_default(void); +extern const char * + std_dh_prime_named(const uschar *); extern int tls_client_start(int, host_item *, address_item *, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *, - uschar *, uschar *, uschar *, int); + int); extern void tls_close(BOOL); extern int tls_feof(void); extern int tls_ferror(void); extern int tls_getc(void); extern int tls_read(uschar *, size_t); -extern int tls_server_start(uschar *, uschar *, uschar *, uschar *); +extern int tls_server_start(const uschar *); extern BOOL tls_smtp_buffered(void); extern int tls_ungetc(int); extern int tls_write(const uschar *, size_t); +extern uschar *tls_validate_require_cipher(void); extern void tls_version_report(FILE *); #ifndef USE_GNUTLS extern BOOL tls_openssl_options_parse(uschar *, long *); @@ -114,7 +119,7 @@ extern void exim_wait_tick(struct timeval *, int); extern BOOL expand_check_condition(uschar *, uschar *, uschar *); extern uschar *expand_string(uschar *); extern uschar *expand_string_copy(uschar *); -extern int expand_string_integer(uschar *, BOOL); +extern int_eximarith_t expand_string_integer(uschar *, BOOL); extern int filter_interpret(uschar *, int, address_item **, uschar **); extern BOOL filter_personal(string_item *, BOOL); @@ -199,7 +204,10 @@ extern uschar *parse_fix_phrase(uschar *, int, uschar *, int); extern uschar *parse_message_id(uschar *, uschar **, uschar **); extern uschar *parse_quote_2047(uschar *, int, uschar *, uschar *, int, BOOL); extern uschar *parse_date_time(uschar *str, time_t *t); -extern int pseudo_random_number(int); +extern int vaguely_random_number(int); +#ifdef SUPPORT_TLS +extern int vaguely_random_number_fallback(int); +#endif extern BOOL queue_action(uschar *, int, uschar **, int, int); extern void queue_check_only(void); @@ -322,7 +330,7 @@ extern uschar *string_copy_malloc(uschar *); extern uschar *string_copylc(uschar *); extern uschar *string_copynlc(uschar *, int); extern uschar *string_dequote(uschar **); -extern BOOL string_format(uschar *, int, const char *, ...) PRINTF_FUNCTION(3,4); +extern BOOL string_format(uschar *, int, const char *, ...) ALMOST_PRINTF(3,4); extern uschar *string_format_size(int, uschar *); extern int string_interpret_escape(uschar **); extern int string_is_ip_address(uschar *, int *); @@ -379,4 +387,6 @@ extern BOOL verify_sender(int *, uschar **); extern BOOL verify_sender_preliminary(int *, uschar **); extern void version_init(void); +extern ssize_t write_to_fd_buf(int, const uschar *, size_t); + /* End of functions.h */ diff --git a/src/src/globals.c b/src/src/globals.c index f11c7c2db..f29fb3c49 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* All the global variables are defined together in this one module, so @@ -111,14 +111,19 @@ const pcre *regex_STARTTLS = NULL; uschar *tls_advertise_hosts = NULL; /* This is deliberate */ uschar *tls_certificate = NULL; uschar *tls_crl = NULL; +/* This default matches NSS DH_MAX_P_BITS value at current time (2012), because +that's the interop problem which has been observed: GnuTLS suggesting a higher +bit-count as "NORMAL" (2432) and Thunderbird dropping connection. */ +int tls_dh_max_bits = 2236; uschar *tls_dhparam = NULL; +#if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS) +uschar *tls_ocsp_file = NULL; +#endif BOOL tls_offered = FALSE; uschar *tls_privatekey = NULL; BOOL tls_remember_esmtp = FALSE; uschar *tls_require_ciphers = NULL; -#ifndef USE_GNUTLS uschar *tls_sni = NULL; -#endif uschar *tls_try_verify_hosts = NULL; uschar *tls_verify_certificates= NULL; uschar *tls_verify_hosts = NULL; @@ -247,7 +252,7 @@ uschar *acl_wherecodes[] = { US"550", /* RCPT */ BOOL active_local_from_check = FALSE; BOOL active_local_sender_retain = FALSE; -BOOL accept_8bitmime = FALSE; +BOOL accept_8bitmime = TRUE; /* deliberately not RFC compliant */ address_item *addr_duplicate = NULL; address_item address_defaults = { @@ -659,6 +664,8 @@ uschar *hosts_connection_nolog = NULL; int ignore_bounce_errors_after = 10*7*24*60*60; /* 10 weeks */ BOOL ignore_fromline_local = FALSE; uschar *ignore_fromline_hosts = NULL; +BOOL inetd_wait_mode = FALSE; +int inetd_wait_timeout = -1; uschar *interface_address = NULL; int interface_port = -1; BOOL is_inetd = FALSE; @@ -1311,7 +1318,9 @@ uschar *warnmsg_delay = NULL; uschar *warnmsg_recipients = NULL; BOOL write_rejectlog = TRUE; -uschar *version_copyright = US"Copyright (c) University of Cambridge, 1995 - 2007"; +uschar *version_copyright = + US"Copyright (c) University of Cambridge, 1995 - 2012\n" + "(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2012"; uschar *version_date = US"?"; uschar *version_cnumber = US"????"; uschar *version_string = US"?"; diff --git a/src/src/globals.h b/src/src/globals.h index f9540785c..fbbec3230 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Almost all the global variables are defined together in this one header, so @@ -93,14 +93,16 @@ extern uschar *tls_advertise_hosts; /* host for which TLS is advertised */ extern uschar *tls_certificate; /* Certificate file */ extern uschar *tls_channelbinding_b64; /* string of base64 channel binding */ extern uschar *tls_crl; /* CRL File */ +extern int tls_dh_max_bits; /* don't accept higher lib suggestions */ extern uschar *tls_dhparam; /* DH param file */ +#if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS) +extern uschar *tls_ocsp_file; /* OCSP stapling proof file */ +#endif extern BOOL tls_offered; /* Server offered TLS */ extern uschar *tls_privatekey; /* Private key file */ extern BOOL tls_remember_esmtp; /* For YAEB */ extern uschar *tls_require_ciphers; /* So some can be avoided */ -#ifndef USE_GNUTLS extern uschar *tls_sni; /* Server Name Indication */ -#endif extern uschar *tls_try_verify_hosts; /* Optional client verification */ extern uschar *tls_verify_certificates;/* Path for certificates to check */ extern uschar *tls_verify_hosts; /* Mandatory client verification */ @@ -424,6 +426,8 @@ extern uschar *hosts_treat_as_local; /* For routing */ extern int ignore_bounce_errors_after; /* Keep them for this time. */ extern BOOL ignore_fromline_local; /* Local SMTP ignore fromline */ extern uschar *ignore_fromline_hosts; /* Hosts permitted to send "From " */ +extern BOOL inetd_wait_mode; /* Whether running in inetd wait mode */ +extern int inetd_wait_timeout; /* Timeout for inetd wait mode */ extern BOOL is_inetd; /* True for inetd calls */ extern uschar *iterate_item; /* Item from iterate list */ diff --git a/src/src/host.c b/src/src/host.c index 0571772cb..9dc9c9a3e 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or @@ -68,7 +68,7 @@ sprintf(addr, "%d.%d.%d.%d", very good for the uses to which it is put. When running the regression tests, start with a fixed seed. -If you need better, see pseudo_random_number() which is potentially stronger, +If you need better, see vaguely_random_number() which is potentially stronger, if a crypto library is available, but might end up just calling this instead. Arguments: diff --git a/src/src/local_scan.h b/src/src/local_scan.h index aedfc9f92..057e4d428 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* This file is the header that is the only Exim header to be included in the @@ -188,6 +188,6 @@ extern void smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2); extern void smtp_vprintf(const char *, va_list); extern uschar *string_copy(const uschar *); extern uschar *string_copyn(uschar *, int); -extern uschar *string_sprintf(const char *, ...) PRINTF_FUNCTION(1,2); +extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2); /* End of local_scan.h */ diff --git a/src/src/log.c b/src/src/log.c index bfd1fef86..3a91ed295 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for writing log files. The code for maintaining datestamped @@ -528,11 +528,11 @@ Returns: length actually written, persisting an errno from write() */ ssize_t -write_to_fd_buf(int fd, uschar *buf, size_t length) +write_to_fd_buf(int fd, const uschar *buf, size_t length) { ssize_t wrote; size_t total_written = 0; -uschar *p = buf; +const uschar *p = buf; size_t left = length; while (1) @@ -1306,7 +1306,7 @@ misconfiguration. The first use of this is in ACL logic, "control = debug/tag=foo/opts=+expand" which can be combined with conditions, etc, to activate extra logging only -for certain sources. */ +for certain sources. The second use is inetd wait mode debug preservation. */ void debug_logging_activate(uschar *tag_name, uschar *opts) @@ -1316,7 +1316,7 @@ int fd = -1; if (debug_file) { debug_printf("DEBUGGING ACTIVATED FROM WITHIN CONFIG.\n" - "DEBUG: Tag=\"%s\" Opts=\"%s\"\n", tag_name, opts); + "DEBUG: Tag=\"%s\" Opts=\"%s\"\n", tag_name, opts ? opts : US""); return; } diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index b4d6821ca..3dec7a716 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 2862a5bc9..0bbc86a56 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -17,6 +17,11 @@ header files. */ #define T_TXT 16 #endif +/* Many systems do not have T_SPF. */ +#ifndef T_SPF +#define T_SPF 99 +#endif + /* Table of recognized DNS record types and their integer values. */ static const char *type_names[] = { @@ -33,6 +38,7 @@ static const char *type_names[] = { "mxh", "ns", "ptr", + "spf", "srv", "txt", "zns" @@ -52,6 +58,7 @@ static int type_values[] = { T_MXH, /* Private type for "MX hostnames" */ T_NS, T_PTR, + T_SPF, T_SRV, T_TXT, T_ZNS /* Private type for "zone nameservers" */ @@ -89,7 +96,8 @@ separator is newline. character used for multiple items of text in "TXT" records. Alternatively, if the next character is ';' then these multiple items are concatenated with no separator. With neither of these options specified, only the first item -is output. +is output. Similarly for "SPF" records, but the default for joining multiple +items in one SPF record is the empty string, for direct concatenation. (c) If the next sequence of characters is 'defer_FOO' followed by a comma, the defer behaviour is set to FOO. The possible behaviours are: 'strict', where @@ -114,7 +122,7 @@ int size = 256; int ptr = 0; int sep = 0; int defer_mode = PASS; -int type = T_TXT; +int type; int failrc = FAIL; uschar *outsep = US"\n"; uschar *outsep2 = NULL; @@ -190,8 +198,10 @@ if (strncmpic(keystring, US"defer_", 6) == 0) while (isspace(*keystring)) keystring++; } -/* If the keystring contains an = this must be preceded by a valid type name. */ +/* Figure out the "type" value if it is not T_TXT. +If the keystring contains an = this must be preceded by a valid type name. */ +type = T_TXT; if ((equals = Ustrchr(keystring, '=')) != NULL) { int i, len; @@ -239,6 +249,14 @@ if (type == T_PTR && keystring[0] != '<' && string_is_ip_address(keystring, NULL) != 0) sep = -1; +/* SPF strings should be concatenated without a separator, thus make +it the default if not defined (see RFC 4408 section 3.1.3). +Multiple SPF records are forbidden (section 3.1.2) but are currently +not handled specially, thus they are concatenated with \n by default. */ + +if (type == T_SPF && outsep2 == NULL) + outsep2 = US""; + /* Now scan the list and do a lookup for each item */ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) @@ -316,7 +334,7 @@ while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1); - if (type == T_TXT) + if (type == T_TXT || type == T_SPF) { if (outsep2 == NULL) { diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 5c1ea0b56..244d67561 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Many thanks to Stuart Lynne for contributing the original code for this diff --git a/src/src/macros.h b/src/src/macros.h index 9b41226e5..f7a22b668 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -196,7 +196,7 @@ enum { RESET_NEXT, RESET_ANSWERS, RESET_AUTHORITY, RESET_ADDITIONAL }; enum { tod_log, tod_log_bare, tod_log_zone, tod_log_datestamp_daily, tod_log_datestamp_monthly, tod_zone, tod_full, tod_bsdin, - tod_mbx, tod_epoch, tod_zulu }; + tod_mbx, tod_epoch, tod_epoch_l, tod_zulu }; /* For identifying types of driver */ diff --git a/src/src/malware.c b/src/src/malware.c index 864564ffc..7de913f49 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -115,7 +115,7 @@ malware_in_file(uschar *eml_filename) { /* spool_mbox() assumes various parameters exist, when creating the relevant directory and the email within */ (void) string_format(message_id_buf, sizeof(message_id_buf), - "dummy-%d", pseudo_random_number(INT_MAX)); + "dummy-%d", vaguely_random_number(INT_MAX)); message_id = message_id_buf; sender_address = US"malware-sender@example.net"; return_path = US""; @@ -131,6 +131,9 @@ malware_in_file(uschar *eml_filename) { set, but if that changes, then it should apply to these tests too */ unspool_mbox(); + /* silence static analysis tools */ + message_id = NULL; + return ret; } @@ -1071,7 +1074,7 @@ static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL); if (cmdline_trigger_re == NULL) { log_write(0, LOG_MAIN|LOG_PANIC, - "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset); + "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger, rerror, roffset); return DEFER; }; @@ -1089,7 +1092,7 @@ static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL); if (cmdline_regex_re == NULL) { log_write(0, LOG_MAIN|LOG_PANIC, - "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset); + "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex, rerror, roffset); return DEFER; }; diff --git a/src/src/mytypes.h b/src/src/mytypes.h index ade294e5d..964abf820 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -37,6 +37,12 @@ the arguments of printf-like functions. This is done by a macro. */ #define ARG_UNUSED /**/ #endif +#ifdef WANT_DEEPER_PRINTF_CHECKS +#define ALMOST_PRINTF(A, B) PRINTF_FUNCTION(A, B) +#else +#define ALMOST_PRINTF(A, B) +#endif + /* Some operating systems (naughtily, imo) include a definition for "uchar" in the standard header files, so we use "uschar". Solaris has u_char in diff --git a/src/src/os.c b/src/src/os.c index a70bc6115..6e02b8fe2 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #ifdef STAND_ALONE @@ -807,7 +807,7 @@ directly, instead making you call a function per thread to get a handle. Other OSs handle thread-safe resolver differently, in ways which fail if the programmer creates their own structs. */ -#ifndef OS_GET_DNS_RESOLVER_RES +#if !defined(OS_GET_DNS_RESOLVER_RES) && !defined(COMPILE_UTILITY) #include <resolv.h> diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index c0c0e3263..4f0da3f71 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1,7 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * - * Copyright (C) 2009 Tom Kistner <tom@duncanthrax.net> + * Copyright (C) 2009 - 2012 Tom Kistner <tom@duncanthrax.net> * * http://duncanthrax.net/pdkim/ * @@ -1383,10 +1383,11 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu char *b = strdup(sig->headernames); char *p = b; char *q = NULL; + pdkim_stringlist *hdrs = ctx->headers; + if (b == NULL) return PDKIM_ERR_OOM; /* clear tags */ - pdkim_stringlist *hdrs = ctx->headers; while (hdrs != NULL) { hdrs->tag = 0; hdrs = hdrs->next; diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h index 2066e2734..764cc83be 100644 --- a/src/src/pdkim/pdkim.h +++ b/src/src/pdkim/pdkim.h @@ -1,7 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * - * Copyright (C) 2009 Tom Kistner <tom@duncanthrax.net> + * Copyright (C) 2009 - 2012 Tom Kistner <tom@duncanthrax.net> * * http://duncanthrax.net/pdkim/ * diff --git a/src/src/readconf.c b/src/src/readconf.c index b35811e48..c3ffe4f82 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading the configuration file, and for displaying @@ -235,6 +235,7 @@ static optionlist optionlist_config[] = { { "gecos_pattern", opt_stringptr, &gecos_pattern }, #ifdef SUPPORT_TLS { "gnutls_compat_mode", opt_bool, &gnutls_compat_mode }, + /* These three gnutls_require_* options stopped working in Exim 4.80 */ { "gnutls_require_kx", opt_stringptr, &gnutls_require_kx }, { "gnutls_require_mac", opt_stringptr, &gnutls_require_mac }, { "gnutls_require_protocols", opt_stringptr, &gnutls_require_proto }, @@ -414,7 +415,11 @@ static optionlist optionlist_config[] = { { "tls_advertise_hosts", opt_stringptr, &tls_advertise_hosts }, { "tls_certificate", opt_stringptr, &tls_certificate }, { "tls_crl", opt_stringptr, &tls_crl }, + { "tls_dh_max_bits", opt_int, &tls_dh_max_bits }, { "tls_dhparam", opt_stringptr, &tls_dhparam }, +#if defined(EXPERIMENTAL_OCSP) && !defined(USE_GNUTLS) + { "tls_ocsp_file", opt_stringptr, &tls_ocsp_file }, +#endif { "tls_on_connect_ports", opt_stringptr, &tls_on_connect_ports }, { "tls_privatekey", opt_stringptr, &tls_privatekey }, { "tls_remember_esmtp", opt_bool, &tls_remember_esmtp }, @@ -2768,6 +2773,71 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malformed ratelimit data: %s", s); /************************************************* +* Drop privs for checking TLS config * +*************************************************/ + +/* We want to validate TLS options during readconf, but do not want to be +root when we call into the TLS library, in case of library linkage errors +which cause segfaults; before this check, those were always done as the Exim +runtime user and it makes sense to continue with that. + +Assumes: tls_require_ciphers has been set, if it will be + exim_user has been set, if it will be + exim_group has been set, if it will be + +Returns: bool for "okay"; false will cause caller to immediately exit. +*/ + +#ifdef SUPPORT_TLS +static BOOL +tls_dropprivs_validate_require_cipher(void) +{ +const uschar *errmsg; +pid_t pid; +int rc, status; +void (*oldsignal)(int); + +oldsignal = signal(SIGCHLD, SIG_DFL); + +fflush(NULL); +if ((pid = fork()) < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check"); + +if (pid == 0) + { + /* in some modes, will have dropped privilege already */ + if (!geteuid()) + exim_setugid(exim_uid, exim_gid, FALSE, + US"calling tls_validate_require_cipher"); + + errmsg = tls_validate_require_cipher(); + if (errmsg) + { + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "tls_require_ciphers invalid: %s", errmsg); + } + fflush(NULL); + _exit(0); + } + +do { + rc = waitpid(pid, &status, 0); +} while (rc < 0 && errno == EINTR); + +DEBUG(D_tls) + debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n", + (int)pid, status); + +signal(SIGCHLD, oldsignal); + +return status == 0; +} +#endif /* SUPPORT_TLS */ + + + + +/************************************************* * Read main configuration options * *************************************************/ @@ -3217,6 +3287,18 @@ if ((tls_verify_hosts != NULL || tls_try_verify_hosts != NULL) && "tls_%sverify_hosts is set, but tls_verify_certificates is not set", (tls_verify_hosts != NULL)? "" : "try_"); +/* This also checks that the library linkage is working and we can call +routines in it, so call even if tls_require_ciphers is unset */ +if (!tls_dropprivs_validate_require_cipher()) + exit(1); + +/* Magic number: at time of writing, 1024 has been the long-standing value +used by so many clients, and what Exim used to use always, that it makes +sense to just min-clamp this max-clamp at that. */ +if (tls_dh_max_bits < 1024) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "tls_dh_max_bits is too small, must be at least 1024 for interop"); + /* If openssl_options is set, validate it */ if (openssl_options != NULL) { diff --git a/src/src/receive.c b/src/src/receive.c index aaaf64ce6..378bb8f3a 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for receiving a message and setting up spool files. */ @@ -3488,12 +3488,10 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 && if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL) s = string_append(s, &size, &sptr, 3, US" DN=\"", string_printing(tls_peerdn), US"\""); -#ifndef USE_GNUTLS if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL) s = string_append(s, &size, &sptr, 3, US" SNI=\"", string_printing(tls_sni), US"\""); #endif -#endif if (sender_host_authenticated != NULL) { diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index e9c4df919..372800783 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -142,7 +142,7 @@ iplookup_router_entry( address_item **addr_succeed) /* put old address here on success */ { uschar *query = NULL; -uschar reply[256]; +uschar *reply; uschar *hostname, *reroute, *domain, *listptr; uschar host_buffer[256]; host_item *host = store_get(sizeof(host_item)); @@ -161,6 +161,8 @@ pw = pw; DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n", rblock->name, addr->address, addr->domain); +reply = store_get(256); + /* Build the query string to send. If not explicitly given, a default of "user@domain user@domain" is used. */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index d1c10f00f..2b5cc26d3 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -841,12 +841,10 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 && if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL) s = string_append(s, &size, &ptr, 3, US" DN=\"", string_printing(tls_peerdn), US"\""); -#ifndef USE_GNUTLS if ((log_extra_selector & LX_tls_sni) != 0 && tls_sni != NULL) s = string_append(s, &size, &ptr, 3, US" SNI=\"", string_printing(tls_sni), US"\""); #endif -#endif sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)? US" C=..." : US" C="; @@ -1676,8 +1674,7 @@ if (!sender_host_unknown) #ifdef SUPPORT_TLS if (tls_on_connect && - tls_server_start(tls_require_ciphers, - gnutls_require_mac, gnutls_require_kx, gnutls_require_proto) != OK) + tls_server_start(tls_require_ciphers) != OK) return FALSE; #endif @@ -3893,8 +3890,7 @@ while (done <= 0) We must allow for an extra EHLO command and an extra AUTH command after STARTTLS that don't add to the nonmail command count. */ - if ((rc = tls_server_start(tls_require_ciphers, gnutls_require_mac, - gnutls_require_kx, gnutls_require_proto)) == OK) + if ((rc = tls_server_start(tls_require_ciphers)) == OK) { if (!tls_remember_esmtp) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index bdc3903c0..3c611a505 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading spool files. When compiling for a utility (eximon), @@ -286,10 +286,8 @@ dkim_collect_input = FALSE; tls_certificate_verified = FALSE; tls_cipher = NULL; tls_peerdn = NULL; -#ifndef USE_GNUTLS tls_sni = NULL; #endif -#endif #ifdef WITH_CONTENT_SCAN spam_score_int = NULL; @@ -552,10 +550,8 @@ for (;;) tls_cipher = string_copy(big_buffer + 12); else if (Ustrncmp(p, "ls_peerdn", 9) == 0) tls_peerdn = string_unprinting(string_copy(big_buffer + 12)); - #ifndef USE_GNUTLS else if (Ustrncmp(p, "ls_sni", 6) == 0) tls_sni = string_unprinting(string_copy(big_buffer + 9)); - #endif break; #endif diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index 635fb8df1..bdeb2b1a6 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -56,7 +56,7 @@ FILE *spool_mbox(unsigned long *mbox_file_size, uschar *source_file_override) { mbox_file = modefopen(mbox_path, "wb", SPOOL_MODE); if (mbox_file == NULL) { log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno, - "scan file %s", mbox_file)); + "scan file %s", mbox_path)); goto OUT; }; @@ -155,7 +155,7 @@ FILE *spool_mbox(unsigned long *mbox_file_size, uschar *source_file_override) { if (Ustat(mbox_path, &statbuf) != 0 || (yield = Ufopen(mbox_path,"rb")) == NULL) { log_write(0, LOG_MAIN|LOG_PANIC, "%s", string_open_failed(errno, - "scan file %s", mbox_file)); + "scan file %s", mbox_path)); goto OUT; }; diff --git a/src/src/spool_out.c b/src/src/spool_out.c index fa4f1b6e2..1a380dd17 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for writing spool files, and moving them about. */ @@ -229,10 +229,8 @@ if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts); if (tls_certificate_verified) fprintf(f, "-tls_certificate_verified\n"); if (tls_cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_cipher); if (tls_peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_peerdn)); -#ifndef USE_GNUTLS if (tls_sni != NULL) fprintf(f, "-tls_sni %s\n", string_printing(tls_sni)); #endif -#endif /* To complete the envelope, write out the tree of non-recipients, followed by the list of recipients. These won't be disjoint the first time, when no diff --git a/src/src/std-crypto.c b/src/src/std-crypto.c new file mode 100644 index 000000000..3f0fec897 --- /dev/null +++ b/src/src/std-crypto.c @@ -0,0 +1,521 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Phil Pennock 2012 + * But almost everything here is fixed published constants from RFCs, so also: + * Copyright (C) The Internet Society (2003) + * Copyright (C) The IETF Trust (2008) + * Most of the text in RFC referencing comments is copy/paste from RFC, + * as is undoubtedly the intention. + * The constants are generated from that text using util/gen_pkcs3.c invoked + * with the -C option. + */ + +/* See the file NOTICE for conditions of use and distribution. */ + +#include "exim.h" + +#ifndef SUPPORT_TLS +static void dummy(int x) { dummy(x-1); } +#else + +/* The IETF defines standard primes as "Modular Exponential (MODP) Groups" for +use in IKE in RFC 2409 and 3526, and then some more, "for Use with IETF +Standards" in RFC 5114. These have been thoroughly reviewed as meeting +certain eligibility criteria, which is more than can be said for primes +generated quickly on no particular criteria. + +Any prime used in TLS is disclosed publicly, and if the security of your +session depends upon the prime being secret, then one of three situations +holds: + (1) the prime is too small + (2) the prime is flawed, use one of these instead + (3) you know of fundamental cryptanalytic breaks not currently publicly known + to the cryptographic community. +*/ + +/* RFC 2409 MODP IKE_id=1 generator=2 bits=768 + The prime is: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } + Its hexadecimal value is + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_1_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MGYCYQD//////////8kP2qIhaMI0xMZii4DcHNEpAk4IimfMdAILvqY7E5siUUoI\n" +"eY40BN3vlRmzzTpDGzArCm3yXxQ3T+E1bW1RwkXkhbV2Yl5+xvRMQummOjYg////\n" +"//////8CAQI=\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 2409 MODP IKE_id=2 generator=2 bits=1024 + The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. + Its hexadecimal value is + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 + FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_2_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR\n" +"Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL\n" +"/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 2409; id=3 and id=4 are EC2N, not yet supported here */ + +/* RFC 3526 MODP IKE_id=5 generator=2 bits=1536 + The prime is: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } + Its hexadecimal value is: + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D + C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F + 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D + 670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_5_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIHHAoHBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR\n" +"Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL\n" +"/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7ORbPcIAfLihY78FmNpINhxV05pp\n" +"Fj+o/STPX4NlXSPco62WHGLzViCFUrue1SkHcJaWbWcMNU5KvJgE8XRsCMojcyf/\n" +"/////////wIBAg==\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 3526 MODP IKE_id=14 generator=2 bits=2048 + This prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } + Its hexadecimal value is: + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D + C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F + 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D + 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B + E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 + DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 + 15728E5A 8AACAA68 FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_14_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n" +"IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n" +"awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n" +"mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n" +"fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n" +"5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 3526 MODP IKE_id=15 generator=2 bits=3072 + This prime is: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } + Its hexadecimal value is: + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D + C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F + 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D + 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B + E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 + DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 + 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 + ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 + ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B + F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C + BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 + 43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_15_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIIBiAKCAYEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n" +"IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n" +"awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n" +"mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n" +"fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n" +"5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYM\n" +"fbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshq\n" +"ZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqTrS\n" +"yv//////////AgEC\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 3526 MODP IKE_id=16 generator=2 bits=4096 + This prime is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } + Its hexadecimal value is: + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D + C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F + 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D + 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B + E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 + DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 + 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 + ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 + ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B + F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C + BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 + 43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 + 88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA + 2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6 + 287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED + 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9 + 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199 + FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_16_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIICCAKCAgEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n" +"IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n" +"awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n" +"mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n" +"fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n" +"5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYM\n" +"fbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshq\n" +"ZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqSEI\n" +"ARpyPBKnh+bXiHGaEL26WyaZwycYavTiPBqUaDS2FQvaJYPpyirUTOjbu8LbBN6O\n" +"+S6O/BQfvsqmKHxZR05rwF2ZspZPoJDDoiM7oYZRW+ftH2EpcM7i16+4G912IXBI\n" +"HNAGkSfVsFqpk7TqmI2P3cGG/7fckKbAj030Nck0BjGZ//////////8CAQI=\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 3526 MODP IKE_id=17 generator=2 bits=6144 + This prime is: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } + Its hexadecimal value is: + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08 + 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B + 302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9 + A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 + 49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 + FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D + 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C + 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718 + 3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D + 04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D + B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 + 1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C + BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC + E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26 + 99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB + 04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2 + 233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 + D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492 + 36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406 + AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918 + DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151 + 2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03 + F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F + BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA + CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B + B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632 + 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E + 6DCC4024 FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_17_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIIDCAKCAwEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n" +"IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n" +"awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n" +"mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n" +"fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n" +"5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYM\n" +"fbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshq\n" +"ZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqSEI\n" +"ARpyPBKnh+bXiHGaEL26WyaZwycYavTiPBqUaDS2FQvaJYPpyirUTOjbu8LbBN6O\n" +"+S6O/BQfvsqmKHxZR05rwF2ZspZPoJDDoiM7oYZRW+ftH2EpcM7i16+4G912IXBI\n" +"HNAGkSfVsFqpk7TqmI2P3cGG/7fckKbAj030Nck0AoSSNsP6tNJ8cCbB1NyyYCZG\n" +"3sl1HnY9uje9+P+UBq2eUw7l2zgvQTABrrBqU+2QJ9gxF5cnsIZaiRjaPtvrz5sU\n" +"7UTObLrO1Lsb238UR+bMJUszIFFRK9evQm+49AE3jNK/WYPKAcZLkuzwMuoV0XId\n" +"A/SC185udP721V5wL0aYDIK1qEAxkAscnlnnyX++x+jzI6l6fjbMiL4PHUW3/1ha\n" +"xUvUB7IrQVSqzI9tfr9I4dgUzF7SD4A34KeXFe7ym+MoBqHVi7fF2nb1UKo9ih+/\n" +"8OsZzLGjE9Vc2lbJ7C7yljI4f+jXbjwEaAQ+j2Y/SGDuEr8tWwt0dNbmlPkebcxA\n" +"JP//////////AgEC\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 3526 MODP IKE_id=18 generator=2 bits=8192 + This prime is: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } + Its hexadecimal value is: + + FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 + 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD + EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 + E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED + EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D + C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F + 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D + 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B + E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 + DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 + 15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64 + ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7 + ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B + F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C + BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 + 43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 + 88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA + 2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6 + 287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED + 1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9 + 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492 + 36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD + F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831 + 179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B + DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF + 5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6 + D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3 + 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA + CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 + 06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C + DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE + 12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4 + 38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300 + 741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568 + 3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9 + 22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B + 4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A + 062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36 + 4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1 + B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92 + 4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47 + 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71 + 60C980DD 98EDD3DF FFFFFFFF FFFFFFFF +*/ +static const char dh_ike_18_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIIECAKCBAEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n" +"IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n" +"awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n" +"mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n" +"fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n" +"5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYM\n" +"fbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshq\n" +"ZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqSEI\n" +"ARpyPBKnh+bXiHGaEL26WyaZwycYavTiPBqUaDS2FQvaJYPpyirUTOjbu8LbBN6O\n" +"+S6O/BQfvsqmKHxZR05rwF2ZspZPoJDDoiM7oYZRW+ftH2EpcM7i16+4G912IXBI\n" +"HNAGkSfVsFqpk7TqmI2P3cGG/7fckKbAj030Nck0AoSSNsP6tNJ8cCbB1NyyYCZG\n" +"3sl1HnY9uje9+P+UBq2eUw7l2zgvQTABrrBqU+2QJ9gxF5cnsIZaiRjaPtvrz5sU\n" +"7UTObLrO1Lsb238UR+bMJUszIFFRK9evQm+49AE3jNK/WYPKAcZLkuzwMuoV0XId\n" +"A/SC185udP721V5wL0aYDIK1qEAxkAscnlnnyX++x+jzI6l6fjbMiL4PHUW3/1ha\n" +"xUvUB7IrQVSqzI9tfr9I4dgUzF7SD4A34KeXFe7ym+MoBqHVi7fF2nb1UKo9ih+/\n" +"8OsZzLGjE9Vc2lbJ7C7yljI4f+jXbjwEaAQ+j2Y/SGDuEr8tWwt0dNbmlPkebb4R\n" +"WXSjkm8S/uXkOHd8tqky34zYvsTQc7kxujvIMraNndMAdB+nv4r8R+0ldvaTa6Qk\n" +"ZjqrY5xa5PVoNCO0dCvxyXgjjxbL451lLeP9uL78hIrZIiIuBKQDfAcT61eoGiPw\n" +"xzRz/GRs6jBrS8vIhi+Dhd36nUt/osCH6HloMwPtW906Bis89bOieKZtKhP4P0T4\n" +"Ld8xDuB0q2o2RZfomaAlXcFk8xzFCEaFHfmrSBld7X6hsdUQvX7nTXP682vDHs+i\n" +"aDWQRvTrh5+SQAlDi0gcbNeImgAu1e44K8kZDab8Am5HlVjkR1Z36aqeMFDidlaU\n" +"38gfVuiAuW5xYMmA3Zjt09///////////wIBAg==\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 5114 IKE_id=22 +2.1. 1024-bit MODP Group with 160-bit Prime Order Subgroup + + The hexadecimal value of the prime is: + + p = B10B8F96 A080E01D DE92DE5E AE5D54EC 52C99FBC FB06A3C6 + 9A6A9DCA 52D23B61 6073E286 75A23D18 9838EF1E 2EE652C0 + 13ECB4AE A9061123 24975C3C D49B83BF ACCBDD7D 90C4BD70 + 98488E9C 219A7372 4EFFD6FA E5644738 FAA31A4F F55BCCC0 + A151AF5F 0DC8B4BD 45BF37DF 365C1A65 E68CFDA7 6D4DA708 + DF1FB2BC 2E4A4371 + + The hexadecimal value of the generator is: + + g = A4D1CBD5 C3FD3412 6765A442 EFB99905 F8104DD2 58AC507F + D6406CFF 14266D31 266FEA1E 5C41564B 777E690F 5504F213 + 160217B4 B01B886A 5E91547F 9E2749F4 D7FBD7D3 B9A92EE1 + 909D0D22 63F80A76 A6A24C08 7A091F53 1DBF0A01 69B6A28A + D662A4D1 8E73AFA3 2D779D59 18D08BC8 858F4DCE F97C2A24 + 855E6EEB 22B3B2E5 + + The generator generates a prime-order subgroup of size: + + q = F518AA87 81A8DF27 8ABA4E7D 64B7CB9D 49462353 +*/ +static const char dh_ike_22_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIIBCAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y\n" +"mDjvHi7mUsAT7LSuqQYRIySXXDzUm4O/rMvdfZDEvXCYSI6cIZpzck7/1vrlZEc4\n" +"+qMaT/VbzMChUa9fDci0vUW/N982XBpl5oz9p21NpwjfH7K8LkpDcQKBgQCk0cvV\n" +"w/00EmdlpELvuZkF+BBN0lisUH/WQGz/FCZtMSZv6h5cQVZLd35pD1UE8hMWAhe0\n" +"sBuIal6RVH+eJ0n01/vX07mpLuGQnQ0iY/gKdqaiTAh6CR9THb8KAWm2oorWYqTR\n" +"jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5Q==\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 5114 IKE_id=23 +2.2. 2048-bit MODP Group with 224-bit Prime Order Subgroup + + The hexadecimal value of the prime is: + + p = AD107E1E 9123A9D0 D660FAA7 9559C51F A20D64E5 683B9FD1 + B54B1597 B61D0A75 E6FA141D F95A56DB AF9A3C40 7BA1DF15 + EB3D688A 309C180E 1DE6B85A 1274A0A6 6D3F8152 AD6AC212 + 9037C9ED EFDA4DF8 D91E8FEF 55B7394B 7AD5B7D0 B6C12207 + C9F98D11 ED34DBF6 C6BA0B2C 8BBC27BE 6A00E0A0 B9C49708 + B3BF8A31 70918836 81286130 BC8985DB 1602E714 415D9330 + 278273C7 DE31EFDC 7310F712 1FD5A074 15987D9A DC0A486D + CDF93ACC 44328387 315D75E1 98C641A4 80CD86A1 B9E587E8 + BE60E69C C928B2B9 C52172E4 13042E9B 23F10B0E 16E79763 + C9B53DCF 4BA80A29 E3FB73C1 6B8E75B9 7EF363E2 FFA31F71 + CF9DE538 4E71B81C 0AC4DFFE 0C10E64F + + The hexadecimal value of the generator is: + + g = AC4032EF 4F2D9AE3 9DF30B5C 8FFDAC50 6CDEBE7B 89998CAF + 74866A08 CFE4FFE3 A6824A4E 10B9A6F0 DD921F01 A70C4AFA + AB739D77 00C29F52 C57DB17C 620A8652 BE5E9001 A8D66AD7 + C1766910 1999024A F4D02727 5AC1348B B8A762D0 521BC98A + E2471504 22EA1ED4 09939D54 DA7460CD B5F6C6B2 50717CBE + F180EB34 118E98D1 19529A45 D6F83456 6E3025E3 16A330EF + BB77A86F 0C1AB15B 051AE3D4 28C8F8AC B70A8137 150B8EEB + 10E183ED D19963DD D9E263E4 770589EF 6AA21E7F 5F2FF381 + B539CCE3 409D13CD 566AFBB4 8D6C0191 81E1BCFE 94B30269 + EDFE72FE 9B6AA4BD 7B5A0F1C 71CFFF4C 19C418E1 F6EC0179 + 81BC087F 2A7065B3 84B890D3 191F2BFA + + The generator generates a prime-order subgroup of size: + + q = 801C0D34 C58D93FE 99717710 1F80535A 4738CEBC BF389A99 + B36371EB +*/ +static const char dh_ike_23_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIICCgKCAQEArRB+HpEjqdDWYPqnlVnFH6INZOVoO5/RtUsVl7YdCnXm+hQd+VpW\n" +"26+aPEB7od8V6z1oijCcGA4d5rhaEnSgpm0/gVKtasISkDfJ7e/aTfjZHo/vVbc5\n" +"S3rVt9C2wSIHyfmNEe002/bGugssi7wnvmoA4KC5xJcIs7+KMXCRiDaBKGEwvImF\n" +"2xYC5xRBXZMwJ4Jzx94x79xzEPcSH9WgdBWYfZrcCkhtzfk6zEQyg4cxXXXhmMZB\n" +"pIDNhqG55YfovmDmnMkosrnFIXLkEwQumyPxCw4W55djybU9z0uoCinj+3PBa451\n" +"uX7zY+L/ox9xz53lOE5xuBwKxN/+DBDmTwKCAQEArEAy708tmuOd8wtcj/2sUGze\n" +"vnuJmYyvdIZqCM/k/+OmgkpOELmm8N2SHwGnDEr6q3OddwDCn1LFfbF8YgqGUr5e\n" +"kAGo1mrXwXZpEBmZAkr00CcnWsE0i7inYtBSG8mK4kcVBCLqHtQJk51U2nRgzbX2\n" +"xrJQcXy+8YDrNBGOmNEZUppF1vg0Vm4wJeMWozDvu3eobwwasVsFGuPUKMj4rLcK\n" +"gTcVC47rEOGD7dGZY93Z4mPkdwWJ72qiHn9fL/OBtTnM40CdE81Wavu0jWwBkYHh\n" +"vP6UswJp7f5y/ptqpL17Wg8ccc//TBnEGOH27AF5gbwIfypwZbOEuJDTGR8r+g==\n" +"-----END DH PARAMETERS-----\n"; + +/* RFC 5114 IKE_id=24 +2.3. 2048-bit MODP Group with 256-bit Prime Order Subgroup + + The hexadecimal value of the prime is: + + p = 87A8E61D B4B6663C FFBBD19C 65195999 8CEEF608 660DD0F2 + 5D2CEED4 435E3B00 E00DF8F1 D61957D4 FAF7DF45 61B2AA30 + 16C3D911 34096FAA 3BF4296D 830E9A7C 209E0C64 97517ABD + 5A8A9D30 6BCF67ED 91F9E672 5B4758C0 22E0B1EF 4275BF7B + 6C5BFC11 D45F9088 B941F54E B1E59BB8 BC39A0BF 12307F5C + 4FDB70C5 81B23F76 B63ACAE1 CAA6B790 2D525267 35488A0E + F13C6D9A 51BFA4AB 3AD83477 96524D8E F6A167B5 A41825D9 + 67E144E5 14056425 1CCACB83 E6B486F6 B3CA3F79 71506026 + C0B857F6 89962856 DED4010A BD0BE621 C3A3960A 54E710C3 + 75F26375 D7014103 A4B54330 C198AF12 6116D227 6E11715F + 693877FA D7EF09CA DB094AE9 1E1A1597 + + The hexadecimal value of the generator is: + + g = 3FB32C9B 73134D0B 2E775066 60EDBD48 4CA7B18F 21EF2054 + 07F4793A 1A0BA125 10DBC150 77BE463F FF4FED4A AC0BB555 + BE3A6C1B 0C6B47B1 BC3773BF 7E8C6F62 901228F8 C28CBB18 + A55AE313 41000A65 0196F931 C77A57F2 DDF463E5 E9EC144B + 777DE62A AAB8A862 8AC376D2 82D6ED38 64E67982 428EBC83 + 1D14348F 6F2F9193 B5045AF2 767164E1 DFC967C1 FB3F2E55 + A4BD1BFF E83B9C80 D052B985 D182EA0A DB2A3B73 13D3FE14 + C8484B1E 052588B9 B7D2BBD2 DF016199 ECD06E15 57CD0915 + B3353BBB 64E0EC37 7FD02837 0DF92B52 C7891428 CDC67EB6 + 184B523D 1DB246C3 2F630784 90F00EF8 D647D148 D4795451 + 5E2327CF EF98C582 664B4C0F 6CC41659 + + The generator generates a prime-order subgroup of size: + + q = 8CF83642 A709A097 B4479976 40129DA2 99B1A47D 1EB3750B + A308B0FE 64F5FBD3 +*/ +static const char dh_ike_24_pem[] = +"-----BEGIN DH PARAMETERS-----\n" +"MIICCQKCAQEAh6jmHbS2Zjz/u9GcZRlZmYzu9ghmDdDyXSzu1ENeOwDgDfjx1hlX\n" +"1Pr330VhsqowFsPZETQJb6o79Cltgw6afCCeDGSXUXq9WoqdMGvPZ+2R+eZyW0dY\n" +"wCLgse9Cdb97bFv8EdRfkIi5QfVOseWbuLw5oL8SMH9cT9twxYGyP3a2Osrhyqa3\n" +"kC1SUmc1SIoO8TxtmlG/pKs62DR3llJNjvahZ7WkGCXZZ+FE5RQFZCUcysuD5rSG\n" +"9rPKP3lxUGAmwLhX9omWKFbe1AEKvQvmIcOjlgpU5xDDdfJjddcBQQOktUMwwZiv\n" +"EmEW0iduEXFfaTh3+tfvCcrbCUrpHhoVlwKCAQA/syybcxNNCy53UGZg7b1ITKex\n" +"jyHvIFQH9Hk6GguhJRDbwVB3vkY//0/tSqwLtVW+OmwbDGtHsbw3c79+jG9ikBIo\n" +"+MKMuxilWuMTQQAKZQGW+THHelfy3fRj5ensFEt3feYqqrioYorDdtKC1u04ZOZ5\n" +"gkKOvIMdFDSPby+Rk7UEWvJ2cWTh38lnwfs/LlWkvRv/6DucgNBSuYXRguoK2yo7\n" +"cxPT/hTISEseBSWIubfSu9LfAWGZ7NBuFVfNCRWzNTu7ZODsN3/QKDcN+StSx4kU\n" +"KM3GfrYYS1I9HbJGwy9jB4SQ8A741kfRSNR5VFFeIyfP75jFgmZLTA9sxBZZ\n" +"-----END DH PARAMETERS-----\n"; + + +/* ========================================================================= */ + +struct dh_constant { + const char *label; + const char *pem; +}; + +/* KEEP SORTED ALPHABETICALLY; + * duplicate PEM are okay, if we want aliases, but names must be alphabetical */ +static struct dh_constant dh_constants[] = { + { "default", dh_ike_23_pem }, + { "ike1", dh_ike_1_pem }, + { "ike14", dh_ike_14_pem }, + { "ike15", dh_ike_15_pem }, + { "ike16", dh_ike_16_pem }, + { "ike17", dh_ike_17_pem }, + { "ike18", dh_ike_18_pem }, + { "ike2", dh_ike_2_pem }, + { "ike22", dh_ike_22_pem }, + { "ike23", dh_ike_23_pem }, + { "ike24", dh_ike_24_pem }, + { "ike5", dh_ike_5_pem }, +}; +static const int dh_constants_count = + sizeof(dh_constants) / sizeof(struct dh_constant); + + +/* A policy decision; in absence of any other data, use a 2048 bit prime, + * pick the first one from the latest RFC providing such. */ +const char * +std_dh_prime_default(void) +{ + return dh_ike_23_pem; +} + + +const char * +std_dh_prime_named(const uschar *name) +{ + int first, last; + char *search_name = CS string_copylc(US name); + + first = 0; + last = dh_constants_count; + while (last > first) { + int middle = (first + last)/2; + int c = strcmp(search_name, dh_constants[middle].label); + if (c == 0) + return dh_constants[middle].pem; + else if (c > 0) + first = middle + 1; + else + last = middle; + } + return NULL; +} + +#endif /* SUPPORT_TLS */ +/* EOF */ diff --git a/src/src/store.c b/src/src/store.c index 0a5a11fc2..790f79ddb 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Exim gets and frees all its store through these functions. In the original diff --git a/src/src/string.c b/src/src/string.c index 3fea7c048..0e73e2c79 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Miscellaneous string-handling functions. Some are not required for @@ -716,7 +716,7 @@ uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; va_start(ap, format); if (!string_vformat(buffer, sizeof(buffer), format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "string_sprintf expansion was longer than %d", sizeof(buffer)); + "string_sprintf expansion was longer than " SIZE_T_FMT, sizeof(buffer)); va_end(ap); return string_copy(buffer); } @@ -1134,7 +1134,8 @@ return yield; BOOL string_vformat(uschar *buffer, int buflen, const char *format, va_list ap) { -enum { L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE }; +/* We assume numbered ascending order, C does not guarantee that */ +enum { L_NORMAL=1, L_SHORT=2, L_LONG=3, L_LONGLONG=4, L_LONGDOUBLE=5, L_SIZE=6 }; BOOL yield = TRUE; int width, precision; @@ -1204,7 +1205,7 @@ while (*fp != 0) } } - /* Skip over 'h', 'L', 'l', and 'll', remembering the item length */ + /* Skip over 'h', 'L', 'l', 'll' and 'z', remembering the item length */ if (*fp == 'h') { fp++; length = L_SHORT; } @@ -1223,6 +1224,8 @@ while (*fp != 0) length = L_LONG; } } + else if (*fp == 'z') + { fp++; length = L_SIZE; } /* Handle each specific format type. */ @@ -1252,6 +1255,7 @@ while (*fp != 0) case L_NORMAL: sprintf(CS p, newformat, va_arg(ap, int)); break; case L_LONG: sprintf(CS p, newformat, va_arg(ap, long int)); break; case L_LONGLONG: sprintf(CS p, newformat, va_arg(ap, LONGLONG_T)); break; + case L_SIZE: sprintf(CS p, newformat, va_arg(ap, size_t)); break; } while (*p) p++; break; diff --git a/src/src/structs.h b/src/src/structs.h index 9b51d0b7c..c319611df 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 7e87dded0..c8bf634bc 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2,178 +2,194 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* This module provides TLS (aka SSL) support for Exim using the GnuTLS -library. It is #included into tls.c when that library is used. The code herein -is based on a patch that was contributed by Nikos Mavroyanopoulos. +/* Copyright (c) Phil Pennock 2012 */ -No cryptographic code is included in Exim. All this module does is to call -functions from the GnuTLS library. */ +/* This file provides TLS/SSL support for Exim using the GnuTLS library, +one of the available supported implementations. This file is #included into +tls.c when USE_GNUTLS has been set. -/* Note: This appears to be using an old API from compat.h; it is likely that -someone familiary with GnuTLS programming could rework a lot of this to a -modern API and perhaps remove the explicit knowledge of crypto algorithms from -Exim. Such a re-work would be most welcome and we'd sacrifice support for -older GnuTLS releases without too many qualms -- maturity and experience -in crypto libraries tends to improve their robustness against attack. -Frankly, if you maintain it, you decide what's supported and what isn't. */ +The code herein is a revamp of GnuTLS integration using the current APIs; the +original tls-gnu.c was based on a patch which was contributed by Nikos +Mavroyanopoulos. The revamp is partially a rewrite, partially cut&paste as +appropriate. -/* Heading stuff for GnuTLS */ +APIs current as of GnuTLS 2.12.18; note that the GnuTLS manual is for GnuTLS 3, +which is not widely deployed by OS vendors. Will note issues below, which may +assist in updating the code in the future. Another sources of hints is +mod_gnutls for Apache (SNI callback registration and handling). -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> +Keeping client and server variables more split than before and is currently +the norm, in anticipation of TLS in ACL callouts. +I wanted to switch to gnutls_certificate_set_verify_function() so that +certificate rejection could happen during handshake where it belongs, rather +than being dropped afterwards, but that was introduced in 2.10.0 and Debian +(6.0.5) is still on 2.8.6. So for now we have to stick with sub-par behaviour. -#define UNKNOWN_NAME "unknown" -#define DH_BITS 1024 -#define PARAM_SIZE 2*1024 +(I wasn't looking for libraries quite that old, when updating to get rid of +compiler warnings of deprecated APIs. If it turns out that a lot of the rest +require current GnuTLS, then we'll drop support for the ancient libraries). +*/ +#include <gnutls/gnutls.h> +/* needed for cert checks in verification and DN extraction: */ +#include <gnutls/x509.h> +/* man-page is incorrect, gnutls_rnd() is not in gnutls.h: */ +#include <gnutls/crypto.h> -/* Values for verify_requirment */ +/* GnuTLS 2 vs 3 -enum { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED }; +GnuTLS 3 only: + gnutls_global_set_audit_log_function() -/* Local static variables for GNUTLS */ +Changes: + gnutls_certificate_verify_peers2(): is new, drop the 2 for old version +*/ -static host_item *client_host; +/* Local static variables for GnuTLS */ -static gnutls_dh_params dh_params = NULL; +/* Values for verify_requirement */ -static gnutls_certificate_server_credentials x509_cred = NULL; -static gnutls_session tls_session = NULL; +enum peer_verify_requirement { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED }; -static char ssl_errstring[256]; +/* This holds most state for server or client; with this, we can set up an +outbound TLS-enabled connection in an ACL callout, while not stomping all +over the TLS variables available for expansion. -static int ssl_session_timeout = 200; -static int verify_requirement; +Some of these correspond to variables in globals.c; those variables will +be set to point to content in one of these instances, as appropriate for +the stage of the process lifetime. -/* Priorities for TLS algorithms to use. In each case there's a default table, -and space into which it can be copied and altered. */ +Not handled here: globals tls_active, tls_bits, tls_cipher, tls_peerdn, +tls_certificate_verified, tls_channelbinding_b64, tls_sni. +*/ -static const int default_proto_priority[16] = { - /* These are gnutls_protocol_t enum values */ -#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 - GNUTLS_TLS1_2, -#endif -#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 - GNUTLS_TLS1_1, -#endif - GNUTLS_TLS1, - GNUTLS_SSL3, - 0 }; +typedef struct exim_gnutls_state { + gnutls_session_t session; + gnutls_certificate_credentials_t x509_cred; + gnutls_priority_t priority_cache; + enum peer_verify_requirement verify_requirement; + int fd_in; + int fd_out; + BOOL peer_cert_verified; + BOOL trigger_sni_changes; + BOOL have_set_peerdn; + const struct host_item *host; + uschar *peerdn; + uschar *ciphersuite; + uschar *received_sni; + + const uschar *tls_certificate; + const uschar *tls_privatekey; + const uschar *tls_sni; /* client send only, not received */ + const uschar *tls_verify_certificates; + const uschar *tls_crl; + const uschar *tls_require_ciphers; + uschar *exp_tls_certificate; + uschar *exp_tls_privatekey; + uschar *exp_tls_sni; + uschar *exp_tls_verify_certificates; + uschar *exp_tls_crl; + uschar *exp_tls_require_ciphers; + + uschar *xfer_buffer; + int xfer_buffer_lwm; + int xfer_buffer_hwm; + int xfer_eof; + int xfer_error; +} exim_gnutls_state_st; + +static const exim_gnutls_state_st exim_gnutls_state_init = { + NULL, NULL, NULL, VERIFY_NONE, -1, -1, FALSE, FALSE, FALSE, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, 0, 0, 0, 0, +}; -static int proto_priority[16]; +/* Not only do we have our own APIs which don't pass around state, assuming +it's held in globals, GnuTLS doesn't appear to let us register callback data +for callbacks, or as part of the session, so we have to keep a "this is the +context we're currently dealing with" pointer and rely upon being +single-threaded to keep from processing data on an inbound TLS connection while +talking to another TLS connection for an outbound check. This does mean that +there's no way for heart-beats to be responded to, for the duration of the +second connection. */ -static const int default_kx_priority[16] = { - GNUTLS_KX_RSA, - GNUTLS_KX_DHE_DSS, - GNUTLS_KX_DHE_RSA, - 0 }; +static exim_gnutls_state_st state_server, state_client; +static exim_gnutls_state_st *current_global_tls_state; -static int kx_priority[16]; +/* dh_params are initialised once within the lifetime of a process using TLS; +if we used TLS in a long-lived daemon, we'd have to reconsider this. But we +don't want to repeat this. */ -static int default_cipher_priority[16] = { - GNUTLS_CIPHER_AES_256_CBC, - GNUTLS_CIPHER_AES_128_CBC, - GNUTLS_CIPHER_3DES_CBC, - GNUTLS_CIPHER_ARCFOUR_128, - 0 }; +static gnutls_dh_params_t dh_server_params = NULL; -static int cipher_priority[16]; +/* No idea how this value was chosen; preserving it. Default is 3600. */ -static const int default_mac_priority[16] = { - GNUTLS_MAC_SHA, - GNUTLS_MAC_MD5, - 0 }; +static const int ssl_session_timeout = 200; -static int mac_priority[16]; +static const char * const exim_default_gnutls_priority = "NORMAL"; -/* These two are currently not changeable. */ +/* Guard library core initialisation */ -static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; -static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 }; +static BOOL exim_gnutls_base_init_done = FALSE; -/* Tables of priority names and equivalent numbers */ -typedef struct pri_item { - uschar *name; - int *values; -} pri_item; +/* ------------------------------------------------------------------------ */ +/* macros */ +#define MAX_HOST_LEN 255 -#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 -static int tls1_2_codes[] = { GNUTLS_TLS1_2, 0 }; +/* Set this to control gnutls_global_set_log_level(); values 0 to 9 will setup +the library logging; a value less than 0 disables the calls to set up logging +callbacks. */ +#ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL +#define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 #endif -#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 -static int tls1_1_codes[] = { GNUTLS_TLS1_1, 0 }; + +#ifndef EXIM_CLIENT_DH_MIN_BITS +#define EXIM_CLIENT_DH_MIN_BITS 1024 #endif -/* more recent libraries define this as an equivalent value to the -canonical GNUTLS_TLS1_0; since they're the same, we stick to the -older name. */ -static int tls1_0_codes[] = { GNUTLS_TLS1, 0 }; -static int ssl3_codes[] = { GNUTLS_SSL3, 0 }; - -static pri_item proto_index[] = { -#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 - { US"TLS1.2", tls1_2_codes }, + +/* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we +can ask for a bit-strength. Without that, we stick to the constant we had +before, for now. */ +#ifndef EXIM_SERVER_DH_BITS_PRE2_12 +#define EXIM_SERVER_DH_BITS_PRE2_12 1024 #endif -#if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 - { US"TLS1.1", tls1_1_codes }, + +#define exim_gnutls_err_check(Label) do { \ + if (rc != GNUTLS_E_SUCCESS) { return tls_error((Label), gnutls_strerror(rc), host); } } while (0) + +#define expand_check_tlsvar(Varname) expand_check(state->Varname, US #Varname, &state->exp_##Varname) + +#if GNUTLS_VERSION_NUMBER >= 0x020c00 +#define HAVE_GNUTLS_SESSION_CHANNEL_BINDING +#define HAVE_GNUTLS_SEC_PARAM_CONSTANTS +#define HAVE_GNUTLS_RND #endif - { US"TLS1.0", tls1_0_codes }, - { US"TLS1", tls1_0_codes }, - { US"SSL3", ssl3_codes } -}; -static int kx_rsa_codes[] = { GNUTLS_KX_RSA, - GNUTLS_KX_DHE_RSA, 0 }; -static int kx_dhe_codes[] = { GNUTLS_KX_DHE_DSS, - GNUTLS_KX_DHE_RSA, 0 }; -static int kx_dhe_dss_codes[] = { GNUTLS_KX_DHE_DSS, 0 }; -static int kx_dhe_rsa_codes[] = { GNUTLS_KX_DHE_RSA, 0 }; -static pri_item kx_index[] = { - { US"DHE_DSS", kx_dhe_dss_codes }, - { US"DHE_RSA", kx_dhe_rsa_codes }, - { US"RSA", kx_rsa_codes }, - { US"DHE", kx_dhe_codes } -}; +/* ------------------------------------------------------------------------ */ +/* Callback declarations */ -static int arcfour_128_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, 0 }; -static int arcfour_40_codes[] = { GNUTLS_CIPHER_ARCFOUR_40, 0 }; -static int arcfour_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, - GNUTLS_CIPHER_ARCFOUR_40, 0 }; -static int aes_256_codes[] = { GNUTLS_CIPHER_AES_256_CBC, 0 }; -static int aes_128_codes[] = { GNUTLS_CIPHER_AES_128_CBC, 0 }; -static int aes_codes[] = { GNUTLS_CIPHER_AES_256_CBC, - GNUTLS_CIPHER_AES_128_CBC, 0 }; -static int des3_codes[] = { GNUTLS_CIPHER_3DES_CBC, 0 }; - -static pri_item cipher_index[] = { - { US"ARCFOUR_128", arcfour_128_codes }, - { US"ARCFOUR_40", arcfour_40_codes }, - { US"ARCFOUR", arcfour_codes }, - { US"AES_256", aes_256_codes }, - { US"AES_128", aes_128_codes }, - { US"AES", aes_codes }, - { US"3DES", des3_codes } -}; +#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0 +static void exim_gnutls_logger_cb(int level, const char *message); +#endif +static int exim_sni_handling_cb(gnutls_session_t session); -static int mac_sha_codes[] = { GNUTLS_MAC_SHA, 0 }; -static int mac_md5_codes[] = { GNUTLS_MAC_MD5, 0 }; -static pri_item mac_index[] = { - { US"SHA", mac_sha_codes }, - { US"SHA1", mac_sha_codes }, - { US"MD5", mac_md5_codes } -}; +/* ------------------------------------------------------------------------ */ +/* Static functions */ /************************************************* * Handle TLS error * @@ -188,120 +204,146 @@ some shared functions. Argument: prefix text to include in the logged error - host NULL if setting up a server; - the connected host if setting up a client msg additional error string (may be NULL) usually obtained from gnutls_strerror() + host NULL if setting up a server; + the connected host if setting up a client Returns: OK/DEFER/FAIL */ static int -tls_error(uschar *prefix, host_item *host, const char *msg) +tls_error(const uschar *prefix, const char *msg, const host_item *host) { -if (host == NULL) +if (host) + { + log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s", + host->name, host->address, prefix, msg ? ": " : "", msg ? msg : ""); + return FAIL; + } +else { uschar *conn_info = smtp_get_connection_info(); - if (strncmp(conn_info, "SMTP ", 5) == 0) + if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s", - conn_info, prefix, msg ? ": " : "", msg ? msg : ""); + conn_info, prefix, msg ? ": " : "", msg ? msg : ""); return DEFER; } +} + + + + +/************************************************* +* Deal with logging errors during I/O * +*************************************************/ + +/* We have to get the identity of the peer from saved data. + +Argument: + state the current GnuTLS exim state container + rc the GnuTLS error code, or 0 if it's a local error + when text identifying read or write + text local error text when ec is 0 + +Returns: nothing +*/ + +static void +record_io_error(exim_gnutls_state_st *state, int rc, uschar *when, uschar *text) +{ +const char *msg; + +if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) + msg = CS string_sprintf("%s: %s", US gnutls_strerror(rc), + US gnutls_alert_get_name(gnutls_alert_get(state->session))); else - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s", - host->name, host->address, prefix, msg ? ": " : "", msg ? msg : ""); - return FAIL; - } + msg = gnutls_strerror(rc); + +tls_error(when, msg, state->host); } + /************************************************* -* Verify certificate * +* Set various Exim expansion vars * *************************************************/ -/* Called after a successful handshake, when certificate verification is -required or optional, for both server and client. +/* We set various Exim global variables from the state, once a session has +been established. With TLS callouts, may need to change this to stack +variables, or just re-call it with the server state after client callout +has finished. -Arguments: - session GNUTLS session - error where to put text giving a reason for failure +Make sure anything set here is inset in tls_getc(). + +Sets: + tls_active fd + tls_bits strength indicator + tls_certificate_verified bool indicator + tls_channelbinding_b64 for some SASL mechanisms + tls_cipher a string + tls_peerdn a string + tls_sni a (UTF-8) string +Also: + current_global_tls_state for API limitations -Returns: TRUE/FALSE +Argument: + state the relevant exim_gnutls_state_st * */ -static BOOL -verify_certificate(gnutls_session session, const char **error) +static void +extract_exim_vars_from_tls_state(exim_gnutls_state_st *state) { -int rc = -1; -uschar *dn_string = US""; -const gnutls_datum *cert; -unsigned int verify, cert_size = 0; +gnutls_cipher_algorithm_t cipher; +#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING +int old_pool; +int rc; +gnutls_datum_t channel; +#endif -*error = NULL; +current_global_tls_state = state; -/* Get the peer's certificate. If it sent one, extract it's DN, and then -attempt to verify the certificate. If no certificate is supplied, verification -is forced to fail. */ +tls_active = state->fd_out; -cert = gnutls_certificate_get_peers(session, &cert_size); -if (cert != NULL) - { - uschar buff[1024]; - gnutls_x509_crt gcert; +cipher = gnutls_cipher_get(state->session); +/* returns size in "bytes" */ +tls_bits = gnutls_cipher_get_key_size(cipher) * 8; - gnutls_x509_crt_init(&gcert); - dn_string = US"unknown"; +tls_cipher = state->ciphersuite; - if (gnutls_x509_crt_import(gcert, cert, GNUTLS_X509_FMT_DER) == 0) - { - size_t bufsize = sizeof(buff); - if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) - dn_string = string_copy_malloc(buff); - } +DEBUG(D_tls) debug_printf("cipher: %s\n", tls_cipher); - rc = gnutls_certificate_verify_peers2(session, &verify); - } -else - { - DEBUG(D_tls) debug_printf("no peer certificate supplied\n"); - verify = GNUTLS_CERT_INVALID; - *error = "not supplied"; - } +tls_certificate_verified = state->peer_cert_verified; -/* Handle the result of verification. INVALID seems to be set as well -as REVOKED, but leave the test for both. */ +/* note that tls_channelbinding_b64 is not saved to the spool file, since it's +only available for use for authenticators while this TLS session is running. */ -if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) - { - tls_certificate_verified = FALSE; - if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)? - "revoked" : "invalid"; - if (verify_requirement == VERIFY_REQUIRED) - { - DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): " - "peerdn=%s\n", *error, dn_string); - gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); - return FALSE; /* reject */ - } - DEBUG(D_tls) debug_printf("TLS certificate verify failure (%s) overridden " - "(host in tls_try_verify_hosts): peerdn=%s\n", *error, dn_string); - } -else - { - tls_certificate_verified = TRUE; - DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", - dn_string); - } +tls_channelbinding_b64 = NULL; +#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING +channel.data = NULL; +channel.size = 0; +rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_UNIQUE, &channel); +if (rc) { + DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); +} else { + old_pool = store_pool; + store_pool = POOL_PERM; + tls_channelbinding_b64 = auth_b64encode(channel.data, (int)channel.size); + store_pool = old_pool; + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n"); +} +#endif + +tls_peerdn = state->peerdn; -tls_peerdn = dn_string; -return TRUE; /* accept */ +tls_sni = state->received_sni; } + /************************************************* * Setup up DH parameters * *************************************************/ @@ -316,30 +358,100 @@ file is never present. If two processes both compute some new parameters, you waste a bit of effort, but it doesn't seem worth messing around with locking to prevent this. -Argument: - host NULL for server, server for client (for error handling) - Returns: OK/DEFER/FAIL */ static int -init_dh(host_item *host) +init_server_dh(void) { -int fd; -int ret; +int fd, rc; +unsigned int dh_bits; gnutls_datum m; -uschar filename[200]; +uschar filename_buf[PATH_MAX]; +uschar *filename = NULL; +size_t sz; +uschar *exp_tls_dhparam; +BOOL use_file_in_spool = FALSE; +BOOL use_fixed_file = FALSE; +host_item *host = NULL; /* dummy for macros */ + +DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n"); -/* Initialize the data structures for holding the parameters */ +rc = gnutls_dh_params_init(&dh_server_params); +exim_gnutls_err_check(US"gnutls_dh_params_init"); -ret = gnutls_dh_params_init(&dh_params); -if (ret < 0) return tls_error(US"init dh_params", host, gnutls_strerror(ret)); +m.data = NULL; +m.size = 0; -/* Set up the name of the cache file */ +if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam)) + return DEFER; -if (!string_format(filename, sizeof(filename), "%s/gnutls-params", - spool_directory)) - return tls_error(US"overlong filename", host, NULL); +if (!exp_tls_dhparam) + { + DEBUG(D_tls) debug_printf("Loading default hard-coded DH params\n"); + m.data = US std_dh_prime_default(); + m.size = Ustrlen(m.data); + } +else if (Ustrcmp(exp_tls_dhparam, "historic") == 0) + use_file_in_spool = TRUE; +else if (Ustrcmp(exp_tls_dhparam, "none") == 0) + { + DEBUG(D_tls) debug_printf("Requested no DH parameters.\n"); + return OK; + } +else if (exp_tls_dhparam[0] != '/') + { + m.data = US std_dh_prime_named(exp_tls_dhparam); + if (m.data == NULL) + return tls_error(US"No standard prime named", CS exp_tls_dhparam, NULL); + m.size = Ustrlen(m.data); + } +else + { + use_fixed_file = TRUE; + filename = exp_tls_dhparam; + } + +if (m.data) + { + rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM); + exim_gnutls_err_check(US"gnutls_dh_params_import_pkcs3"); + DEBUG(D_tls) debug_printf("Loaded fixed standard D-H parameters\n"); + return OK; + } + +#ifdef HAVE_GNUTLS_SEC_PARAM_CONSTANTS +/* If you change this constant, also change dh_param_fn_ext so that we can use a +different filename and ensure we have sufficient bits. */ +dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL); +if (!dh_bits) + return tls_error(US"gnutls_sec_param_to_pk_bits() failed", NULL, NULL); +DEBUG(D_tls) + debug_printf("GnuTLS tells us that for D-H PK, NORMAL is %d bits.\n", + dh_bits); +#else +dh_bits = EXIM_SERVER_DH_BITS_PRE2_12; +DEBUG(D_tls) + debug_printf("GnuTLS lacks gnutls_sec_param_to_pk_bits(), using %d bits.\n", + dh_bits); +#endif + +/* Some clients have hard-coded limits. */ +if (dh_bits > tls_dh_max_bits) + { + DEBUG(D_tls) + debug_printf("tls_dh_max_bits clamping override, using %d bits instead.\n", + tls_dh_max_bits); + dh_bits = tls_dh_max_bits; + } + +if (use_file_in_spool) + { + if (!string_format(filename_buf, sizeof(filename_buf), + "%s/gnutls-params-%d", spool_directory, dh_bits)) + return tls_error(US"overlong filename", NULL, NULL); + filename = filename_buf; + } /* Open the cache file for reading and if successful, read it and set up the parameters. */ @@ -348,27 +460,50 @@ fd = Uopen(filename, O_RDONLY, 0); if (fd >= 0) { struct stat statbuf; - if (fstat(fd, &statbuf) < 0) + FILE *fp; + int saved_errno; + + if (fstat(fd, &statbuf) < 0) /* EIO */ + { + saved_errno = errno; + (void)close(fd); + return tls_error(US"TLS cache stat failed", strerror(saved_errno), NULL); + } + if (!S_ISREG(statbuf.st_mode)) + { + (void)close(fd); + return tls_error(US"TLS cache not a file", NULL, NULL); + } + fp = fdopen(fd, "rb"); + if (!fp) { + saved_errno = errno; (void)close(fd); - return tls_error(US"TLS cache stat failed", host, strerror(errno)); + return tls_error(US"fdopen(TLS cache stat fd) failed", + strerror(saved_errno), NULL); } m.size = statbuf.st_size; m.data = malloc(m.size); if (m.data == NULL) - return tls_error(US"memory allocation failed", host, strerror(errno)); - errno = 0; - if (read(fd, m.data, m.size) != m.size) - return tls_error(US"TLS cache read failed", host, strerror(errno)); - (void)close(fd); - - ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM); - if (ret < 0) - return tls_error(US"DH params import", host, gnutls_strerror(ret)); - DEBUG(D_tls) debug_printf("read D-H parameters from file\n"); + { + fclose(fp); + return tls_error(US"malloc failed", strerror(errno), NULL); + } + sz = fread(m.data, m.size, 1, fp); + if (!sz) + { + saved_errno = errno; + fclose(fp); + free(m.data); + return tls_error(US"fread failed", strerror(saved_errno), NULL); + } + fclose(fp); + rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM); free(m.data); + exim_gnutls_err_check(US"gnutls_dh_params_import_pkcs3"); + DEBUG(D_tls) debug_printf("read D-H parameters from file \"%s\"\n", filename); } /* If the file does not exist, fall through to compute new data and cache it. @@ -376,13 +511,13 @@ If there was any other opening error, it is serious. */ else if (errno == ENOENT) { - ret = -1; + rc = -1; DEBUG(D_tls) - debug_printf("parameter cache file %s does not exist\n", filename); + debug_printf("D-H parameter cache file \"%s\" does not exist\n", filename); } else - return tls_error(string_open_failed(errno, "%s for reading", filename), - host, NULL); + return tls_error(string_open_failed(errno, "\"%s\" for reading", filename), + NULL, NULL); /* If ret < 0, either the cache file does not exist, or the data it contains is not useful. One particular case of this is when upgrading from an older @@ -390,59 +525,93 @@ release of Exim in which the data was stored in a different format. We don't try to be clever and support both formats; we just regenerate new data in this case. */ -if (ret < 0) +if (rc < 0) { - uschar tempfilename[sizeof(filename) + 10]; - - DEBUG(D_tls) debug_printf("generating %d bit Diffie-Hellman key...\n", - DH_BITS); - ret = gnutls_dh_params_generate2(dh_params, DH_BITS); - if (ret < 0) return tls_error(US"D-H key generation", host, gnutls_strerror(ret)); + uschar *temp_fn; + unsigned int dh_bits_gen = dh_bits; - /* Write the parameters to a file in the spool directory so that we - can use them from other Exim processes. */ + if ((PATH_MAX - Ustrlen(filename)) < 10) + return tls_error(US"Filename too long to generate replacement", + CS filename, NULL); - sprintf(CS tempfilename, "%s-%d", filename, (int)getpid()); - fd = Uopen(tempfilename, O_WRONLY|O_CREAT, 0400); + temp_fn = string_copy(US "%s.XXXXXXX"); + fd = mkstemp(CS temp_fn); /* modifies temp_fn */ if (fd < 0) - return tls_error(string_open_failed(errno, "%s for writing", filename), - host, NULL); + return tls_error(US"Unable to open temp file", strerror(errno), NULL); (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */ - /* export the parameters in a format that can be generated using GNUTLS' - * certtool or other programs. - * - * The commands for certtool are: - * $ certtool --generate-dh-params --bits 1024 > params + /* GnuTLS overshoots! + * If we ask for 2236, we might get 2237 or more. + * But there's no way to ask GnuTLS how many bits there really are. + * We can ask how many bits were used in a TLS session, but that's it! + * The prime itself is hidden behind too much abstraction. + * So we ask for less, and proceed on a wing and a prayer. + * First attempt, subtracted 3 for 2233 and got 2240. */ + if (dh_bits >= EXIM_CLIENT_DH_MIN_BITS + 10) + { + dh_bits_gen = dh_bits - 10; + DEBUG(D_tls) + debug_printf("being paranoid about DH generation, make it '%d' bits'\n", + dh_bits_gen); + } - m.size = PARAM_SIZE; + DEBUG(D_tls) + debug_printf("requesting generation of %d bit Diffie-Hellman prime ...\n", + dh_bits_gen); + rc = gnutls_dh_params_generate2(dh_server_params, dh_bits_gen); + exim_gnutls_err_check(US"gnutls_dh_params_generate2"); + + /* gnutls_dh_params_export_pkcs3() will tell us the exact size, every time, + and I confirmed that a NULL call to get the size first is how the GnuTLS + sample apps handle this. */ + + sz = 0; + m.data = NULL; + rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM, + m.data, &sz); + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) + exim_gnutls_err_check(US"gnutls_dh_params_export_pkcs3(NULL) sizing"); + m.size = sz; m.data = malloc(m.size); if (m.data == NULL) - return tls_error(US"memory allocation failed", host, strerror(errno)); - - m.size = PARAM_SIZE; - ret = gnutls_dh_params_export_pkcs3(dh_params, GNUTLS_X509_FMT_PEM, m.data, - &m.size); - if (ret < 0) - return tls_error(US"DH params export", host, gnutls_strerror(ret)); - - m.size = Ustrlen(m.data); - errno = 0; - if (write(fd, m.data, m.size) != m.size || write(fd, "\n", 1) != 1) - return tls_error(US"TLS cache write failed", host, strerror(errno)); + return tls_error(US"memory allocation failed", strerror(errno), NULL); + /* this will return a size 1 less than the allocation size above */ + rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM, + m.data, &sz); + if (rc != GNUTLS_E_SUCCESS) + { + free(m.data); + exim_gnutls_err_check(US"gnutls_dh_params_export_pkcs3() real"); + } + m.size = sz; /* shrink by 1, probably */ + sz = write_to_fd_buf(fd, m.data, (size_t) m.size); + if (sz != m.size) + { + free(m.data); + return tls_error(US"TLS cache write D-H params failed", + strerror(errno), NULL); + } free(m.data); - (void)close(fd); + sz = write_to_fd_buf(fd, US"\n", 1); + if (sz != 1) + return tls_error(US"TLS cache write D-H params final newline failed", + strerror(errno), NULL); + + rc = close(fd); + if (rc) + return tls_error(US"TLS cache write close() failed", + strerror(errno), NULL); - if (rename(CS tempfilename, CS filename) < 0) - return tls_error(string_sprintf("failed to rename %s as %s", - tempfilename, filename), host, strerror(errno)); + if (Urename(temp_fn, filename) < 0) + return tls_error(string_sprintf("failed to rename \"%s\" as \"%s\"", + temp_fn, filename), strerror(errno), NULL); - DEBUG(D_tls) debug_printf("wrote D-H parameters to file %s\n", filename); + DEBUG(D_tls) debug_printf("wrote D-H parameters to file \"%s\"\n", filename); } -DEBUG(D_tls) debug_printf("initialized D-H parameters\n"); +DEBUG(D_tls) debug_printf("initialized server D-H parameters\n"); return OK; } @@ -450,91 +619,118 @@ return OK; /************************************************* -* Initialize for GnuTLS * +* Variables re-expanded post-SNI * *************************************************/ -/* Called from both server and client code. In the case of a server, errors -before actual TLS negotiation return DEFER. +/* Called from both server and client code, via tls_init(), and also from +the SNI callback after receiving an SNI, if tls_certificate includes "tls_sni". + +We can tell the two apart by state->received_sni being non-NULL in callback. + +The callback should not call us unless state->trigger_sni_changes is true, +which we are responsible for setting on the first pass through. Arguments: - host connected host, if client; NULL if server - certificate certificate file - privatekey private key file - cas CA certs file - crl CRL file + state exim_gnutls_state_st * Returns: OK/DEFER/FAIL */ static int -tls_init(host_item *host, uschar *certificate, uschar *privatekey, uschar *cas, - uschar *crl) +tls_expand_session_files(exim_gnutls_state_st *state) { +struct stat statbuf; int rc; -uschar *cert_expanded, *key_expanded, *cas_expanded, *crl_expanded; - -client_host = host; +const host_item *host = state->host; /* macro should be reconsidered? */ +uschar *saved_tls_certificate = NULL; +uschar *saved_tls_privatekey = NULL; +uschar *saved_tls_verify_certificates = NULL; +uschar *saved_tls_crl = NULL; +int cert_count; + +/* We check for tls_sni *before* expansion. */ +if (!state->host) + { + if (!state->received_sni) + { + if (state->tls_certificate && Ustrstr(state->tls_certificate, US"tls_sni")) + { + DEBUG(D_tls) debug_printf("We will re-expand TLS session files if we receive SNI.\n"); + state->trigger_sni_changes = TRUE; + } + } + else + { + /* useful for debugging */ + saved_tls_certificate = state->exp_tls_certificate; + saved_tls_privatekey = state->exp_tls_privatekey; + saved_tls_verify_certificates = state->exp_tls_verify_certificates; + saved_tls_crl = state->exp_tls_crl; + } + } -rc = gnutls_global_init(); -if (rc < 0) return tls_error(US"tls-init", host, gnutls_strerror(rc)); +rc = gnutls_certificate_allocate_credentials(&state->x509_cred); +exim_gnutls_err_check(US"gnutls_certificate_allocate_credentials"); -/* Create D-H parameters, or read them from the cache file. This function does -its own SMTP error messaging. */ +/* remember: expand_check_tlsvar() is expand_check() but fiddling with +state members, assuming consistent naming; and expand_check() returns +false if expansion failed, unless expansion was forced to fail. */ -rc = init_dh(host); -if (rc != OK) return rc; +/* check if we at least have a certificate, before doing expensive +D-H generation. */ -/* Create the credentials structure */ +if (!expand_check_tlsvar(tls_certificate)) + return DEFER; -rc = gnutls_certificate_allocate_credentials(&x509_cred); -if (rc < 0) - return tls_error(US"certificate_allocate_credentials", - host, gnutls_strerror(rc)); +/* certificate is mandatory in server, optional in client */ -/* This stuff must be done for each session, because different certificates -may be required for different sessions. */ +if ((state->exp_tls_certificate == NULL) || + (*state->exp_tls_certificate == '\0')) + { + if (state->host == NULL) + return tls_error(US"no TLS server certificate is specified", NULL, NULL); + else + DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n"); + } -if (!expand_check(certificate, US"tls_certificate", &cert_expanded)) +if (state->tls_privatekey && !expand_check_tlsvar(tls_privatekey)) return DEFER; -key_expanded = NULL; -if (privatekey != NULL) +/* tls_privatekey is optional, defaulting to same file as certificate */ + +if (state->tls_privatekey == NULL || *state->tls_privatekey == '\0') { - if (!expand_check(privatekey, US"tls_privatekey", &key_expanded)) - return DEFER; + state->tls_privatekey = state->tls_certificate; + state->exp_tls_privatekey = state->exp_tls_certificate; } -/* If expansion was forced to fail, key_expanded will be NULL. If the result of -the expansion is an empty string, ignore it also, and assume that the private -key is in the same file as the certificate. */ - -if (key_expanded == NULL || *key_expanded == 0) - key_expanded = cert_expanded; - -/* Set the certificate and private keys */ -if (cert_expanded != NULL) +if (state->exp_tls_certificate && *state->exp_tls_certificate) { DEBUG(D_tls) debug_printf("certificate file = %s\nkey file = %s\n", - cert_expanded, key_expanded); - rc = gnutls_certificate_set_x509_key_file(x509_cred, CS cert_expanded, - CS key_expanded, GNUTLS_X509_FMT_PEM); - if (rc < 0) + state->exp_tls_certificate, state->exp_tls_privatekey); + + if (state->received_sni) { - uschar *msg = string_sprintf("cert/key setup: cert=%s key=%s", - cert_expanded, key_expanded); - return tls_error(msg, host, gnutls_strerror(rc)); + if ((Ustrcmp(state->exp_tls_certificate, saved_tls_certificate) == 0) && + (Ustrcmp(state->exp_tls_privatekey, saved_tls_privatekey) == 0)) + { + DEBUG(D_tls) debug_printf("TLS SNI: cert and key unchanged\n"); + } + else + { + DEBUG(D_tls) debug_printf("TLS SNI: have a changed cert/key pair.\n"); + } } - } -/* A certificate is mandatory in a server, but not in a client */ - -else - { - if (host == NULL) - return tls_error(US"no TLS server certificate is specified", NULL, NULL); - DEBUG(D_tls) debug_printf("no TLS client certificate is specified\n"); - } + rc = gnutls_certificate_set_x509_key_file(state->x509_cred, + CS state->exp_tls_certificate, CS state->exp_tls_privatekey, + GNUTLS_X509_FMT_PEM); + exim_gnutls_err_check( + string_sprintf("cert/key setup: cert=%s key=%s", + state->exp_tls_certificate, state->exp_tls_privatekey)); + DEBUG(D_tls) debug_printf("TLS: cert/key registered\n"); + } /* tls_certificate */ /* Set the trusted CAs file if one is provided, and then add the CRL if one is provided. Experiment shows that, if the certificate file is empty, an unhelpful @@ -542,48 +738,87 @@ error message is provided. However, if we just refrain from setting anything up in that case, certificate verification fails, which seems to be the correct behaviour. */ -if (cas != NULL) +if (state->tls_verify_certificates && *state->tls_verify_certificates) { - struct stat statbuf; - - if (!expand_check(cas, US"tls_verify_certificates", &cas_expanded)) + if (!expand_check_tlsvar(tls_verify_certificates)) return DEFER; + if (state->tls_crl && *state->tls_crl) + if (!expand_check_tlsvar(tls_crl)) + return DEFER; - if (stat(CS cas_expanded, &statbuf) < 0) + if (!(state->exp_tls_verify_certificates && + *state->exp_tls_verify_certificates)) { - log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " - "(tls_verify_certificates): %s", cas_expanded, strerror(errno)); - return DEFER; + DEBUG(D_tls) + debug_printf("TLS: tls_verify_certificates expanded empty, ignoring\n"); + /* With no tls_verify_certificates, we ignore tls_crl too */ + return OK; } + } +else + { + DEBUG(D_tls) + debug_printf("TLS: tls_verify_certificates not set or empty, ignoring\n"); + return OK; + } - DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", - cas_expanded, statbuf.st_size); +if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " + "(tls_verify_certificates): %s", state->exp_tls_verify_certificates, + strerror(errno)); + return DEFER; + } - /* If the cert file is empty, there's no point in loading the CRL file. */ +/* The test suite passes in /dev/null; we could check for that path explicitly, +but who knows if someone has some weird FIFO which always dumps some certs, or +other weirdness. The thing we really want to check is that it's not a +directory, since while OpenSSL supports that, GnuTLS does not. +So s/!S_ISREG/S_ISDIR/ and change some messsaging ... */ +if (S_ISDIR(statbuf.st_mode)) + { + DEBUG(D_tls) + debug_printf("verify certificates path is a dir: \"%s\"\n", + state->exp_tls_verify_certificates); + log_write(0, LOG_MAIN|LOG_PANIC, + "tls_verify_certificates \"%s\" is a directory", + state->exp_tls_verify_certificates); + return DEFER; + } - if (statbuf.st_size > 0) - { - rc = gnutls_certificate_set_x509_trust_file(x509_cred, CS cas_expanded, - GNUTLS_X509_FMT_PEM); - if (rc < 0) return tls_error(US"setup_certs", host, gnutls_strerror(rc)); +DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", + state->exp_tls_verify_certificates, statbuf.st_size); - if (crl != NULL && *crl != 0) - { - if (!expand_check(crl, US"tls_crl", &crl_expanded)) - return DEFER; - DEBUG(D_tls) debug_printf("loading CRL file = %s\n", crl_expanded); - rc = gnutls_certificate_set_x509_crl_file(x509_cred, CS crl_expanded, - GNUTLS_X509_FMT_PEM); - if (rc < 0) return tls_error(US"CRL setup", host, gnutls_strerror(rc)); - } - } +if (statbuf.st_size == 0) + { + DEBUG(D_tls) + debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n"); + return OK; } -/* Associate the parameters with the x509 credentials structure. */ +cert_count = gnutls_certificate_set_x509_trust_file(state->x509_cred, + CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM); +if (cert_count < 0) + { + rc = cert_count; + exim_gnutls_err_check(US"gnutls_certificate_set_x509_trust_file"); + } +DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count); -gnutls_certificate_set_dh_params(x509_cred, dh_params); +if (state->tls_crl && *state->tls_crl && + state->exp_tls_crl && *state->exp_tls_crl) + { + DEBUG(D_tls) debug_printf("loading CRL file = %s\n", state->exp_tls_crl); + cert_count = gnutls_certificate_set_x509_crl_file(state->x509_cred, + CS state->exp_tls_crl, GNUTLS_X509_FMT_PEM); + if (cert_count < 0) + { + rc = cert_count; + exim_gnutls_err_check(US"gnutls_certificate_set_x509_crl_file"); + } + DEBUG(D_tls) debug_printf("Processed %d CRLs.\n", cert_count); + } -DEBUG(D_tls) debug_printf("initialized certificate stuff\n"); return OK; } @@ -591,310 +826,532 @@ return OK; /************************************************* -* Remove from a priority list * +* Set X.509 state variables * *************************************************/ -/* Cautiously written so that it will remove duplicates if present. +/* In GnuTLS, the registered cert/key are not replaced by a later +set of a cert/key, so for SNI support we need a whole new x509_cred +structure. Which means various other non-re-expanded pieces of state +need to be re-set in the new struct, so the setting logic is pulled +out to this. Arguments: - list a zero-terminated list - remove_list a zero-terminated list to be removed + state exim_gnutls_state_st * -Returns: nothing +Returns: OK/DEFER/FAIL */ -static void -remove_priority(int *list, int *remove_list) +static int +tls_set_remaining_x509(exim_gnutls_state_st *state) { -for (; *remove_list != 0; remove_list++) +int rc; +const host_item *host = state->host; /* macro should be reconsidered? */ + +/* Create D-H parameters, or read them from the cache file. This function does +its own SMTP error messaging. This only happens for the server, TLS D-H ignores +client-side params. */ + +if (!state->host) { - int *p = list; - while (*p != 0) + if (!dh_server_params) { - if (*p == *remove_list) - { - int *pp = p; - do { pp[0] = pp[1]; pp++; } while (*pp != 0); - } - else p++; + rc = init_server_dh(); + if (rc != OK) return rc; } + gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params); } -} +/* Link the credentials to the session. */ +rc = gnutls_credentials_set(state->session, GNUTLS_CRD_CERTIFICATE, state->x509_cred); +exim_gnutls_err_check(US"gnutls_credentials_set"); + +return OK; +} /************************************************* -* Add to a priority list * +* Initialize for GnuTLS * *************************************************/ -/* Cautiously written to check the list size +/* Called from both server and client code. In the case of a server, errors +before actual TLS negotiation return DEFER. Arguments: - list a zero-terminated list - list_max maximum offset in the list - add_list a zero-terminated list to be added + host connected host, if client; NULL if server + certificate certificate file + privatekey private key file + sni TLS SNI to send, sometimes when client; else NULL + cas CA certs file + crl CRL file + require_ciphers tls_require_ciphers setting -Returns: TRUE if OK; FALSE if list overflows +Returns: OK/DEFER/FAIL */ -static BOOL -add_priority(int *list, int list_max, int *add_list) +static int +tls_init( + const host_item *host, + const uschar *certificate, + const uschar *privatekey, + const uschar *sni, + const uschar *cas, + const uschar *crl, + const uschar *require_ciphers, + exim_gnutls_state_st **caller_state) { -int next = 0; -while (list[next] != 0) next++; -while (*add_list != 0) +exim_gnutls_state_st *state; +int rc; +size_t sz; +const char *errpos; +uschar *p; +BOOL want_default_priorities; + +if (!exim_gnutls_base_init_done) { - if (next >= list_max) return FALSE; - list[next++] = *add_list++; + DEBUG(D_tls) debug_printf("GnuTLS global init required.\n"); + + rc = gnutls_global_init(); + exim_gnutls_err_check(US"gnutls_global_init"); + +#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0 + DEBUG(D_tls) + { + gnutls_global_set_log_function(exim_gnutls_logger_cb); + /* arbitrarily chosen level; bump upto 9 for more */ + gnutls_global_set_log_level(EXIM_GNUTLS_LIBRARY_LOG_LEVEL); + } +#endif + + exim_gnutls_base_init_done = TRUE; } -list[next] = 0; -return TRUE; -} +if (host) + { + state = &state_client; + memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); + DEBUG(D_tls) debug_printf("initialising GnuTLS client session\n"); + rc = gnutls_init(&state->session, GNUTLS_CLIENT); + } +else + { + state = &state_server; + memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); + DEBUG(D_tls) debug_printf("initialising GnuTLS server session\n"); + rc = gnutls_init(&state->session, GNUTLS_SERVER); + } +exim_gnutls_err_check(US"gnutls_init"); +state->host = host; -/************************************************* -* Adjust a priority list * -*************************************************/ +state->tls_certificate = certificate; +state->tls_privatekey = privatekey; +state->tls_require_ciphers = require_ciphers; +state->tls_sni = sni; +state->tls_verify_certificates = cas; +state->tls_crl = crl; -/* This function is called to adjust the lists of cipher algorithms, MAC -algorithms, key-exchange methods, and protocols. +/* This handles the variables that might get re-expanded after TLS SNI; +that's tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */ -Arguments: - plist the appropriate priority list - psize the length of the list - s the configuation string - index the index of recognized strings - isize the length of the index +DEBUG(D_tls) + debug_printf("Expanding various TLS configuration options for session credentials.\n"); +rc = tls_expand_session_files(state); +if (rc != OK) return rc; +/* These are all other parts of the x509_cred handling, since SNI in GnuTLS +requires a new structure afterwards. */ - which text for an error message +rc = tls_set_remaining_x509(state); +if (rc != OK) return rc; -Returns: FALSE if the table overflows, else TRUE -*/ +/* set SNI in client, only */ +if (host) + { + if (!expand_check_tlsvar(tls_sni)) + return DEFER; + if (state->exp_tls_sni && *state->exp_tls_sni) + { + DEBUG(D_tls) + debug_printf("Setting TLS client SNI to \"%s\"\n", state->exp_tls_sni); + sz = Ustrlen(state->exp_tls_sni); + rc = gnutls_server_name_set(state->session, + GNUTLS_NAME_DNS, state->exp_tls_sni, sz); + exim_gnutls_err_check(US"gnutls_server_name_set"); + } + } +else if (state->tls_sni) + DEBUG(D_tls) debug_printf("*** PROBABLY A BUG *** " \ + "have an SNI set for a client [%s]\n", state->tls_sni); -static BOOL -set_priority(int *plist, int psize, uschar *s, pri_item *index, int isize, - uschar *which) -{ -int sep = 0; -BOOL first = TRUE; -uschar *t; +/* This is the priority string support, +http://www.gnu.org/software/gnutls/manual/html_node/Priority-Strings.html +and replaces gnutls_require_kx, gnutls_require_mac & gnutls_require_protocols. +This was backwards incompatible, but means Exim no longer needs to track +all algorithms and provide string forms for them. */ + +want_default_priorities = TRUE; -while ((t = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) != NULL) +if (state->tls_require_ciphers && *state->tls_require_ciphers) { - int i; - BOOL exclude = t[0] == '!'; - if (first && !exclude) plist[0] = 0; - first = FALSE; - for (i = 0; i < isize; i++) + if (!expand_check_tlsvar(tls_require_ciphers)) + return DEFER; + if (state->exp_tls_require_ciphers && *state->exp_tls_require_ciphers) { - uschar *ss = strstric(t, index[i].name, FALSE); - if (ss != NULL) - { - uschar *endss = ss + Ustrlen(index[i].name); - if ((ss == t || !isalnum(ss[-1])) && !isalnum(*endss)) - { - if (exclude) - remove_priority(plist, index[i].values); - else - { - if (!add_priority(plist, psize, index[i].values)) - { - log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: %s " - "priority table overflow", which); - return FALSE; - } - } - } - } + DEBUG(D_tls) debug_printf("GnuTLS session cipher/priority \"%s\"\n", + state->exp_tls_require_ciphers); + + rc = gnutls_priority_init(&state->priority_cache, + CS state->exp_tls_require_ciphers, &errpos); + want_default_priorities = FALSE; + p = state->exp_tls_require_ciphers; } } +if (want_default_priorities) + { + DEBUG(D_tls) + debug_printf("GnuTLS using default session cipher/priority \"%s\"\n", + exim_default_gnutls_priority); + rc = gnutls_priority_init(&state->priority_cache, + exim_default_gnutls_priority, &errpos); + p = US exim_default_gnutls_priority; + } -DEBUG(D_tls) +exim_gnutls_err_check(string_sprintf( + "gnutls_priority_init(%s) failed at offset %ld, \"%.6s..\"", + p, errpos - CS p, errpos)); + +rc = gnutls_priority_set(state->session, state->priority_cache); +exim_gnutls_err_check(US"gnutls_priority_set"); + +gnutls_db_set_cache_expiration(state->session, ssl_session_timeout); + +/* Reduce security in favour of increased compatibility, if the admin +decides to make that trade-off. */ +if (gnutls_compat_mode) { - int *ptr = plist; - debug_printf("adjusted %s priorities:", which); - while (*ptr != 0) debug_printf(" %d", *ptr++); - debug_printf("\n"); +#if LIBGNUTLS_VERSION_NUMBER >= 0x020104 + DEBUG(D_tls) debug_printf("lowering GnuTLS security, compatibility mode\n"); + gnutls_session_enable_compatibility_mode(state->session); +#else + DEBUG(D_tls) debug_printf("Unable to set gnutls_compat_mode - GnuTLS version too old\n"); +#endif } -return TRUE; +*caller_state = state; +/* needs to happen before callbacks during handshake */ +current_global_tls_state = state; +return OK; } /************************************************* -* Initialize a single GNUTLS session * +* Extract peer information * *************************************************/ -/* Set the algorithm, the db backend, whether to request certificates etc. +/* Called from both server and client code. +Only this is allowed to set state->peerdn and state->have_set_peerdn +and we use that to detect double-calls. -TLS in Exim was first implemented using OpenSSL. This has a function to which -you pass a list of cipher suites that are permitted/not permitted. GnuTLS works -differently. It operates using priority lists for the different components of -cipher suites. +NOTE: the state blocks last while the TLS connection is up, which is fine +for logging in the server side, but for the client side, we log after teardown +in src/deliver.c. While the session is up, we can twist about states and +repoint tls_* globals, but those variables used for logging or other variable +expansion that happens _after_ delivery need to have a longer life-time. -For compatibility of configuration, we scan a list of cipher suites and set -priorities therefrom. However, at the moment, we pay attention only to the bulk -cipher. +So for those, we get the data from POOL_PERM; the re-invoke guard keeps us from +doing this more than once per generation of a state context. We set them in +the state context, and repoint tls_* to them. After the state goes away, the +tls_* copies of the pointers remain valid and client delivery logging is happy. + +tls_certificate_verified is a BOOL, so the tls_peerdn and tls_cipher issues +don't apply. Arguments: - side one of GNUTLS_SERVER, GNUTLS_CLIENT - expciphers expanded ciphers list or NULL - expmac expanded MAC list or NULL - expkx expanded key-exchange list or NULL - expproto expanded protocol list or NULL + state exim_gnutls_state_st * -Returns: a gnutls_session, or NULL if there is a problem +Returns: OK/DEFER/FAIL */ -static gnutls_session -tls_session_init(int side, uschar *expciphers, uschar *expmac, uschar *expkx, - uschar *expproto) +static int +peer_status(exim_gnutls_state_st *state) { -gnutls_session session; - -gnutls_init(&session, side); +uschar cipherbuf[256]; +const gnutls_datum *cert_list; +int old_pool, rc; +unsigned int cert_list_size = 0; +gnutls_protocol_t protocol; +gnutls_cipher_algorithm_t cipher; +gnutls_kx_algorithm_t kx; +gnutls_mac_algorithm_t mac; +gnutls_certificate_type_t ct; +gnutls_x509_crt_t crt; +uschar *p, *dn_buf; +size_t sz; + +if (state->have_set_peerdn) + return OK; +state->have_set_peerdn = TRUE; + +state->peerdn = NULL; + +/* tls_cipher */ +cipher = gnutls_cipher_get(state->session); +protocol = gnutls_protocol_get_version(state->session); +mac = gnutls_mac_get(state->session); +kx = gnutls_kx_get(state->session); + +string_format(cipherbuf, sizeof(cipherbuf), + "%s:%s:%d", + gnutls_protocol_get_name(protocol), + gnutls_cipher_suite_get_name(kx, cipher, mac), + (int) gnutls_cipher_get_key_size(cipher) * 8); + +/* I don't see a way that spaces could occur, in the current GnuTLS +code base, but it was a concern in the old code and perhaps older GnuTLS +releases did return "TLS 1.0"; play it safe, just in case. */ +for (p = cipherbuf; *p != '\0'; ++p) + if (isspace(*p)) + *p = '-'; +old_pool = store_pool; +store_pool = POOL_PERM; +state->ciphersuite = string_copy(cipherbuf); +store_pool = old_pool; +tls_cipher = state->ciphersuite; + +/* tls_peerdn */ +cert_list = gnutls_certificate_get_peers(state->session, &cert_list_size); + +if (cert_list == NULL || cert_list_size == 0) + { + DEBUG(D_tls) debug_printf("TLS: no certificate from peer (%p & %d)\n", + cert_list, cert_list_size); + if (state->verify_requirement == VERIFY_REQUIRED) + return tls_error(US"certificate verification failed", + "no certificate received from peer", state->host); + return OK; + } -/* Initialize the lists of permitted protocols, key-exchange methods, ciphers, -and MACs. */ +ct = gnutls_certificate_type_get(state->session); +if (ct != GNUTLS_CRT_X509) + { + const char *ctn = gnutls_certificate_type_get_name(ct); + DEBUG(D_tls) + debug_printf("TLS: peer cert not X.509 but instead \"%s\"\n", ctn); + if (state->verify_requirement == VERIFY_REQUIRED) + return tls_error(US"certificate verification not possible, unhandled type", + ctn, state->host); + return OK; + } -memcpy(cipher_priority, default_cipher_priority, sizeof(cipher_priority)); -memcpy(mac_priority, default_mac_priority, sizeof(mac_priority)); -memcpy(kx_priority, default_kx_priority, sizeof(kx_priority)); -memcpy(proto_priority, default_proto_priority, sizeof(proto_priority)); +#define exim_gnutls_peer_err(Label) do { \ + if (rc != GNUTLS_E_SUCCESS) { \ + DEBUG(D_tls) debug_printf("TLS: peer cert problem: %s: %s\n", (Label), gnutls_strerror(rc)); \ + if (state->verify_requirement == VERIFY_REQUIRED) { return tls_error((Label), gnutls_strerror(rc), state->host); } \ + return OK; } } while (0) -/* The names OpenSSL uses in tls_require_ciphers are of the form DES-CBC3-SHA, -using hyphen separators. GnuTLS uses underscore separators. So that I can use -either form for tls_require_ciphers in my tests, and also for general -convenience, we turn hyphens into underscores before scanning the list. */ +rc = gnutls_x509_crt_init(&crt); +exim_gnutls_peer_err(US"gnutls_x509_crt_init (crt)"); -if (expciphers != NULL) +rc = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER); +exim_gnutls_peer_err(US"failed to import certificate [gnutls_x509_crt_import(cert 0)]"); +sz = 0; +rc = gnutls_x509_crt_get_dn(crt, NULL, &sz); +if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) { - uschar *s = expciphers; - while (*s != 0) { if (*s == '-') *s = '_'; s++; } + exim_gnutls_peer_err(US"getting size for cert DN failed"); + return FAIL; /* should not happen */ } +dn_buf = store_get_perm(sz); +rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz); +exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]"); +state->peerdn = dn_buf; -if ((expciphers != NULL && - !set_priority(cipher_priority, sizeof(cipher_priority)/sizeof(int), - expciphers, cipher_index, sizeof(cipher_index)/sizeof(pri_item), - US"cipher")) || - (expmac != NULL && - !set_priority(mac_priority, sizeof(mac_priority)/sizeof(int), - expmac, mac_index, sizeof(mac_index)/sizeof(pri_item), - US"MAC")) || - (expkx != NULL && - !set_priority(kx_priority, sizeof(kx_priority)/sizeof(int), - expkx, kx_index, sizeof(kx_index)/sizeof(pri_item), - US"key-exchange")) || - (expproto != NULL && - !set_priority(proto_priority, sizeof(proto_priority)/sizeof(int), - expproto, proto_index, sizeof(proto_index)/sizeof(pri_item), - US"protocol"))) - { - gnutls_deinit(session); - return NULL; - } +return OK; +#undef exim_gnutls_peer_err +} -/* Define the various priorities */ -gnutls_cipher_set_priority(session, cipher_priority); -gnutls_compression_set_priority(session, comp_priority); -gnutls_kx_set_priority(session, kx_priority); -gnutls_protocol_set_priority(session, proto_priority); -gnutls_mac_set_priority(session, mac_priority); -gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); -gnutls_dh_set_prime_bits(session, DH_BITS); +/************************************************* +* Verify peer certificate * +*************************************************/ -/* Request or demand a certificate of the peer, as configured. This will -happen only in a server. */ +/* Called from both server and client code. +*Should* be using a callback registered with +gnutls_certificate_set_verify_function() to fail the handshake if we dislike +the peer information, but that's too new for some OSes. -if (verify_requirement != VERIFY_NONE) - gnutls_certificate_server_set_request(session, - (verify_requirement == VERIFY_OPTIONAL)? - GNUTLS_CERT_REQUEST : GNUTLS_CERT_REQUIRE); +Arguments: + state exim_gnutls_state_st * + error where to put an error message -gnutls_db_set_cache_expiration(session, ssl_session_timeout); +Returns: + FALSE if the session should be rejected + TRUE if the cert is okay or we just don't care +*/ -/* Reduce security in favour of increased compatibility, if the admin -decides to make that trade-off. */ -if (gnutls_compat_mode) +static BOOL +verify_certificate(exim_gnutls_state_st *state, const char **error) +{ +int rc; +unsigned int verify; + +*error = NULL; + +rc = peer_status(state); +if (rc != OK) { -#if LIBGNUTLS_VERSION_NUMBER >= 0x020104 - DEBUG(D_tls) debug_printf("lowering GnuTLS security, compatibility mode\n"); - gnutls_session_enable_compatibility_mode(session); -#else - DEBUG(D_tls) debug_printf("Unable to set gnutls_compat_mode - GnuTLS version too old\n"); -#endif + verify = GNUTLS_CERT_INVALID; + *error = "not supplied"; + } +else + { + rc = gnutls_certificate_verify_peers2(state->session, &verify); } -DEBUG(D_tls) debug_printf("initialized GnuTLS session\n"); -return session; -} +/* Handle the result of verification. INVALID seems to be set as well +as REVOKED, but leave the test for both. */ +if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) + { + state->peer_cert_verified = FALSE; + if (*error == NULL) + *error = ((verify & GNUTLS_CERT_REVOKED) != 0) ? "revoked" : "invalid"; + DEBUG(D_tls) + debug_printf("TLS certificate verification failed (%s): peerdn=%s\n", + *error, state->peerdn ? state->peerdn : US"<unset>"); + + if (state->verify_requirement == VERIFY_REQUIRED) + { + gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); + return FALSE; + } + DEBUG(D_tls) + debug_printf("TLS verify failure overridden (host in tls_try_verify_hosts)\n"); + } +else + { + state->peer_cert_verified = TRUE; + DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", + state->peerdn ? state->peerdn : US"<unset>"); + } + +tls_peerdn = state->peerdn; + +return TRUE; +} -/************************************************* -* Get name of cipher in use * -*************************************************/ -/* The answer is left in a static buffer, and tls_cipher is set to point -to it. -Argument: pointer to a GnuTLS session -Returns: nothing -*/ +/* ------------------------------------------------------------------------ */ +/* Callbacks */ + +/* Logging function which can be registered with + * gnutls_global_set_log_function() + * gnutls_global_set_log_level() 0..9 + */ +#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0 static void -construct_cipher_name(gnutls_session session) +exim_gnutls_logger_cb(int level, const char *message) { -static uschar cipherbuf[256]; -uschar *ver; -int c, kx, mac; -#ifdef GNUTLS_CB_TLS_UNIQUE -int rc; -gnutls_datum_t channel; + size_t len = strlen(message); + if (len < 1) + { + DEBUG(D_tls) debug_printf("GnuTLS<%d> empty debug message\n", level); + return; + } + DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s%s", level, message, + message[len-1] == '\n' ? "" : "\n"); +} #endif -ver = string_copy( - US gnutls_protocol_get_name(gnutls_protocol_get_version(session))); -if (Ustrncmp(ver, "TLS ", 4) == 0) ver[3] = '-'; /* Don't want space */ -c = gnutls_cipher_get(session); -/* returns size in "bytes" */ -tls_bits = gnutls_cipher_get_key_size(c) * 8; +/* Called after client hello, should handle SNI work. +This will always set tls_sni (state->received_sni) if available, +and may trigger presenting different certificates, +if state->trigger_sni_changes is TRUE. + +Should be registered with + gnutls_handshake_set_post_client_hello_function() -mac = gnutls_mac_get(session); -kx = gnutls_kx_get(session); +"This callback must return 0 on success or a gnutls error code to terminate the +handshake.". -string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver, - gnutls_cipher_suite_get_name(kx, c, mac), tls_bits); -tls_cipher = cipherbuf; +For inability to get SNI information, we return 0. +We only return non-zero if re-setup failed. +*/ -DEBUG(D_tls) debug_printf("cipher: %s\n", cipherbuf); +static int +exim_sni_handling_cb(gnutls_session_t session) +{ +char sni_name[MAX_HOST_LEN]; +size_t data_len = MAX_HOST_LEN; +exim_gnutls_state_st *state = current_global_tls_state; +unsigned int sni_type; +int rc, old_pool; + +rc = gnutls_server_name_get(session, sni_name, &data_len, &sni_type, 0); +if (rc != GNUTLS_E_SUCCESS) + { + DEBUG(D_tls) { + if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + debug_printf("TLS: no SNI presented in handshake.\n"); + else + debug_printf("TLS failure: gnutls_server_name_get(): %s [%d]\n", + gnutls_strerror(rc), rc); + }; + return 0; + } -if (tls_channelbinding_b64) - free(tls_channelbinding_b64); -tls_channelbinding_b64 = NULL; +if (sni_type != GNUTLS_NAME_DNS) + { + DEBUG(D_tls) debug_printf("TLS: ignoring SNI of unhandled type %u\n", sni_type); + return 0; + } -#ifdef GNUTLS_CB_TLS_UNIQUE -channel = { NULL, 0 }; -rc = gnutls_session_channel_binding(session, GNUTLS_CB_TLS_UNIQUE, &channel); -if (rc) { - DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); -} else { - tls_channelbinding_b64 = auth_b64encode(channel.data, (int)channel.size); - DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n"); -} -#endif +/* We now have a UTF-8 string in sni_name */ +old_pool = store_pool; +store_pool = POOL_PERM; +state->received_sni = string_copyn(US sni_name, data_len); +store_pool = old_pool; + +/* We set this one now so that variable expansions below will work */ +tls_sni = state->received_sni; + +DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", sni_name, + state->trigger_sni_changes ? "" : " (unused for certificate selection)"); + +if (!state->trigger_sni_changes) + return 0; + +rc = tls_expand_session_files(state); +if (rc != OK) + { + /* If the setup of certs/etc failed before handshake, TLS would not have + been offered. The best we can do now is abort. */ + return GNUTLS_E_APPLICATION_ERROR_MIN; + } + +rc = tls_set_remaining_x509(state); +if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN; + +return 0; } + +/* ------------------------------------------------------------------------ */ +/* Exported functions */ + + + + /************************************************* * Start a TLS session in a server * *************************************************/ @@ -905,9 +1362,6 @@ a TLS session. Arguments: require_ciphers list of allowed ciphers or NULL - require_mac list of allowed MACs or NULL - require_kx list of allowed key_exchange methods or NULL - require_proto list of allowed protocols or NULL Returns: OK on success DEFER for errors before the start of the negotiation @@ -916,21 +1370,18 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers, uschar *require_mac, - uschar *require_kx, uschar *require_proto) +tls_server_start(const uschar *require_ciphers) { int rc; const char *error; -uschar *expciphers = NULL; -uschar *expmac = NULL; -uschar *expkx = NULL; -uschar *expproto = NULL; +exim_gnutls_state_st *state = NULL; /* Check for previous activation */ +/* nb: this will not be TLS callout safe, needs reworking as part of that. */ if (tls_active >= 0) { - tls_error("STARTTLS received after TLS started", NULL, ""); + tls_error(US"STARTTLS received after TLS started", "", NULL); smtp_printf("554 Already in TLS\r\n"); return FAIL; } @@ -938,36 +1389,40 @@ if (tls_active >= 0) /* Initialize the library. If it fails, it will already have logged the error and sent an SMTP response. */ -DEBUG(D_tls) debug_printf("initializing GnuTLS as a server\n"); +DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n"); -rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates, - tls_crl); +rc = tls_init(NULL, tls_certificate, tls_privatekey, + NULL, tls_verify_certificates, tls_crl, + require_ciphers, &state); if (rc != OK) return rc; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || - !expand_check(require_mac, US"gnutls_require_mac", &expmac) || - !expand_check(require_kx, US"gnutls_require_kx", &expkx) || - !expand_check(require_proto, US"gnutls_require_proto", &expproto)) - return FAIL; - /* If this is a host for which certificate verification is mandatory or optional, set up appropriately. */ -tls_certificate_verified = FALSE; -verify_requirement = VERIFY_NONE; - if (verify_check_host(&tls_verify_hosts) == OK) - verify_requirement = VERIFY_REQUIRED; + { + DEBUG(D_tls) debug_printf("TLS: a client certificate will be required.\n"); + state->verify_requirement = VERIFY_REQUIRED; + gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE); + } else if (verify_check_host(&tls_try_verify_hosts) == OK) - verify_requirement = VERIFY_OPTIONAL; + { + DEBUG(D_tls) debug_printf("TLS: a client certificate will be requested but not required.\n"); + state->verify_requirement = VERIFY_OPTIONAL; + gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUEST); + } +else + { + DEBUG(D_tls) debug_printf("TLS: a client certificate will not be requested.\n"); + state->verify_requirement = VERIFY_NONE; + gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE); + } -/* Prepare for new connection */ +/* Register SNI handling; always, even if not in tls_certificate, so that the +expansion variable $tls_sni is always available. */ -tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx, - expproto); -if (tls_session == NULL) - return tls_error(US"tls_session_init", NULL, - gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); +gnutls_handshake_set_post_client_hello_function(state->session, + exim_sni_handling_cb); /* Set context and tell client to go ahead, except in the case of TLS startup on connection, where outputting anything now upsets the clients and tends to @@ -984,19 +1439,25 @@ if (!tls_on_connect) /* Now negotiate the TLS session. We put our own timer on it, since it seems that the GnuTLS library doesn't. */ -gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr)fileno(smtp_in), - (gnutls_transport_ptr)fileno(smtp_out)); +gnutls_transport_set_ptr2(state->session, + (gnutls_transport_ptr)fileno(smtp_in), + (gnutls_transport_ptr)fileno(smtp_out)); +state->fd_in = fileno(smtp_in); +state->fd_out = fileno(smtp_out); sigalrm_seen = FALSE; if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); -rc = gnutls_handshake(tls_session); +do + { + rc = gnutls_handshake(state->session); + } while ((rc == GNUTLS_E_AGAIN) || + (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen)); alarm(0); -if (rc < 0) +if (rc != GNUTLS_E_SUCCESS) { - tls_error(US"gnutls_handshake", NULL, - sigalrm_seen ? "timed out" : gnutls_strerror(rc)); - + tls_error(US"gnutls_handshake", + sigalrm_seen ? "timed out" : gnutls_strerror(rc), NULL); /* It seems that, except in the case of a timeout, we have to close the connection right here; otherwise if the other end is running OpenSSL it hangs until the server times out. */ @@ -1012,21 +1473,39 @@ if (rc < 0) DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); -if (verify_requirement != VERIFY_NONE && - !verify_certificate(tls_session, &error)) +/* Verify after the fact */ + +if (state->verify_requirement != VERIFY_NONE) { - tls_error(US"certificate verification failed", NULL, error); - return FAIL; + if (!verify_certificate(state, &error)) + { + if (state->verify_requirement == VERIFY_OPTIONAL) + { + DEBUG(D_tls) + debug_printf("TLS: continuing on only because verification was optional, after: %s\n", + error); + } + else + { + tls_error(US"certificate verification failed", error, NULL); + return FAIL; + } + } } -construct_cipher_name(tls_session); +/* Figure out peer DN, and if authenticated, etc. */ + +rc = peer_status(state); +if (rc != OK) return rc; + +/* Sets various Exim expansion variables; always safe within server */ + +extract_exim_vars_from_tls_state(state); /* TLS has been set up. Adjust the input functions to read via TLS, and initialize appropriately. */ -ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size); -ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0; -ssl_xfer_eof = ssl_xfer_error = 0; +state->xfer_buffer = store_malloc(ssl_xfer_buffer_size); receive_getc = tls_getc; receive_ungetc = tls_ungetc; @@ -1034,8 +1513,6 @@ receive_feof = tls_feof; receive_ferror = tls_ferror; receive_smtp_buffered = tls_smtp_buffered; -tls_active = fileno(smtp_out); - return OK; } @@ -1052,16 +1529,13 @@ Arguments: fd the fd of the connection host connected host (for messages) addr the first address (not used) - dhparam DH parameter file + dhparam DH parameter file (ignored, we're a client) certificate certificate file privatekey private key file sni TLS SNI to send to remote host verify_certs file for certificate verify verify_crl CRL for verify require_ciphers list of allowed ciphers or NULL - require_mac list of allowed MACs or NULL - require_kx list of allowed key_exchange methods or NULL - require_proto list of allowed protocols or NULL timeout startup timeout Returns: OK/DEFER/FAIL (because using common functions), @@ -1069,114 +1543,121 @@ Returns: OK/DEFER/FAIL (because using common functions), */ int -tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, - uschar *certificate, uschar *privatekey, uschar *sni ARG_UNUSED, - uschar *verify_certs, uschar *verify_crl, - uschar *require_ciphers, uschar *require_mac, - uschar *require_kx, uschar *require_proto, int timeout) +tls_client_start(int fd, host_item *host, + address_item *addr ARG_UNUSED, uschar *dhparam ARG_UNUSED, + uschar *certificate, uschar *privatekey, uschar *sni, + uschar *verify_certs, uschar *verify_crl, + uschar *require_ciphers, int timeout) { -const gnutls_datum *server_certs; -uschar *expciphers = NULL; -uschar *expmac = NULL; -uschar *expkx = NULL; -uschar *expproto = NULL; -const char *error; -unsigned int server_certs_size; int rc; +const char *error; +exim_gnutls_state_st *state = NULL; -DEBUG(D_tls) debug_printf("initializing GnuTLS as a client\n"); +DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd); -verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED; -rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl); +rc = tls_init(host, certificate, privatekey, + sni, verify_certs, verify_crl, require_ciphers, &state); if (rc != OK) return rc; -if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || - !expand_check(require_mac, US"gnutls_require_mac", &expmac) || - !expand_check(require_kx, US"gnutls_require_kx", &expkx) || - !expand_check(require_proto, US"gnutls_require_proto", &expproto)) - return FAIL; - -tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx, - expproto); +gnutls_dh_set_prime_bits(state->session, EXIM_CLIENT_DH_MIN_BITS); -if (tls_session == NULL) - return tls_error(US "tls_session_init", host, - gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); +if (verify_certs == NULL) + { + DEBUG(D_tls) debug_printf("TLS: server certificate verification not required\n"); + state->verify_requirement = VERIFY_NONE; + /* we still ask for it, to log it, etc */ + gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUEST); + } +else + { + DEBUG(D_tls) debug_printf("TLS: server certificate verification required\n"); + state->verify_requirement = VERIFY_REQUIRED; + gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE); + } -gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fd); +gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd); +state->fd_in = fd; +state->fd_out = fd; /* There doesn't seem to be a built-in timeout on connection. */ sigalrm_seen = FALSE; alarm(timeout); -rc = gnutls_handshake(tls_session); +do + { + rc = gnutls_handshake(state->session); + } while ((rc == GNUTLS_E_AGAIN) || + (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen)); alarm(0); -if (rc < 0) - return tls_error(US "gnutls_handshake", host, - sigalrm_seen ? "timed out" : gnutls_strerror(rc)); +if (rc != GNUTLS_E_SUCCESS) + return tls_error(US"gnutls_handshake", + sigalrm_seen ? "timed out" : gnutls_strerror(rc), state->host); -server_certs = gnutls_certificate_get_peers(tls_session, &server_certs_size); +DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); -if (server_certs != NULL) - { - uschar buff[1024]; - gnutls_x509_crt gcert; +/* Verify late */ - gnutls_x509_crt_init(&gcert); - tls_peerdn = US"unknown"; +if (state->verify_requirement != VERIFY_NONE && + !verify_certificate(state, &error)) + return tls_error(US"certificate verification failed", error, state->host); - if (gnutls_x509_crt_import(gcert, server_certs, GNUTLS_X509_FMT_DER) == 0) - { - size_t bufsize = sizeof(buff); - if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) - tls_peerdn = string_copy_malloc(buff); - } - } +/* Figure out peer DN, and if authenticated, etc. */ + +rc = peer_status(state); +if (rc != OK) return rc; -/* Should we also verify the hostname here? */ +/* Sets various Exim expansion variables; may need to adjust for ACL callouts */ -if (verify_requirement != VERIFY_NONE && - !verify_certificate(tls_session, &error)) - return tls_error(US"certificate verification failed", host, error); +extract_exim_vars_from_tls_state(state); -construct_cipher_name(tls_session); /* Sets tls_cipher */ -tls_active = fd; return OK; } + /************************************************* -* Deal with logging errors during I/O * +* Close down a TLS session * *************************************************/ -/* We have to get the identity of the peer from saved data. - -Argument: - ec the GnuTLS error code, or 0 if it's a local error - when text identifying read or write - text local error text when ec is 0 +/* This is also called from within a delivery subprocess forked from the +daemon, to shut down the TLS library, without actually doing a shutdown (which +would tamper with the TLS session in the parent process). -Returns: nothing +Arguments: TRUE if gnutls_bye is to be called +Returns: nothing */ -static void -record_io_error(int ec, uschar *when, uschar *text) +void +tls_close(BOOL shutdown) { -const char *msg; +exim_gnutls_state_st *state = current_global_tls_state; -if (ec == GNUTLS_E_FATAL_ALERT_RECEIVED) - msg = string_sprintf("%s: %s", gnutls_strerror(ec), - gnutls_alert_get_name(gnutls_alert_get(tls_session))); -else - msg = gnutls_strerror(ec); +if (tls_active < 0) return; /* TLS was not active */ + +if (shutdown) + { + DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS\n"); + gnutls_bye(state->session, GNUTLS_SHUT_WR); + } -tls_error(when, client_host, msg); +gnutls_deinit(state->session); + +memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); + +if ((state_server.session == NULL) && (state_client.session == NULL)) + { + gnutls_global_deinit(); + exim_gnutls_base_init_done = FALSE; + } + +tls_active = -1; } + /************************************************* * TLS version of getc * *************************************************/ @@ -1184,6 +1665,8 @@ tls_error(when, client_host, msg); /* This gets the next byte from the TLS input buffer. If the buffer is empty, it refills the buffer via the GnuTLS reading function. +This feeds DKIM and should be used for all message-body reads. + Arguments: none Returns: the next character or EOF */ @@ -1191,15 +1674,16 @@ Returns: the next character or EOF int tls_getc(void) { -if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) +exim_gnutls_state_st *state = current_global_tls_state; +if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm) { - int inbytes; + ssize_t inbytes; - DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%lx, %lx, %u)\n", - (long) tls_session, (long) ssl_xfer_buffer, ssl_xfer_buffer_size); + DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%p, %p, %u)\n", + state->session, state->xfer_buffer, ssl_xfer_buffer_size); if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); - inbytes = gnutls_record_recv(tls_session, CS ssl_xfer_buffer, + inbytes = gnutls_record_recv(state->session, state->xfer_buffer, ssl_xfer_buffer_size); alarm(0); @@ -1217,9 +1701,12 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) receive_ferror = smtp_ferror; receive_smtp_buffered = smtp_buffered; - gnutls_deinit(tls_session); - tls_session = NULL; + gnutls_deinit(state->session); + state->session = NULL; tls_active = -1; + tls_bits = 0; + tls_certificate_verified = FALSE; + tls_channelbinding_b64 = NULL; tls_cipher = NULL; tls_peerdn = NULL; @@ -1230,30 +1717,31 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) else if (inbytes < 0) { - record_io_error(inbytes, US"recv", NULL); - ssl_xfer_error = 1; + record_io_error(state, (int) inbytes, US"recv", NULL); + state->xfer_error = 1; return EOF; } #ifndef DISABLE_DKIM - dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); + dkim_exim_verify_feed(state->xfer_buffer, inbytes); #endif - ssl_xfer_buffer_hwm = inbytes; - ssl_xfer_buffer_lwm = 0; + state->xfer_buffer_hwm = (int) inbytes; + state->xfer_buffer_lwm = 0; } - /* Something in the buffer; return next uschar */ -return ssl_xfer_buffer[ssl_xfer_buffer_lwm++]; +return state->xfer_buffer[state->xfer_buffer_lwm++]; } + /************************************************* * Read bytes from TLS channel * *************************************************/ -/* +/* This does not feed DKIM, so if the caller uses this for reading message body, +then the caller must feed DKIM. Arguments: buff buffer of data len size of buffer @@ -1265,24 +1753,36 @@ Returns: the number of bytes read int tls_read(uschar *buff, size_t len) { -int inbytes; +exim_gnutls_state_st *state = current_global_tls_state; +ssize_t inbytes; + +if (len > INT_MAX) + len = INT_MAX; + +if (state->xfer_buffer_lwm < state->xfer_buffer_hwm) + DEBUG(D_tls) + debug_printf("*** PROBABLY A BUG *** " \ + "tls_read() called with data in the tls_getc() buffer, %d ignored\n", + state->xfer_buffer_hwm - state->xfer_buffer_lwm); -DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%lx, %lx, %u)\n", - (long) tls_session, (long) buff, len); +DEBUG(D_tls) + debug_printf("Calling gnutls_record_recv(%p, %p, " SIZE_T_FMT ")\n", + state->session, buff, len); -inbytes = gnutls_record_recv(tls_session, CS buff, len); +inbytes = gnutls_record_recv(state->session, buff, len); if (inbytes > 0) return inbytes; if (inbytes == 0) { DEBUG(D_tls) debug_printf("Got TLS_EOF\n"); } -else record_io_error(inbytes, US"recv", NULL); +else record_io_error(state, (int)inbytes, US"recv", NULL); return -1; } + /************************************************* * Write bytes down TLS channel * *************************************************/ @@ -1299,25 +1799,26 @@ Returns: the number of bytes after a successful write, int tls_write(const uschar *buff, size_t len) { -int outbytes; -int left = len; +ssize_t outbytes; +size_t left = len; +exim_gnutls_state_st *state = current_global_tls_state; -DEBUG(D_tls) debug_printf("tls_do_write(%lx, %d)\n", (long) buff, left); +DEBUG(D_tls) debug_printf("tls_do_write(%p, " SIZE_T_FMT ")\n", buff, left); while (left > 0) { - DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %lx, %d)\n", (long)buff, - left); - outbytes = gnutls_record_send(tls_session, CS buff, left); + DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %p, " SIZE_T_FMT ")\n", + buff, left); + outbytes = gnutls_record_send(state->session, buff, left); - DEBUG(D_tls) debug_printf("outbytes=%d\n", outbytes); + DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes); if (outbytes < 0) { - record_io_error(outbytes, US"send", NULL); + record_io_error(state, outbytes, US"send", NULL); return -1; } if (outbytes == 0) { - record_io_error(0, US"send", US"TLS channel closed on write"); + record_io_error(state, 0, US"send", US"TLS channel closed on write"); return -1; } @@ -1325,39 +1826,137 @@ while (left > 0) buff += outbytes; } -return len; +if (len > INT_MAX) + { + DEBUG(D_tls) + debug_printf("Whoops! Wrote more bytes (" SIZE_T_FMT ") than INT_MAX\n", + len); + len = INT_MAX; + } + +return (int) len; } + /************************************************* -* Close down a TLS session * +* Random number generation * *************************************************/ -/* This is also called from within a delivery subprocess forked from the -daemon, to shut down the TLS library, without actually doing a shutdown (which -would tamper with the TLS session in the parent process). +/* Pseudo-random number generation. The result is not expected to be +cryptographically strong but not so weak that someone will shoot themselves +in the foot using it as a nonce in input in some email header scheme or +whatever weirdness they'll twist this into. The result should handle fork() +and avoid repeating sequences. OpenSSL handles that for us. -Arguments: TRUE if gnutls_bye is to be called -Returns: nothing +Arguments: + max range maximum +Returns a random number in range [0, max-1] */ -void -tls_close(BOOL shutdown) +#ifdef HAVE_GNUTLS_RND +int +vaguely_random_number(int max) { -if (tls_active < 0) return; /* TLS was not active */ - -if (shutdown) +unsigned int r; +int i, needed_len; +uschar *p; +uschar smallbuf[sizeof(r)]; + +if (max <= 1) + return 0; + +needed_len = sizeof(r); +/* Don't take 8 times more entropy than needed if int is 8 octets and we were + * asked for a number less than 10. */ +for (r = max, i = 0; r; ++i) + r >>= 1; +i = (i + 7) / 8; +if (i < needed_len) + needed_len = i; + +i = gnutls_rnd(GNUTLS_RND_NONCE, smallbuf, needed_len); +if (i < 0) { - DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS\n"); - gnutls_bye(tls_session, GNUTLS_SHUT_WR); + DEBUG(D_all) debug_printf("gnutls_rnd() failed, using fallback.\n"); + return vaguely_random_number_fallback(max); } +r = 0; +for (p = smallbuf; needed_len; --needed_len, ++p) + { + r *= 256; + r += *p; + } + +/* We don't particularly care about weighted results; if someone wants + * smooth distribution and cares enough then they should submit a patch then. */ +return r % max; +} +#else /* HAVE_GNUTLS_RND */ +int +vaguely_random_number(int max) +{ + return vaguely_random_number_fallback(max); +} +#endif /* HAVE_GNUTLS_RND */ -gnutls_deinit(tls_session); -tls_session = NULL; + + + +/************************************************* +* Let tls_require_ciphers be checked at startup * +*************************************************/ + +/* The tls_require_ciphers option, if set, must be something which the +library can parse. + +Returns: NULL on success, or error message +*/ + +uschar * +tls_validate_require_cipher(void) +{ +int rc; +uschar *expciphers = NULL; +gnutls_priority_t priority_cache; +const char *errpos; + +#define validate_check_rc(Label) do { \ + if (rc != GNUTLS_E_SUCCESS) { if (exim_gnutls_base_init_done) gnutls_global_deinit(); \ + return string_sprintf("%s failed: %s", (Label), gnutls_strerror(rc)); } } while (0) +#define return_deinit(Label) do { gnutls_global_deinit(); return (Label); } while (0) + +if (exim_gnutls_base_init_done) + log_write(0, LOG_MAIN|LOG_PANIC, + "already initialised GnuTLS, Exim developer bug"); + +rc = gnutls_global_init(); +validate_check_rc(US"gnutls_global_init()"); +exim_gnutls_base_init_done = TRUE; + +if (!(tls_require_ciphers && *tls_require_ciphers)) + return_deinit(NULL); + +if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers)) + return_deinit(US"failed to expand tls_require_ciphers"); + +if (!(expciphers && *expciphers)) + return_deinit(NULL); + +DEBUG(D_tls) + debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers); + +rc = gnutls_priority_init(&priority_cache, CS expciphers, &errpos); +validate_check_rc(string_sprintf( + "gnutls_priority_init(%s) failed at offset %ld, \"%.8s..\"", + expciphers, errpos - CS expciphers, errpos)); + +#undef return_deinit +#undef validate_check_rc gnutls_global_deinit(); -tls_active = -1; +return NULL; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index ea32bdb40..22c0730c3 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides the TLS (aka SSL) support for Exim using the OpenSSL @@ -20,6 +20,18 @@ functions from the OpenSSL library. */ #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/rand.h> +#ifdef EXPERIMENTAL_OCSP +#include <openssl/ocsp.h> +#endif + +#ifdef EXPERIMENTAL_OCSP +#define EXIM_OCSP_SKEW_SECONDS (300L) +#define EXIM_OCSP_MAX_AGE (-1L) +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +#define EXIM_HAVE_OPENSSL_TLSEXT +#endif /* Structure for collecting random data for seeding. */ @@ -34,7 +46,9 @@ static BOOL verify_callback_called = FALSE; static const uschar *sid_ctx = US"exim"; static SSL_CTX *ctx = NULL; +#ifdef EXIM_HAVE_OPENSSL_TLSEXT static SSL_CTX *ctx_sni = NULL; +#endif static SSL *ssl = NULL; static char ssl_errstring[256]; @@ -48,6 +62,11 @@ static BOOL reexpand_tls_files_for_sni = FALSE; typedef struct tls_ext_ctx_cb { uschar *certificate; uschar *privatekey; +#ifdef EXPERIMENTAL_OCSP + uschar *ocsp_file; + uschar *ocsp_file_expanded; + OCSP_RESPONSE *ocsp_response; +#endif uschar *dhparam; /* these are cached from first expand */ uschar *server_cipher_list; @@ -63,6 +82,14 @@ tls_ext_ctx_cb *static_cbinfo = NULL; static int setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional); +/* Callbacks */ +#ifdef EXIM_HAVE_OPENSSL_TLSEXT +static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg); +#endif +#ifdef EXPERIMENTAL_OCSP +static int tls_stapling_cb(SSL *s, void *arg); +#endif + /************************************************* * Handle TLS error * @@ -248,52 +275,211 @@ DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s)); /* If dhparam is set, expand it, and load up the parameters for DH encryption. Arguments: - dhparam DH parameter file + dhparam DH parameter file or fixed parameter identity string host connected host, if client; NULL if server Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_dh(uschar *dhparam, host_item *host) +init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host) { -BOOL yield = TRUE; BIO *bio; DH *dh; uschar *dhexpanded; +const char *pem; if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded)) return FALSE; -if (dhexpanded == NULL) return TRUE; - -if ((bio = BIO_new_file(CS dhexpanded, "r")) == NULL) +if (dhexpanded == NULL || *dhexpanded == '\0') { - tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), - host, (uschar *)strerror(errno)); - yield = FALSE; + bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1); } -else +else if (dhexpanded[0] == '/') { - if ((dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)) == NULL) + bio = BIO_new_file(CS dhexpanded, "r"); + if (bio == NULL) { tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), - host, NULL); - yield = FALSE; + host, US strerror(errno)); + return FALSE; } - else + } +else + { + if (Ustrcmp(dhexpanded, "none") == 0) { - SSL_CTX_set_tmp_dh(ctx, dh); - DEBUG(D_tls) - debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n", - dhexpanded, 8*DH_size(dh)); - DH_free(dh); + DEBUG(D_tls) debug_printf("Requested no DH parameters.\n"); + return TRUE; } + + pem = std_dh_prime_named(dhexpanded); + if (!pem) + { + tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded), + host, US strerror(errno)); + return FALSE; + } + bio = BIO_new_mem_buf(CS pem, -1); + } + +dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); +if (dh == NULL) + { BIO_free(bio); + tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded), + host, NULL); + return FALSE; + } + +/* Even if it is larger, we silently return success rather than cause things + * to fail out, so that a too-large DH will not knock out all TLS; it's a + * debatable choice. */ +if ((8*DH_size(dh)) > tls_dh_max_bits) + { + DEBUG(D_tls) + debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d", + 8*DH_size(dh), tls_dh_max_bits); + } +else + { + SSL_CTX_set_tmp_dh(sctx, dh); + DEBUG(D_tls) + debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n", + dhexpanded ? dhexpanded : US"default", 8*DH_size(dh)); + } + +DH_free(dh); +BIO_free(bio); + +return TRUE; +} + + + + +#ifdef EXPERIMENTAL_OCSP +/************************************************* +* Load OCSP information into state * +*************************************************/ + +/* Called to load the OCSP response from the given file into memory, once +caller has determined this is needed. Checks validity. Debugs a message +if invalid. + +ASSUMES: single response, for single cert. + +Arguments: + sctx the SSL_CTX* to update + cbinfo various parts of session state + expanded the filename putatively holding an OCSP response + +*/ + +static void +ocsp_load_response(SSL_CTX *sctx, + tls_ext_ctx_cb *cbinfo, + const uschar *expanded) +{ +BIO *bio; +OCSP_RESPONSE *resp; +OCSP_BASICRESP *basic_response; +OCSP_SINGLERESP *single_response; +ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; +X509_STORE *store; +unsigned long verify_flags; +int status, reason, i; + +cbinfo->ocsp_file_expanded = string_copy(expanded); +if (cbinfo->ocsp_response) + { + OCSP_RESPONSE_free(cbinfo->ocsp_response); + cbinfo->ocsp_response = NULL; + } + +bio = BIO_new_file(CS cbinfo->ocsp_file_expanded, "rb"); +if (!bio) + { + DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n", + cbinfo->ocsp_file_expanded); + return; + } + +resp = d2i_OCSP_RESPONSE_bio(bio, NULL); +BIO_free(bio); +if (!resp) + { + DEBUG(D_tls) debug_printf("Error reading OCSP response.\n"); + return; + } + +status = OCSP_response_status(resp); +if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) + { + DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n", + OCSP_response_status_str(status), status); + return; } -return yield; +basic_response = OCSP_response_get1_basic(resp); +if (!basic_response) + { + DEBUG(D_tls) + debug_printf("OCSP response parse error: unable to extract basic response.\n"); + return; + } + +store = SSL_CTX_get_cert_store(sctx); +verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */ + +/* May need to expose ability to adjust those flags? +OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT +OCSP_TRUSTOTHER OCSP_NOINTERN */ + +i = OCSP_basic_verify(basic_response, NULL, store, verify_flags); +if (i <= 0) + { + DEBUG(D_tls) { + ERR_error_string(ERR_get_error(), ssl_errstring); + debug_printf("OCSP response verify failure: %s\n", US ssl_errstring); + } + return; + } + +/* Here's the simplifying assumption: there's only one response, for the +one certificate we use, and nothing for anything else in a chain. If this +proves false, we need to extract a cert id from our issued cert +(tls_certificate) and use that for OCSP_resp_find_status() (which finds the +right cert in the stack and then calls OCSP_single_get0_status()). + +I'm hoping to avoid reworking a bunch more of how we handle state here. */ +single_response = OCSP_resp_get0(basic_response, 0); +if (!single_response) + { + DEBUG(D_tls) + debug_printf("Unable to get first response from OCSP basic response.\n"); + return; + } + +status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd); +/* how does this status differ from the one above? */ +if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) + { + DEBUG(D_tls) debug_printf("OCSP response not valid (take 2): %s (%d)\n", + OCSP_response_status_str(status), status); + return; + } + +if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE)) + { + DEBUG(D_tls) debug_printf("OCSP status invalid times.\n"); + return; + } + +cbinfo->ocsp_response = resp; } +#endif @@ -314,7 +500,7 @@ Returns: OK/DEFER/FAIL */ static int -tls_expand_session_files(SSL_CTX *sctx, const tls_ext_ctx_cb *cbinfo) +tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo) { uschar *expanded; @@ -352,6 +538,27 @@ if (expanded != NULL && *expanded != 0) "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL); } +#ifdef EXPERIMENTAL_OCSP +if (cbinfo->ocsp_file != NULL) + { + if (!expand_check(cbinfo->ocsp_file, US"tls_ocsp_file", &expanded)) + return DEFER; + + if (expanded != NULL && *expanded != 0) + { + DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded); + if (cbinfo->ocsp_file_expanded && + (Ustrcmp(expanded, cbinfo->ocsp_file_expanded) == 0)) + { + DEBUG(D_tls) + debug_printf("tls_ocsp_file value unchanged, using existing values.\n"); + } else { + ocsp_load_response(sctx, cbinfo, expanded); + } + } + } +#endif + return OK; } @@ -375,15 +582,12 @@ Arguments: Returns: SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK} */ -static int -tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg); -/* pre-declared for SSL_CTX_set_tlsext_servername_callback call within func */ - +#ifdef EXIM_HAVE_OPENSSL_TLSEXT static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg) { const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); -const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; +tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; int rc; int old_pool = store_pool; @@ -424,11 +628,23 @@ SSL_CTX_set_tlsext_servername_callback(ctx_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(ctx_sni, cbinfo); if (cbinfo->server_cipher_list) SSL_CTX_set_cipher_list(ctx_sni, CS cbinfo->server_cipher_list); +#ifdef EXPERIMENTAL_OCSP +if (cbinfo->ocsp_file) + { + SSL_CTX_set_tlsext_status_cb(ctx_sni, tls_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); + } +#endif +rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE); +if (rc != OK) return SSL_TLSEXT_ERR_NOACK; + +/* do this after setup_certs, because this can require the certs for verifying +OCSP information. */ rc = tls_expand_session_files(ctx_sni, cbinfo); if (rc != OK) return SSL_TLSEXT_ERR_NOACK; -rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE); +rc = init_dh(ctx_sni, cbinfo->dhparam, NULL); if (rc != OK) return SSL_TLSEXT_ERR_NOACK; DEBUG(D_tls) debug_printf("Switching SSL context.\n"); @@ -436,6 +652,46 @@ SSL_set_SSL_CTX(s, ctx_sni); return SSL_TLSEXT_ERR_OK; } +#endif /* EXIM_HAVE_OPENSSL_TLSEXT */ + + + + +#ifdef EXPERIMENTAL_OCSP +/************************************************* +* Callback to handle OCSP Stapling * +*************************************************/ + +/* Called when acting as server during the TLS session setup if the client +requests OCSP information with a Certificate Status Request. + +Documentation via openssl s_server.c and the Apache patch from the OpenSSL +project. + +*/ + +static int +tls_stapling_cb(SSL *s, void *arg) +{ +const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg; +uschar *response_der; +int response_der_len; + +DEBUG(D_tls) debug_printf("Received TLS status request (OCSP stapling); %s response.\n", + cbinfo->ocsp_response ? "have" : "lack"); +if (!cbinfo->ocsp_response) + return SSL_TLSEXT_ERR_NOACK; + +response_der = NULL; +response_der_len = i2d_OCSP_RESPONSE(cbinfo->ocsp_response, &response_der); +if (response_der_len <= 0) + return SSL_TLSEXT_ERR_NOACK; + +SSL_set_tlsext_status_ocsp_resp(ssl, response_der, response_der_len); +return SSL_TLSEXT_ERR_OK; +} + +#endif /* EXPERIMENTAL_OCSP */ @@ -459,7 +715,11 @@ Returns: OK/DEFER/FAIL static int tls_init(host_item *host, uschar *dhparam, uschar *certificate, - uschar *privatekey, address_item *addr) + uschar *privatekey, +#ifdef EXPERIMENTAL_OCSP + uschar *ocsp_file, +#endif + address_item *addr) { long init_options; int rc; @@ -469,6 +729,9 @@ tls_ext_ctx_cb *cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); cbinfo->certificate = certificate; cbinfo->privatekey = privatekey; +#ifdef EXPERIMENTAL_OCSP +cbinfo->ocsp_file = ocsp_file; +#endif cbinfo->dhparam = dhparam; cbinfo->host = host; @@ -550,17 +813,28 @@ else /* Initialize with DH parameters if supplied */ -if (!init_dh(dhparam, host)) return DEFER; +if (!init_dh(ctx, dhparam, host)) return DEFER; -/* Set up certificate and key */ +/* Set up certificate and key (and perhaps OCSP info) */ rc = tls_expand_session_files(ctx, cbinfo); if (rc != OK) return rc; /* If we need to handle SNI, do so */ -#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) +#ifdef EXIM_HAVE_OPENSSL_TLSEXT if (host == NULL) { +#ifdef EXPERIMENTAL_OCSP + /* We check ocsp_file, not ocsp_response, because we care about if + the option exists, not what the current expansion might be, as SNI might + change the certificate and OCSP file in use between now and the time the + callback is invoked. */ + if (cbinfo->ocsp_file) + { + SSL_CTX_set_tlsext_status_cb(ctx, tls_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, cbinfo); + } +#endif /* We always do this, so that $tls_sni is available even if not used in tls_certificate */ SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb); @@ -785,11 +1059,6 @@ a TLS session. Arguments: require_ciphers allowed ciphers - ------------------------------------------------------ - require_mac list of allowed MACs ) Not used - require_kx list of allowed key_exchange methods ) for - require_proto list of allowed protocols ) OpenSSL - ------------------------------------------------------ Returns: OK on success DEFER for errors before the start of the negotiation @@ -798,8 +1067,7 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers, uschar *require_mac, - uschar *require_kx, uschar *require_proto) +tls_server_start(const uschar *require_ciphers) { int rc; uschar *expciphers; @@ -817,7 +1085,11 @@ if (tls_active >= 0) /* Initialize the SSL library. If it fails, it will already have logged the error. */ -rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL); +rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, +#ifdef EXPERIMENTAL_OCSP + tls_ocsp_file, +#endif + NULL); if (rc != OK) return rc; cbinfo = static_cbinfo; @@ -825,8 +1097,9 @@ if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers)) return FAIL; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they -are separated by underscores. So that I can use either form in my tests, and -also for general convenience, we turn underscores into hyphens here. */ +were historically separated by underscores. So that I can use either form in my +tests, and also for general convenience, we turn underscores into hyphens here. +*/ if (expciphers != NULL) { @@ -960,11 +1233,6 @@ Argument: verify_certs file for certificate verify crl file containing CRL require_ciphers list of allowed ciphers - ------------------------------------------------------ - require_mac list of allowed MACs ) Not used - require_kx list of allowed key_exchange methods ) for - require_proto list of allowed protocols ) OpenSSL - ------------------------------------------------------ timeout startup timeout Returns: OK on success @@ -976,15 +1244,18 @@ int tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, uschar *certificate, uschar *privatekey, uschar *sni, uschar *verify_certs, uschar *crl, - uschar *require_ciphers, uschar *require_mac, uschar *require_kx, - uschar *require_proto, int timeout) + uschar *require_ciphers, int timeout) { static uschar txt[256]; uschar *expciphers; X509* server_cert; int rc; -rc = tls_init(host, dhparam, certificate, privatekey, addr); +rc = tls_init(host, dhparam, certificate, privatekey, +#ifdef EXPERIMENTAL_OCSP + NULL, +#endif + addr); if (rc != OK) return rc; tls_certificate_verified = FALSE; @@ -1022,8 +1293,14 @@ if (sni) tls_sni = NULL; else { +#ifdef EXIM_HAVE_OPENSSL_TLSEXT DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_sni); SSL_set_tlsext_host_name(ssl, tls_sni); +#else + DEBUG(D_tls) + debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n", + tls_sni); +#endif } } @@ -1272,6 +1549,72 @@ tls_active = -1; /************************************************* +* Let tls_require_ciphers be checked at startup * +*************************************************/ + +/* The tls_require_ciphers option, if set, must be something which the +library can parse. + +Returns: NULL on success, or error message +*/ + +uschar * +tls_validate_require_cipher(void) +{ +SSL_CTX *ctx; +uschar *s, *expciphers, *err; + +/* this duplicates from tls_init(), we need a better "init just global +state, for no specific purpose" singleton function of our own */ + +SSL_load_error_strings(); +OpenSSL_add_ssl_algorithms(); +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +/* SHA256 is becoming ever more popular. This makes sure it gets added to the +list of available digests. */ +EVP_add_digest(EVP_sha256()); +#endif + +if (!(tls_require_ciphers && *tls_require_ciphers)) + return NULL; + +if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers)) + return US"failed to expand tls_require_ciphers"; + +if (!(expciphers && *expciphers)) + return NULL; + +/* normalisation ripped from above */ +s = expciphers; +while (*s != 0) { if (*s == '_') *s = '-'; s++; } + +err = NULL; + +ctx = SSL_CTX_new(SSLv23_server_method()); +if (!ctx) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring); + } + +DEBUG(D_tls) + debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers); + +if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed", expciphers); + } + +SSL_CTX_free(ctx); + +return err; +} + + + + +/************************************************* * Report the library versions. * *************************************************/ @@ -1298,7 +1641,7 @@ fprintf(f, "Library version: OpenSSL: Compile: %s\n" /************************************************* -* Pseudo-random number generation * +* Random number generation * *************************************************/ /* Pseudo-random number generation. The result is not expected to be @@ -1313,7 +1656,7 @@ Returns a random number in range [0, max-1] */ int -pseudo_random_number(int max) +vaguely_random_number(int max) { unsigned int r; int i, needed_len; @@ -1349,7 +1692,14 @@ if (i < needed_len) needed_len = i; /* We do not care if crypto-strong */ -(void) RAND_pseudo_bytes(smallbuf, needed_len); +i = RAND_pseudo_bytes(smallbuf, needed_len); +if (i < 0) + { + DEBUG(D_all) + debug_printf("OpenSSL RAND_pseudo_bytes() not supported by RAND method, using fallback.\n"); + return vaguely_random_number_fallback(max); + } + r = 0; for (p = smallbuf; needed_len; --needed_len, ++p) { @@ -1526,7 +1876,7 @@ uschar keep_c; BOOL adding, item_parsed; result = 0L; -/* Prior to 4.78 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed +/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed * from default because it increases BEAST susceptibility. */ #ifdef SSL_OP_NO_SSLv2 result |= SSL_OP_NO_SSLv2; diff --git a/src/src/tls.c b/src/src/tls.c index d975a2c89..0c98aeba9 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is @@ -30,15 +30,19 @@ static void dummy(int x) { dummy(x-1); } #else /* Static variables that are used for buffering data by both sets of -functions and the common functions below. */ +functions and the common functions below. +We're moving away from this; GnuTLS is already using a state, which +can switch, so we can do TLS callouts during ACLs. */ +static const int ssl_xfer_buffer_size = 4096; +#ifndef USE_GNUTLS static uschar *ssl_xfer_buffer = NULL; -static int ssl_xfer_buffer_size = 4096; static int ssl_xfer_buffer_lwm = 0; static int ssl_xfer_buffer_hwm = 0; static int ssl_xfer_eof = 0; static int ssl_xfer_error = 0; +#endif uschar *tls_channelbinding_b64 = NULL; @@ -59,11 +63,11 @@ Returns: TRUE if OK; result may still be NULL after forced failure */ static BOOL -expand_check(uschar *s, uschar *name, uschar **result) +expand_check(const uschar *s, const uschar *name, uschar **result) { if (s == NULL) *result = NULL; else { - *result = expand_string(s); + *result = expand_string(US s); /* need to clean up const some more */ if (*result == NULL && !expand_string_forcedfail) { log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name, @@ -81,6 +85,13 @@ return TRUE; #ifdef USE_GNUTLS #include "tls-gnu.c" + +#define ssl_xfer_buffer (current_global_tls_state->xfer_buffer) +#define ssl_xfer_buffer_lwm (current_global_tls_state->xfer_buffer_lwm) +#define ssl_xfer_buffer_hwm (current_global_tls_state->xfer_buffer_hwm) +#define ssl_xfer_eof (current_global_tls_state->xfer_eof) +#define ssl_xfer_error (current_global_tls_state->xfer_error) + #else #include "tls-openssl.c" #endif diff --git a/src/src/tod.c b/src/src/tod.c index c6afb713e..9aa845c82 100644 --- a/src/src/tod.c +++ b/src/src/tod.c @@ -34,6 +34,7 @@ option. Argument: type of timestamp required: tod_bsdin BSD inbox format tod_epoch Unix epoch format + tod_epochl Unix epoch/usec format tod_full full date and time tod_log log file data line format, with zone if log_timezone is TRUE @@ -51,9 +52,19 @@ Returns: pointer to fixed buffer containing the timestamp uschar * tod_stamp(int type) { -time_t now = time(NULL); +time_t now; struct tm *t; +if (type == tod_epoch_l) + { + struct timeval tv; + gettimeofday(&tv, NULL); + (void) sprintf(CS timebuf, "%ld%06ld", tv.tv_sec, tv.tv_usec ); /* Unix epoch/usec format */ + return timebuf; + } + +now = time(NULL); + /* Vary log type according to timezone requirement */ if (type == tod_log) type = log_timezone? tod_log_zone : tod_log_bare; diff --git a/src/src/transport.c b/src/src/transport.c index 0557318d0..6894e96df 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* General functions concerned with transportation, and generic options for all @@ -919,19 +919,19 @@ if ((options & topt_no_body) == 0) } } - /* Finished with the check string */ - - nl_check_length = nl_escape_length = 0; - /* A read error on the body will have left len == -1 and errno set. */ if (len != 0) return FALSE; + } - /* If requested, add a terminating "." line (SMTP output). */ +/* Finished with the check string */ - if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf)) - return FALSE; - } +nl_check_length = nl_escape_length = 0; + +/* If requested, add a terminating "." line (SMTP output). */ + +if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf)) + return FALSE; /* Write out any remaining data in the buffer before returning. */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index b1fedd2d4..f9f225fca 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -62,6 +62,9 @@ optionlist smtp_transport_options[] = { { "gethostbyname", opt_bool, (void *)offsetof(smtp_transport_options_block, gethostbyname) }, #ifdef SUPPORT_TLS + /* These are no longer honoured, as of Exim 4.80; for now, we silently + ignore; a later release will warn, and a later-still release will remove + these options, so that using them becomes an error. */ { "gnutls_require_kx", opt_stringptr, (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, { "gnutls_require_mac", opt_stringptr, @@ -895,7 +898,9 @@ outblock.authenticating = FALSE; tls_bits = 0; tls_cipher = NULL; tls_peerdn = NULL; +#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) tls_sni = NULL; +#endif /* If an authenticated_sender override has been specified for this transport instance, expand it. If the expansion is forced to fail, and there was already @@ -1131,9 +1136,6 @@ if (tls_offered && !suppress_tls && ob->tls_verify_certificates, ob->tls_crl, ob->tls_require_ciphers, - ob->gnutls_require_mac, - ob->gnutls_require_kx, - ob->gnutls_require_proto, ob->command_timeout); /* TLS negotiation failed; give an error. From outside, this function may diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 605be4800..621cb6ba9 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Private structure for the private options and other private data. */ diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index 8e57d28b2..7a240b6c0 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions in support of the use of maildirsize files for handling quotas in diff --git a/src/util/gen_pkcs3.c b/src/util/gen_pkcs3.c new file mode 100644 index 000000000..ae7e7610a --- /dev/null +++ b/src/util/gen_pkcs3.c @@ -0,0 +1,229 @@ +/* Copyright (C) 2012 Phil Pennock. + * This is distributed as part of Exim and licensed under the GPL. + * See the file "NOTICE" for more details. + */ + +/* Build with: + * c99 $(pkg-config --cflags openssl) gen_pkcs3.c $(pkg-config --libs openssl) + */ + +#include <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/err.h> +#include <openssl/pem.h> + +extern const char *__progname; + + +void __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2))) +die(const char *fmt, ...) +{ + va_list ap; + + fflush(NULL); + fprintf(stderr, "%s: ", __progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + fflush(stderr); + exit(1); +} + + +void __attribute__((__noreturn__)) +die_openssl_err(const char *msg) +{ + char err_string[250]; + unsigned long e; + + ERR_error_string_n(ERR_get_error(), err_string, sizeof(err_string)); + die("%s: %s", msg, err_string); +} + + +static BIGNUM * +bn_from_text(const char *text) +{ + BIGNUM *b; + char *p, *spaceless; + const char *q, *end; + size_t len; + int rc; + + len = strlen(text); + spaceless = malloc(len); + if (!spaceless) + die("malloc(%zu) failed: %s", len, strerror(errno)); + + for (p = spaceless, q = text, end = text + len; + q < end; + ++q) { + if (!isspace(*q)) + *p++ = *q; + } + + b = NULL; + rc = BN_hex2bn(&b, spaceless); + + if (rc != p - spaceless) + die("BN_hex2bn did not convert entire input; took %d of %z bytes", + rc, p - spaceless); + + return b; +} + + +static void +our_dh_check(DH *dh) +{ + int rc, errflags = 0; + + rc = DH_check(dh, &errflags); + if (!rc) die_openssl_err("DH_check() could not be performed");; + + /* We ignore DH_UNABLE_TO_CHECK_GENERATOR because some of the invocations + * deliberately provide generators other than 2 or 5. */ + + if (errflags & DH_CHECK_P_NOT_SAFE_PRIME) + die("DH_check(): p not a safe prime"); + if (errflags & DH_NOT_SUITABLE_GENERATOR) + die("DH_check(): g not suitable as generator"); +} + + +static void +emit_c_format_dh(FILE *stream, DH *dh) +{ + BIO *bio; + long length; + char *data, *end, *p, *nl; + + bio = BIO_new(BIO_s_mem()); + PEM_write_bio_DHparams(bio, dh); + length = BIO_get_mem_data(bio, &data); + if (!length) + die("no data in memory BIO to format for printing"); + if (length < 0) + die("grr, negative length memory not supported"); + end = data + length; + + for (p = data; p < end; /**/) { + nl = strchr(p, '\n'); + if (!nl) { + fprintf(stream, "\"%s\\n\"\n/* missing final newline */\n", p); + break; + } + *nl = '\0'; + fprintf(stream, "\"%s\\n\"\n", p); + p = nl + 1; + } +} + + +void __attribute__((__noreturn__)) +usage(FILE *stream, int exitcode) +{ + fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g>\n" +"Both dh_p and dh_g should be hex strings representing the numbers\n" +"They may contain whitespace.\n" +"\n" +" -C show C string form of PEM result\n" +" -P do not show PEM\n" +" -c run OpenSSL DH_check() on the DH object\n" +" -s show the parsed p and g\n" +" -t show text form of certificate\n" + + , __progname); + exit(exitcode); +} + + +int +main(int argc, char *argv[]) +{ + BIGNUM *p, *g; + DH *dh; + int ch; + bool perform_dh_check = false; + bool show_c_form = false; + bool show_numbers = false; + bool show_pem = true; + bool show_text = false; + + while ((ch = getopt(argc, argv, "CPcsth")) != -1) { + switch (ch) { + case 'C': + show_c_form = true; + break; + case 'P': + show_pem = false; + break; + case 'c': + perform_dh_check = true; + break; + case 's': + show_numbers = true; + break; + case 't': + show_text = true; + break; + + case 'h': + usage(stdout, 0); + case '?': + die("Unknown option or missing argument -%c", optopt); + default: + die("Unhandled option -%c", ch); + } + } + + optind -= 1; + argc -= optind; + argv += optind; + + if (argc != 3) { + fprintf(stderr, "argc: %d\n", argc); + usage(stderr, 1); + } + + p = bn_from_text(argv[1]); + g = bn_from_text(argv[2]); + + if (show_numbers) { + printf("p = "); + BN_print_fp(stdout, p); + printf("\ng = "); + BN_print_fp(stdout, g); + printf("\n"); + } + + dh = DH_new(); + dh->p = p; + dh->g = g; + + if (perform_dh_check) + our_dh_check(dh); + + if (show_text) + DHparams_print_fp(stdout, dh); + + if (show_pem) { + if (show_c_form) + emit_c_format_dh(stdout, dh); + else + PEM_write_DHparams(stdout, dh); + } + + DH_free(dh); /* should free p & g too */ + return 0; +} |