summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore2
-rw-r--r--src/ACKNOWLEDGMENTS6
-rw-r--r--src/OS/Makefile-Base3
-rw-r--r--src/OS/Makefile-SunOS45
-rw-r--r--src/OS/Makefile-mips1
-rw-r--r--src/OS/eximon.conf-Default2
-rw-r--r--src/OS/os.h-Darwin4
-rw-r--r--src/OS/os.h-HP-UX7
-rw-r--r--src/OS/os.h-Linux7
-rw-r--r--src/OS/os.h-OpenBSD2
-rw-r--r--src/README2
-rw-r--r--src/README.UPDATING93
-rw-r--r--src/exim_monitor/em_globals.c4
-rw-r--r--src/exim_monitor/em_log.c2
-rw-r--r--src/exim_monitor/em_main.c6
-rw-r--r--src/exim_monitor/em_text.c2
-rwxr-xr-xsrc/scripts/Configure-Makefile15
-rwxr-xr-xsrc/scripts/MakeLinks1
-rw-r--r--src/src/EDITME21
-rw-r--r--src/src/acl.c2
-rw-r--r--src/src/auths/check_serv_cond.c2
-rw-r--r--src/src/auths/cram_md5.c2
-rw-r--r--src/src/auths/cyrus_sasl.c9
-rw-r--r--src/src/auths/cyrus_sasl.h2
-rw-r--r--src/src/auths/get_no64_data.c2
-rw-r--r--src/src/auths/spa.c16
-rw-r--r--src/src/buildconfig.c65
-rw-r--r--src/src/config.h.defaults27
-rw-r--r--src/src/configure.default7
-rw-r--r--src/src/daemon.c325
-rw-r--r--src/src/dcc.c40
-rw-r--r--src/src/deliver.c2
-rw-r--r--src/src/dns.c3
-rw-r--r--src/src/drtables.c2
-rw-r--r--src/src/exim.c46
-rw-r--r--src/src/exim.h21
-rw-r--r--src/src/eximon.src19
-rw-r--r--src/src/expand.c151
-rw-r--r--src/src/functions.h22
-rw-r--r--src/src/globals.c19
-rw-r--r--src/src/globals.h10
-rw-r--r--src/src/host.c4
-rw-r--r--src/src/local_scan.h4
-rw-r--r--src/src/log.c10
-rw-r--r--src/src/lookups/dbmdb.c2
-rw-r--r--src/src/lookups/dnsdb.c28
-rw-r--r--src/src/lookups/ldap.c2
-rw-r--r--src/src/macros.h4
-rw-r--r--src/src/malware.c9
-rw-r--r--src/src/mytypes.h8
-rw-r--r--src/src/os.c4
-rw-r--r--src/src/pdkim/pdkim.c5
-rw-r--r--src/src/pdkim/pdkim.h2
-rw-r--r--src/src/readconf.c84
-rw-r--r--src/src/receive.c4
-rw-r--r--src/src/routers/iplookup.c4
-rw-r--r--src/src/smtp_in.c10
-rw-r--r--src/src/spool_in.c6
-rw-r--r--src/src/spool_mbox.c4
-rw-r--r--src/src/spool_out.c4
-rw-r--r--src/src/std-crypto.c521
-rw-r--r--src/src/store.c2
-rw-r--r--src/src/string.c12
-rw-r--r--src/src/structs.h2
-rw-r--r--src/src/tls-gnu.c2045
-rw-r--r--src/src/tls-openssl.c458
-rw-r--r--src/src/tls.c21
-rw-r--r--src/src/tod.c13
-rw-r--r--src/src/transport.c18
-rw-r--r--src/src/transports/smtp.c10
-rw-r--r--src/src/transports/smtp.h2
-rw-r--r--src/src/transports/tf_maildir.c2
-rw-r--r--src/util/gen_pkcs3.c229
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;
+}