LDAP TLS negotiation support.
authorPhil Pennock <pdp@exim.org>
Sat, 5 Feb 2011 05:22:28 +0000 (00:22 -0500)
committerPhil Pennock <pdp@exim.org>
Sat, 5 Feb 2011 05:22:28 +0000 (00:22 -0500)
closes bug 230
Applies patches provided by Adam Ciarcinski of NetBSD in bug 230.
Adds documentation.

Tested the negotiation and server verification, not tested the client
certificate presentation but looks sane.

doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/globals.c
src/src/globals.h
src/src/lookups/ldap.c
src/src/readconf.c

index 659a469bf1e30a90530b5d65003c0926f82193ef..9dacb979c5d012dd59a911a89281bd1e32010339 100644 (file)
@@ -6885,6 +6885,12 @@ The URL may begin with &`ldap`& or &`ldaps`& if your LDAP library supports
 secure (encrypted) LDAP connections. The second of these ensures that an
 encrypted TLS connection is used.
 
+.new
+With sufficiently modern LDAP libraries, Exim supports forcing TLS over regular
+LDAP connections, rather than the SSL-on-connect &`ldaps`&.
+See the &%ldap_start_tls%& option.
+.wen
+
 
 .section "LDAP quoting" "SECID68"
 .cindex "LDAP" "quoting"
@@ -12393,7 +12399,14 @@ listed in more than one group.
 .section "Data lookups" "SECID101"
 .table2
 .row &%ibase_servers%&               "InterBase servers"
+.row &%ldap_ca_cert_dir%&            "dir of CA certs to verify LDAP server's"
+.row &%ldap_ca_cert_file%&           "file of CA certs to verify LDAP server's"
+.row &%ldap_cert_file%&              "client cert file for LDAP"
+.row &%ldap_cert_key%&               "client key file for LDAP"
+.row &%ldap_cipher_suite%&           "TLS negotiation preference control"
 .row &%ldap_default_servers%&        "used if no server in query"
+.row &%ldap_require_cert%&           "action to take without LDAP server cert"
+.row &%ldap_start_tls%&              "require TLS within LDAP"
 .row &%ldap_version%&                "set protocol version"
 .row &%lookup_open_max%&             "lookup files held open"
 .row &%mysql_servers%&               "default MySQL servers"
@@ -13805,6 +13818,56 @@ next attempt to deliver such a message, it gets removed. The incident is
 logged.
 
 
+.new
+.option ldap_ca_cert_dir main string unset
+.cindex "LDAP", "TLS CA certificate directory"
+This option indicates which directory contains CA certificates for verifying
+a TLS certificate presented by an LDAP server.
+While Exim does not provide a default value, your SSL library may.
+Analogous to &%tls_verify_certificates%& but as a client-side option for LDAP
+and constrained to be a directory.
+.wen
+
+
+.new
+.option ldap_ca_cert_file main string unset
+.cindex "LDAP", "TLS CA certificate file"
+This option indicates which file contains CA certificates for verifying
+a TLS certificate presented by an LDAP server.
+While Exim does not provide a default value, your SSL library may.
+Analogous to &%tls_verify_certificates%& but as a client-side option for LDAP
+and constrained to be a file.
+.wen
+
+
+.new
+.option ldap_cert_file main string unset
+.cindex "LDAP" "TLS client certificate file"
+This option indicates which file contains an TLS client certificate which
+Exim should present to the LDAP server during TLS negotiation.
+Should be used together with &%ldap_cert_key%&.
+.wen
+
+
+.new
+.option ldap_cert_key main string unset
+.cindex "LDAP" "TLS client key file"
+This option indicates which file contains the secret/private key to use
+to prove identity to the LDAP server during TLS negotiation.
+Should be used together with &%ldap_cert_file%&, which contains the
+identity to be proven.
+.wen
+
+
+.new
+.option ldap_cipher_suite main string unset
+.cindex "LDAP" "TLS cipher suite"
+This controls the TLS cipher-suite negotiation during TLS negotiation with
+the LDAP server.  See &<<SECTreqciphssl>>& for more details of the format of
+cipher-suite options with OpenSSL (as used by LDAP client libraries).
+.wen
+
+
 .option ldap_default_servers main "string list" unset
 .cindex "LDAP" "default servers"
 This option provides a list of LDAP servers which are tried in turn when an
@@ -13813,6 +13876,29 @@ details of LDAP queries. This option is available only when Exim has been built
 with LDAP support.
 
 
+.new
+.option ldap_require_cert main string unset.
+.cindex "LDAP" "policy for LDAP server TLS cert presentation"
+This should be one of the values "hard", "demand", "allow", "try" or "never".
+A value other than one of these is interpreted as "never".
+See the entry "TLS_REQCERT" in your system man page for ldap.conf(5).
+Although Exim does not set a default, the LDAP library probably defaults
+to hard/demand.
+.wen
+
+
+.new
+.option ldap_start_tls main boolean false
+.cindex "LDAP" "whether or not to negotiate TLS"
+If set, Exim will attempt to negotiate TLS with the LDAP server when
+connecting on a regular LDAP port.  This is the LDAP equivalent of SMTP's
+"STARTTLS".  This is distinct from using "ldaps", which is the LDAP form
+of SSL-on-connect.
+In the event of failure to negotiate TLS, the action taken is controlled
+by &%ldap_require_cert%&.
+.wen
+
+
 .option ldap_version main integer unset
 .cindex "LDAP" "protocol version, forcing"
 This option can be used to force Exim to set a specific protocol version for
index 6e1bd45666d45c8466ce0b878eafa07cafc68f63..083870af63cfa177ea80c9d71eeae1549efc0d14 100644 (file)
@@ -32,6 +32,8 @@ NM/02 Fix wide character breakage in the rfc2047 coding
 NM/03 Allow underscore in dnslist lookups
       Fixes bug 1026. Patch from Graeme Fowler
 
+PP/04 Bugzilla 230: Support TLS-enabled LDAP (in addition to ldaps).
+      Code patches from Adam Ciarcinski of NetBSD.
 
 
 Exim version 4.74
index 3a3ad5de5fc326fb3464aed332c570007b9539e2..55bde992d89ffc19655056c2d1581d8791c03e6d 100644 (file)
@@ -9,6 +9,16 @@ test from the snapshots or the CVS before the documentation is updated. Once
 the documentation is updated, this file is reduced to a short list.
 
 
+Version 4.75
+------------
+
+ 1. In addition to the existing LDAP and LDAP/SSL ("ldaps") support, there
+    is now LDAP/TLS support, given sufficiently modern OpenLDAP client
+    libraries.  The following global options have been added in support of
+    this: ldap_ca_cert_dir, ldap_ca_cert_file, ldap_cert_file, ldap_cert_key,
+    ldap_cipher_suite, ldap_require_cert, ldap_start_tls.
+
+
 Version 4.74
 ------------
 
index 6653d62dfb3bdfa107b51ad639f31edbd3794190..8631c7d8cc42031029e3cd1a45f7ad6086b27820 100644 (file)
@@ -60,8 +60,15 @@ uschar *ibase_servers          = NULL;
 #endif
 
 #ifdef LOOKUP_LDAP
+uschar *eldap_ca_cert_dir      = NULL;
+uschar *eldap_ca_cert_file     = NULL;
+uschar *eldap_cert_file        = NULL;
+uschar *eldap_cert_key         = NULL;
+uschar *eldap_cipher_suite     = NULL;
 uschar *eldap_default_servers  = NULL;
+uschar *eldap_require_cert     = NULL;
 int     eldap_version          = -1;
+BOOL    eldap_start_tls        = FALSE;
 #endif
 
 #ifdef LOOKUP_MYSQL
index db7a79bf7f4431ae3484bdbbe138111ac6baa3b1..bdc9bcf6dcdcefc3ed275b2112f0868ddef6454a 100644 (file)
@@ -35,7 +35,14 @@ extern uschar *ibase_servers;
 #endif
 
 #ifdef LOOKUP_LDAP
+extern uschar *eldap_ca_cert_dir;      /* Directory with CA certificates */
+extern uschar *eldap_ca_cert_file;     /* CA certificate file */
+extern uschar *eldap_cert_file;        /* Certificate file */
+extern uschar *eldap_cert_key;         /* Certificate key file */
+extern uschar *eldap_cipher_suite;     /* Allowed cipher suite */
 extern uschar *eldap_default_servers;  /* List of default servers */
+extern uschar *eldap_require_cert;     /* Peer certificate checking strategy */
+extern BOOL    eldap_start_tls;        /* Use STARTTLS */
 extern int     eldap_version;          /* LDAP version */
 #endif
 
index 943e431db99e53bf5d7a1a4fa13eb9646aabc91d..ddf803e21047da98a3ce5141fa9171d15b5fa812 100644 (file)
@@ -431,6 +431,60 @@ if (lcp == NULL)
     }
   #endif  /* LDAP_OPT_X_TLS */
 
+  #ifdef LDAP_OPT_X_TLS_CACERTFILE
+  if (eldap_ca_cert_file != NULL)
+    {
+    ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, eldap_ca_cert_file);
+    }
+  #endif
+  #ifdef LDAP_OPT_X_TLS_CACERTDIR
+  if (eldap_ca_cert_dir != NULL)
+    {
+    ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, eldap_ca_cert_dir);
+    }
+  #endif
+  #ifdef LDAP_OPT_X_TLS_CERTFILE
+  if (eldap_cert_file != NULL)
+    {
+    ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, eldap_cert_file);
+    }
+  #endif
+  #ifdef LDAP_OPT_X_TLS_KEYFILE
+  if (eldap_cert_key != NULL)
+    {
+    ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, eldap_cert_key);
+    }
+  #endif
+  #ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
+  if (eldap_cipher_suite != NULL)
+    {
+    ldap_set_option(ld, LDAP_OPT_X_TLS_CIPHER_SUITE, eldap_cipher_suite);
+    }
+  #endif
+  #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
+  if (eldap_require_cert != NULL)
+    {
+    int cert_option = LDAP_OPT_X_TLS_NEVER;
+    if (Ustrcmp(eldap_require_cert, "hard") == 0)
+      {
+      cert_option = LDAP_OPT_X_TLS_HARD;
+      }
+    else if (Ustrcmp(eldap_require_cert, "demand") == 0)
+      {
+      cert_option = LDAP_OPT_X_TLS_DEMAND;
+      }
+    else if (Ustrcmp(eldap_require_cert, "allow") == 0)
+      {
+      cert_option = LDAP_OPT_X_TLS_ALLOW;
+      }
+    else if (Ustrcmp(eldap_require_cert, "try") == 0)
+      {
+      cert_option = LDAP_OPT_X_TLS_TRY;
+      }
+    ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, cert_option);
+    }
+  #endif
+
   /* Now add this connection to the chain of cached connections */
 
   lcp = store_get(sizeof(LDAP_CONNECTION));
@@ -467,6 +521,10 @@ if (!lcp->bound ||
   {
   DEBUG(D_lookup) debug_printf("%sbinding with user=%s password=%s\n",
     (lcp->bound)? "re-" : "", user, password);
+  if (eldap_start_tls)
+    {
+    ldap_start_tls_s(lcp->ld, NULL, NULL);
+    }
   if ((msgid = ldap_bind(lcp->ld, CS user, CS password, LDAP_AUTH_SIMPLE))
        == -1)
     {
index 0b78958e4f0e6222bca6c3dea52bc851f0a8b231..b9d3747a5e6eb7d887579e1cd0dcdf563e7939a7 100644 (file)
@@ -262,7 +262,14 @@ static optionlist optionlist_config[] = {
   { "ignore_fromline_local",    opt_bool,        &ignore_fromline_local },
   { "keep_malformed",           opt_time,        &keep_malformed },
 #ifdef LOOKUP_LDAP
+  { "ldap_ca_cert_dir",         opt_stringptr,   &eldap_ca_cert_dir },
+  { "ldap_ca_cert_file",        opt_stringptr,   &eldap_ca_cert_file },
+  { "ldap_cert_file",           opt_stringptr,   &eldap_cert_file },
+  { "ldap_cert_key",            opt_stringptr,   &eldap_cert_key },
+  { "ldap_cipher_suite",        opt_stringptr,   &eldap_cipher_suite },
   { "ldap_default_servers",     opt_stringptr,   &eldap_default_servers },
+  { "ldap_require_cert",        opt_stringptr,   &eldap_require_cert },
+  { "ldap_start_tls",           opt_bool,        &eldap_start_tls },
   { "ldap_version",             opt_int,         &eldap_version },
 #endif
   { "local_from_check",         opt_bool,        &local_from_check },