Add gnutls_require_{kx,mac,protocols}.
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Thu, 18 Jan 2007 15:35:42 +0000 (15:35 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Thu, 18 Jan 2007 15:35:42 +0000 (15:35 +0000)
15 files changed:
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/smtp_in.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transports/smtp.c
src/src/transports/smtp.h
test/confs/2011
test/log/2011
test/scripts/2000-GnuTLS/2011

index 240c815eb657baa41bbc00d6316aabfb0f2a7233..238621e9bd5bd7b8ec8f78bc9e04cbd15985cf24 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.454 2007/01/17 11:17:58 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.455 2007/01/18 15:35:42 ph10 Exp $
 
 Change log file for Exim from version 4.21
 -------------------------------------------
@@ -26,6 +26,9 @@ PH/02 In an ACL statement such as
 PH/03 Added additional dnslists conditions == and =& which are different from
       = and & when the dns lookup returns more than one IP address.
 
+PH/04 Added gnutls_require_{kx,mac,protocols} to give more control over the
+      cipher suites used by GnuTLS. These options are ignored by OpenSSL.
+
 
 Exim version 4.66
 -----------------
index 960f93ce80a124205befccd7409c6aefb4067426..9cc8f81cc4da0eb993df2785be09d2fdf398d6cf 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.127 2007/01/17 11:17:58 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.128 2007/01/18 15:35:42 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -106,6 +106,75 @@ Version 4.67
     When the DNS lookup yields only a single IP address, there is no difference
     between = and == and between & and =&.
 
+ 3. Up till now, the only control over which cipher suites GnuTLS uses has been
+    for the cipher algorithms. New options have been added to allow some of the
+    other parameters to be varied. Here is complete documentation for the
+    available features:
+
+    GnuTLS allows the caller to specify separate lists of permitted key
+    exchange methods, main cipher algorithms, and MAC algorithms. These may be
+    used in any combination to form a specific cipher suite. This is unlike
+    OpenSSL, where complete cipher names can be passed to its control function.
+    GnuTLS also allows a list of acceptable protocols to be supplied.
+
+    For compatibility with OpenSSL, the tls_require_ciphers option can be set
+    to complete cipher suite names such as RSA_ARCFOUR_SHA, but for GnuTLS this
+    option controls only the cipher algorithms. Exim searches each item in the
+    list for the name of an available algorithm. For example, if the list
+    contains RSA_AES_SHA, then AES is recognized, and the behaviour is exactly
+    the same as if just AES were given.
+
+    There are additional options called gnutls_require_kx, gnutls_require_mac,
+    and gnutls_require_protocols that can be used to restrict the key exchange
+    methods, MAC algorithms, and protocols, respectively. These options are
+    ignored if OpenSSL is in use.
+
+    All four options are available as global options, controlling how Exim
+    behaves as a server, and also as options of the smtp transport, controlling
+    how Exim behaves as a client. All the values are string expanded. After
+    expansion, the values must be colon-separated lists, though the separator
+    can be changed in the usual way.
+
+    Each of the four lists starts out with a default set of algorithms. If the
+    first item in one of the "require" options does _not_ start with an
+    exclamation mark, all the default items are deleted. In this case, only
+    those that are explicitly specified can be used. If the first item in one
+    of the "require" items _does_ start with an exclamation mark, the defaults
+    are left on the list.
+
+    Then, any item that starts with an exclamation mark causes the relevant
+    entry to be removed from the list, and any item that does not start with an
+    exclamation mark causes a new entry to be added to the list. Unrecognized
+    items in the list are ignored. Thus:
+
+      tls_require_ciphers = !ARCFOUR
+
+    allows all the defaults except ARCFOUR, whereas
+
+      tls_require_ciphers = AES : 3DES
+
+    allows only cipher suites that use AES or 3DES. For tls_require_ciphers
+    the recognized names are AES_256, AES_128, AES (both of the preceding),
+    3DES, ARCFOUR_128, ARCFOUR_40, and ARCFOUR (both of the preceding). The
+    default list does not contain all of these; it just has AES_256, AES_128,
+    3DES, and ARCFOUR_128.
+
+    For gnutls_require_kx, the recognized names are DHE_RSA, RSA (which
+    includes DHE_RSA), DHE_DSS, and DHE (which includes both DHE_RSA and
+    DHE_DSS). The default list contains RSA, DHE_DSS, DHE_RSA.
+
+    For gnutls_require_mac, the recognized names are SHA (synonym SHA1), and
+    MD5. The default list contains SHA, MD5.
+
+    For gnutls_require_protocols, the recognized names are TLS1 and SSL3.
+    The default list contains TLS1, SSL3.
+
+    In a server, the order of items in these lists is unimportant. The server
+    will advertise the availability of all the relevant cipher suites. However,
+    in a client, the order in the tls_require_ciphers list specifies a
+    preference order for the cipher algorithms. The first one in the client's
+    list that is also advertised by the server is tried first.
+
 
 Version 4.66
 ------------
index e484f98e94ad758a90a2591e885c48aaab9fbc60..67b03c79320de45e2ad63b8c5fd4d842426d6ae5 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/OptionLists.txt,v 1.28 2006/12/05 11:35:28 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/OptionLists.txt,v 1.29 2007/01/18 15:35:42 ph10 Exp $
 
 LISTS OF EXIM OPTIONS
 ---------------------
@@ -11,7 +11,7 @@ This file contains complete lists of four kinds of Exim option:
   4. Those that can appear in the build time configuration for the Exim monitor
      (Local/eximon.conf).
 
-This file was last updated for Exim release 4.64.
+This file was last updated for Exim release 4.67.
 
 
 1. RUN TIME OPTIONS
@@ -231,6 +231,12 @@ from                                 string*         unset         autoreply
 gecos_name                           string*         unset         main
 gecos_pattern                        string          unset         main
 gethostbyname                        boolean         false         smtp
+gnutls_require_kx                    string*         unset         main              4.67
+                                     string*         unset         smtp              4.67
+gnutls_require_mac                   string*         unset         main              4.67
+                                     string*         unset         smtp              4.67
+gnutls_require_protocols             string*         unset         main              4.67
+                                     string*         unset         smtp              4.67
 group                                string          +             routers           4.00
                                                      unset         transports        4.00 replaces local option in some transports
 header_line_maxsize                  integer         0 (unset)     main              4.14
index f151423361975687a744838357622036ed642f50..5e3f9d5802b69fa27a81f6b75cdf99e1db929b89 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.33 2007/01/15 15:59:22 ph10 Exp $ */
+/* $Cambridge: exim/src/src/functions.h,v 1.34 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -24,13 +24,14 @@ extern uschar *init_perl(uschar *);
 
 #ifdef SUPPORT_TLS
 extern int     tls_client_start(int, host_item *, address_item *, uschar *,
-                 uschar *, uschar *, uschar *, uschar *, uschar *, int);
+                 uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
+                 uschar *, uschar *, 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 *);
+extern int     tls_server_start(uschar *, uschar *, uschar *, uschar *);
 extern int     tls_ungetc(int);
 extern int     tls_write(const uschar *, size_t);
 #endif
index 796104bc039bafeef9f977e91ebbcec35b7d2136..c60ef864f151b82220b7eed89f91bee8fdc55c10 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.62 2007/01/15 15:59:22 ph10 Exp $ */
+/* $Cambridge: exim/src/src/globals.c,v 1.63 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -109,6 +109,9 @@ uschar *tls_on_connect_ports   = NULL;
 uschar *tls_peerdn             = NULL;
 
 #ifdef SUPPORT_TLS
+uschar *gnutls_require_mac     = NULL;
+uschar *gnutls_require_kx      = NULL;
+uschar *gnutls_require_proto   = NULL;
 const pcre *regex_STARTTLS     = NULL;
 uschar *tls_advertise_hosts    = NULL;    /* This is deliberate */
 uschar *tls_certificate        = NULL;
index 2ea06dad961c061bffc07b729b4462493f740402..090e1b4834d2dbfd98fffb690f5ac4349b86c2fc 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.43 2007/01/15 15:59:22 ph10 Exp $ */
+/* $Cambridge: exim/src/src/globals.h,v 1.44 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -71,6 +71,9 @@ extern uschar *tls_on_connect_ports;   /* Ports always tls-on-connect */
 extern uschar *tls_peerdn;             /* DN from peer */
 
 #ifdef SUPPORT_TLS
+extern uschar *gnutls_require_mac;     /* So some can be avoided */
+extern uschar *gnutls_require_kx;      /* So some can be avoided */
+extern uschar *gnutls_require_proto;   /* So some can be avoided */
 extern const pcre *regex_STARTTLS;     /* For recognizing STARTTLS settings */
 extern uschar *tls_advertise_hosts;    /* host for which TLS is advertised */
 extern uschar *tls_certificate;        /* Certificate file */
index 593da8a5749d60bdb86348050d387585a7af6b65..c3514aba5ddac919d3925e0af5c514f519fdf59e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/readconf.c,v 1.26 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.27 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -218,6 +218,11 @@ static optionlist optionlist_config[] = {
   { "freeze_tell",              opt_stringptr,   &freeze_tell },
   { "gecos_name",               opt_stringptr,   &gecos_name },
   { "gecos_pattern",            opt_stringptr,   &gecos_pattern },
+#ifdef SUPPORT_TLS
+  { "gnutls_require_kx",        opt_stringptr,   &gnutls_require_kx },
+  { "gnutls_require_mac",       opt_stringptr,   &gnutls_require_mac },
+  { "gnutls_require_protocols", opt_stringptr,   &gnutls_require_proto },
+#endif
   { "header_line_maxsize",      opt_int,         &header_line_maxsize },
   { "header_maxsize",           opt_int,         &header_maxsize },
   { "headers_charset",          opt_stringptr,   &headers_charset },
index b1a1eba3dd8de80fa7ae84cc1009a841e803b8b4..ea703993577bdf948bc85e8dc824f1dd37027582 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/smtp_in.c,v 1.50 2007/01/15 15:59:22 ph10 Exp $ */
+/* $Cambridge: exim/src/src/smtp_in.c,v 1.51 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -1540,7 +1540,9 @@ if (!sender_host_unknown)
   smtps port for use with older style SSL MTAs. */
 
   #ifdef SUPPORT_TLS
-  if (tls_on_connect && tls_server_start(tls_require_ciphers) != OK)
+  if (tls_on_connect &&
+      tls_server_start(tls_require_ciphers,
+        gnutls_require_mac, gnutls_require_kx, gnutls_require_proto) != OK)
     return FALSE;
   #endif
 
@@ -3593,7 +3595,8 @@ 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)) == OK)
+    if ((rc = tls_server_start(tls_require_ciphers, gnutls_require_mac,
+           gnutls_require_kx, gnutls_require_proto)) == OK)
       {
       if (!tls_remember_esmtp)
         helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE;
index 891d1ea0057f4553c898557eba186d23d8bd5bf4..ee57659fa7bf174f86d1c0e0c35b21159cc51ac8 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/tls-gnu.c,v 1.17 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/tls-gnu.c,v 1.18 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -46,17 +46,24 @@ static char ssl_errstring[256];
 static int  ssl_session_timeout = 200;
 static int  verify_requirement;
 
-/* Priorities for TLS algorithms to use. At present, only the cipher priority
-vector can be altered. */
+/* Priorities for TLS algorithms to use. In each case there's a default table,
+and space into which it can be copied and altered. */
 
-static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
+static const int default_proto_priority[16] = {
+  GNUTLS_TLS1,
+  GNUTLS_SSL3,
+  0 };
+
+static int proto_priority[16];
 
-static const int kx_priority[16] = {
+static const int default_kx_priority[16] = {
   GNUTLS_KX_RSA,
   GNUTLS_KX_DHE_DSS,
   GNUTLS_KX_DHE_RSA,
   0 };
 
+static int kx_priority[16];
+
 static int default_cipher_priority[16] = {
   GNUTLS_CIPHER_AES_256_CBC,
   GNUTLS_CIPHER_AES_128_CBC,
@@ -66,21 +73,50 @@ static int default_cipher_priority[16] = {
 
 static int cipher_priority[16];
 
-static const int mac_priority[16] = {
+static const int default_mac_priority[16] = {
   GNUTLS_MAC_SHA,
   GNUTLS_MAC_MD5,
   0 };
 
+static int mac_priority[16];
+
+/* These two are currently not changeable. */
+
 static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
 static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
 
-/* Tables of cipher names and equivalent numbers */
+/* Tables of priority names and equivalent numbers */
 
 typedef struct pri_item {
   uschar *name;
   int *values;
 } pri_item;
 
+
+static int tls1_codes[] = { GNUTLS_TLS1, 0 };
+static int ssl3_codes[] = { GNUTLS_SSL3, 0 };
+
+static pri_item proto_index[] = {
+  { US"TLS1", tls1_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 }
+};
+
+
 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,
@@ -102,6 +138,16 @@ static pri_item cipher_index[] = {
 };
 
 
+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 }
+};
+
+
 
 /*************************************************
 *               Handle TLS error                 *
@@ -512,7 +558,7 @@ return OK;
 
 
 /*************************************************
-*        Remove ciphers from priority list       *
+*           Remove from a priority list          *
 *************************************************/
 
 /* Cautiously written so that it will remove duplicates if present.
@@ -525,7 +571,7 @@ Returns:       nothing
 */
 
 static void
-remove_ciphers(int *list, int *remove_list)
+remove_priority(int *list, int *remove_list)
 {
 for (; *remove_list != 0; remove_list++)
   {
@@ -545,7 +591,7 @@ for (; *remove_list != 0; remove_list++)
 
 
 /*************************************************
-*        Add ciphers to priority list            *
+*            Add to a priority list              *
 *************************************************/
 
 /* Cautiously written to check the list size
@@ -559,7 +605,7 @@ Returns:       TRUE if OK; FALSE if list overflows
 */
 
 static BOOL
-add_ciphers(int *list, int list_max, int *add_list)
+add_priority(int *list, int list_max, int *add_list)
 {
 int next = 0;
 while (list[next] != 0) next++;
@@ -574,6 +620,78 @@ return TRUE;
 
 
 
+/*************************************************
+*          Adjust a priority list                *
+*************************************************/
+
+/* This function is called to adjust the lists of cipher algorithms, MAC
+algorithms, key-exchange methods, and protocols.
+
+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
+
+
+  which       text for an error message
+
+Returns:      FALSE if the table overflows, else TRUE
+*/
+
+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;
+
+while ((t = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) != NULL)
+  {
+  int i;
+  BOOL exclude = t[0] == '!';
+  if (first && !exclude) plist[0] = 0;
+  first = FALSE;
+  for (i = 0; i < isize; i++)
+    {
+    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)
+  {
+  int *ptr = plist;
+  debug_printf("adjusted %s priorities:", which);
+  while (*ptr != 0) debug_printf(" %d", *ptr++);
+  debug_printf("\n");
+  }
+
+return TRUE;
+}
+
+
+
+
 /*************************************************
 *        Initialize a single GNUTLS session      *
 *************************************************/
@@ -591,78 +709,60 @@ cipher.
 
 Arguments:
   side         one of GNUTLS_SERVER, GNUTLS_CLIENT
-  expciphers   expanded ciphers list
+  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
 
 Returns:  a gnutls_session, or NULL if there is a problem
 */
 
 static gnutls_session
-tls_session_init(int side, uschar *expciphers)
+tls_session_init(int side, uschar *expciphers, uschar *expmac, uschar *expkx,
+  uschar *expproto)
 {
 gnutls_session session;
 
 gnutls_init(&session, side);
 
-/* Handle the list of permitted ciphers */
+/* Initialize the lists of permitted protocols, key-exchange methods, ciphers,
+and MACs. */
 
 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));
+
+/* 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. */
 
 if (expciphers != NULL)
   {
-  int sep = 0;
-  BOOL first = TRUE;
-  uschar *cipher;
-
-  /* The names OpenSSL uses are of the form DES-CBC3-SHA, using hyphen
-  separators. GnuTLS uses underscore separators. So that I can use either form
-  in my tests, and also for general convenience, we turn hyphens into
-  underscores before scanning the list. */
-
   uschar *s = expciphers;
   while (*s != 0) { if (*s == '-') *s = '_'; s++; }
+  }
 
-  while ((cipher = string_nextinlist(&expciphers, &sep, big_buffer,
-             big_buffer_size)) != NULL)
-    {
-    int i;
-    BOOL exclude = cipher[0] == '!';
-    if (first && !exclude) cipher_priority[0] = 0;
-    first = FALSE;
-
-    for (i = 0; i < sizeof(cipher_index)/sizeof(pri_item); i++)
-      {
-      uschar *ss = strstric(cipher, cipher_index[i].name, FALSE);
-      if (ss != NULL)
-        {
-        uschar *endss = ss + Ustrlen(cipher_index[i].name);
-        if ((ss == cipher || !isalnum(ss[-1])) && !isalnum(*endss))
-          {
-          if (exclude)
-            remove_ciphers(cipher_priority, cipher_index[i].values);
-          else
-            {
-            if (!add_ciphers(cipher_priority,
-                             sizeof(cipher_priority)/sizeof(pri_item),
-                             cipher_index[i].values))
-              {
-              log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: cipher "
-                "priority table overflow");
-              gnutls_deinit(session);
-              return NULL;
-              }
-            }
-          }
-        }
-      }
-    }
-
-  DEBUG(D_tls)
-    {
-    int *ptr = cipher_priority;
-    debug_printf("adjusted cipher priorities:");
-    while (*ptr != 0) debug_printf(" %d", *ptr++);
-    debug_printf("\n");
-    }
+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;
   }
 
 /* Define the various priorities */
@@ -670,7 +770,7 @@ if (expciphers != NULL)
 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, protocol_priority);
+gnutls_protocol_set_priority(session, proto_priority);
 gnutls_mac_set_priority(session, mac_priority);
 
 gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
@@ -739,7 +839,10 @@ the STARTTLS command. It must respond to that command, and then negotiate
 a TLS session.
 
 Arguments:
-  require_ciphers  list of allowed ciphers
+  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
@@ -748,11 +851,15 @@ Returns:           OK on success
 */
 
 int
-tls_server_start(uschar *require_ciphers)
+tls_server_start(uschar *require_ciphers, uschar *require_mac,
+  uschar *require_kx, uschar *require_proto)
 {
 int rc;
 uschar *error;
 uschar *expciphers = NULL;
+uschar *expmac = NULL;
+uschar *expkx = NULL;
+uschar *expproto = NULL;
 
 /* Check for previous activation */
 
@@ -774,7 +881,10 @@ rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates,
   tls_crl);
 if (rc != OK) return rc;
 
-if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
+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
@@ -790,7 +900,8 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK)
 
 /* Prepare for new connection */
 
-tls_session = tls_session_init(GNUTLS_SERVER, expciphers);
+tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx,
+  expproto);
 if (tls_session == NULL)
   return tls_error(US"tls_session_init", NULL, GNUTLS_E_MEMORY_ERROR);
 
@@ -883,13 +994,16 @@ return OK;
 Arguments:
   fd                the fd of the connection
   host              connected host (for messages)
-  addr
+  addr              the first address (not used)
   dhparam           DH parameter file
   certificate       certificate file
   privatekey        private key file
   verify_certs      file for certificate verify
   verify_crl        CRL for verify
-  require_ciphers   list of allowed ciphers
+  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),
@@ -899,10 +1013,14 @@ 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 *verify_certs,
-  uschar *verify_crl, uschar *require_ciphers, int timeout)
+  uschar *verify_crl, uschar *require_ciphers, uschar *require_mac,
+  uschar *require_kx, uschar *require_proto, int timeout)
 {
 const gnutls_datum *server_certs;
 uschar *expciphers = NULL;
+uschar *expmac = NULL;
+uschar *expkx = NULL;
+uschar *expproto = NULL;
 uschar *error;
 unsigned int server_certs_size;
 int rc;
@@ -914,10 +1032,15 @@ verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED;
 rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl);
 if (rc != OK) return rc;
 
-if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
+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);
+tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx,
+  expproto);
+
 if (tls_session == NULL)
   return tls_error(US "tls_session_init", host, GNUTLS_E_MEMORY_ERROR);
 
index 297b68b0de59b4d84b999f1814b536ab54124399..1ae725b865107cb5d8a90bb4e55a4b07228e8e9c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/tls-openssl.c,v 1.9 2007/01/08 10:50:18 ph10 Exp $ */
+/* $Cambridge: exim/src/src/tls-openssl.c,v 1.10 2007/01/18 15:35:42 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -598,6 +598,11 @@ 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
@@ -606,7 +611,8 @@ Returns:            OK on success
 */
 
 int
-tls_server_start(uschar *require_ciphers)
+tls_server_start(uschar *require_ciphers, uschar *require_mac,
+  uschar *require_kx, uschar *require_proto)
 {
 int rc;
 uschar *expciphers;
@@ -746,12 +752,19 @@ return OK;
 Argument:
   fd               the fd of the connection
   host             connected host (for messages)
+  addr             the first address
   dhparam          DH parameter file
   certificate      certificate file
   privatekey       private key file
   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
                    FAIL otherwise - note that tls_error() will not give DEFER
@@ -761,7 +774,8 @@ Returns:           OK on success
 int
 tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
   uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl,
-  uschar *require_ciphers, int timeout)
+  uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
+  uschar *require_proto, int timeout)
 {
 static uschar txt[256];
 uschar *expciphers;
index a5a96aeed024837d93b385c51c690f3326ff89c1..887b1ff821a897630a95a73d7007e92d5b637de8 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.30 2007/01/08 10:50:20 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.c,v 1.31 2007/01/18 15:35:43 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -63,6 +63,14 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, final_timeout) },
   { "gethostbyname",        opt_bool,
       (void *)offsetof(smtp_transport_options_block, gethostbyname) },
+  #ifdef SUPPORT_TLS
+  { "gnutls_require_kx",    opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) },
+  { "gnutls_require_mac",   opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, gnutls_require_mac) },
+  { "gnutls_require_protocols", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, gnutls_require_proto) },
+  #endif
   { "helo_data",            opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, helo_data) },
   { "hosts",                opt_stringptr,
@@ -178,6 +186,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* tls_crl */
   NULL,                /* tls_privatekey */
   NULL,                /* tls_require_ciphers */
+  NULL,                /* gnutls_require_kx */
+  NULL,                /* gnutls_require_mac */
+  NULL,                /* gnutls_require_proto */
   NULL,                /* tls_verify_certificates */
   TRUE                 /* tls_tempfail_tryclear */
   #endif
@@ -1053,13 +1064,18 @@ if (tls_offered && !suppress_tls &&
 
   else
     {
-    int rc = tls_client_start(inblock.sock, host, addrlist,
+    int rc = tls_client_start(inblock.sock,
+      host,
+      addrlist,
       NULL,                    /* No DH param */
       ob->tls_certificate,
       ob->tls_privatekey,
       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
index e759286bc37e04085dfb162df03746615a6df581..4f5dec707e78fd37016daa3be7793512b9bb2e5f 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/transports/smtp.h,v 1.10 2007/01/08 10:50:20 ph10 Exp $ */
+/* $Cambridge: exim/src/src/transports/smtp.h,v 1.11 2007/01/18 15:35:43 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -50,6 +50,9 @@ typedef struct {
   uschar *tls_crl;
   uschar *tls_privatekey;
   uschar *tls_require_ciphers;
+  uschar *gnutls_require_kx;
+  uschar *gnutls_require_mac;
+  uschar *gnutls_require_proto;
   uschar *tls_verify_certificates;
   BOOL    tls_tempfail_tryclear;
   #endif
index f5f707eb1b225ecceb4589e49cf10e522aaaef0d..334ca894f36d6c028410449135cf12b47866c20a 100644 (file)
@@ -1,6 +1,10 @@
 # Exim test configuration 2011
 
 SERVER =
+CREQCIP =
+CREQMAC =
+SREQCIP =
+SREQMAC =
 
 exim_path = EXIM_PATH
 host_lookup_order = bydns
@@ -28,6 +32,9 @@ tls_privatekey = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}
 tls_verify_hosts = *
 tls_verify_certificates = ${if eq {SERVER}{server}{DIR/aux-fixed/cert2}fail}
 
+SREQCIP
+SREQMAC
+
 
 # ----- Routers -----
 
@@ -52,7 +59,7 @@ send_to_server:
   port = PORT_D
   tls_certificate = DIR/aux-fixed/cert2
   tls_privatekey = DIR/aux-fixed/cert2
-  tls_require_ciphers = IDEA-CBC-MD5 \
-    ${if eq{$host_address}{127.0.0.1}{:DES-CBC3-SHA:RSA_ARCFOUR_SHA}}
+  CREQCIP
+  CREQMAC
 
 # End
index 477754e37ed2ab3e72844f08b9f6cebc137ede02..7fc966b95ef1a709e1f013223b048b3df312bc8a 100644 (file)
@@ -1,11 +1,50 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] (gnutls_handshake): No supported cipher suites have been found.
-1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to 127.0.0.1 [127.0.0.1] (gnutls_handshake): No supported cipher suites have been found.
+1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to 127.0.0.1 [127.0.0.1] (not in hosts_require_tls)
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmbA-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmbB-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmbC-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmbD-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=SSL 3.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmbE-0005vi-00 => userx@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel"
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
 
 ******** SERVER ********
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
 1999-03-02 09:44:33 TLS error on connection from the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] (gnutls_handshake): A TLS packet with unexpected length was received.
-1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 TLS error on connection from localhost (myhost.test.ex) [127.0.0.1] (gnutls_handshake): A TLS packet with unexpected length was received.
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_SHA1:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=SSL 3.0:RSA_AES_256_CBC_SHA1:32 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex H=the.local.host.name (myhost.test.ex) [ip4.ip4.ip4.ip4] P=esmtps X=TLS-1.0:RSA_ARCFOUR_MD5:16 DN="C=UK,L=Cambridge,O=University of Cambridge,OU=Computing Service,CN=Philip Hazel" S=sss id=E10HmaX-0005vi-00@myhost.test.ex
index 4e48ebce6f236cbce4f25c1025775a732f999c98..6f72fba80293ed9999ec28b22f2e6b8f3b2f6b49 100644 (file)
@@ -1,10 +1,38 @@
-# TLS client: require_ciphers
+# TLS client & server: (gnu)tls_require_xxx
 gnutls
+# Start up the server
 exim -DSERVER=server -bd -oX PORT_D
 ****
+# This puts a message on the queue (queue_only is set).
 exim userx@test.ex
 Testing
 ****
+# This will fail to deliver encrypted because there are no acceptable 
+# ciphers, so it will deliver in clear.
+exim -qf -DCREQCIP=tls_require_ciphers=IDEA-CBC-MD5
+****
+# This delivers the message to the server, where it will remain
+# on the queue because queue_only is set.
+exim -qf -DCREQCIP=tls_require_ciphers=IDEA-CBC-MD5:DES-CBC3-SHA:RSA_ARCFOUR_SHA
+****
+# So we can deliver it again and again, with different parameters.
+exim -qf -DCREQMAC=gnutls_require_mac=MD5
+****
+exim -qf -DCREQMAC=gnutls_require_mac=!SHA1
+****
+exim -qf -DCREQMAC=gnutls_require_mac=MD5:SHA
+****
+exim -qf -DCREQMAC=gnutls_require_kx=!DHE
+****
+exim -qf -DCREQMAC=gnutls_require_protocols=SSL3
+****
+# Restart the server with a cipher restriction
+killdaemon
+exim -DSERVER=server \
+     -DSREQCIP=tls_require_ciphers=ARCFOUR \
+     -DSREQMAC=gnutls_require_mac=MD5 \
+     -bd -oX PORT_D
+****
 exim -qf
 ****
 killdaemon