[exim-cvs] Various SASL fixes.

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Various SASL fixes.
Gitweb: http://git.exim.org/exim.git/commitdiff/edc33b5f1aca3f17ee8ca0b93689e6d14009df54
Commit:     edc33b5f1aca3f17ee8ca0b93689e6d14009df54
Parent:     8ebb1c9ea93eb27500756a578640d890b53264d3
Author:     Phil Pennock <pdp@???>
AuthorDate: Sat Feb 4 02:26:27 2012 -0500
Committer:  Phil Pennock <pdp@???>
CommitDate: Sat Feb 4 02:26:27 2012 -0500


    Various SASL fixes.


    Export $tls_bits new expansion variable (not yet documented).
    Fix tls-gnu.c so that ciphername string construction uses bit-count, not byte-count.
    Avoid hard-coding primary_hostname in first call to init Cyrus SASL.
    Cast fix for function pointer (Cyrus-SASL uses void params in struct entry funcptr, so need to cast).
    Many more debug statements in cyrus_sasl.c
    Pass external SSF from TLS cipher into Cyrus SASL initialisation.
    Detect when we can't get an identity from SASL properties (error out correctly).
    Detect when SASL negotiated a protection layer and error out, since we do not support those.
---
 src/src/auths/cyrus_sasl.c |   84 ++++++++++++++++++++++++++++++++++++++++---
 src/src/expand.c           |    1 +
 src/src/globals.c          |    1 +
 src/src/globals.h          |    1 +
 src/src/tls-gnu.c          |    7 ++--
 src/src/tls-openssl.c      |    5 +--
 6 files changed, 87 insertions(+), 12 deletions(-)


diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c
index fea1def..e61625e 100644
--- a/src/src/auths/cyrus_sasl.c
+++ b/src/src/auths/cyrus_sasl.c
@@ -98,7 +98,7 @@ auth_cyrus_sasl_options_block *ob =
uschar *list, *listptr, *buffer;
int rc, i;
unsigned int len;
-uschar *rs_point;
+uschar *rs_point, *expanded_hostname;

sasl_conn_t *conn;
sasl_callback_t cbs[]={
@@ -109,11 +109,17 @@ sasl_callback_t cbs[]={
if(ob->server_mech == NULL)
ob->server_mech=string_copy(ablock->public_name);

+expanded_hostname = expand_string(ob->server_hostname);
+if (expanded_hostname == NULL)
+  log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
+      "couldn't expand server_hostname [%s]: %s",
+      ablock->name, ob->server_hostname, expand_string_message);
+
 /* we're going to initialise the library to check that there is an
  * authenticator of type whatever mechanism we're using
  */


-cbs[0].proc = &mysasl_config;
+cbs[0].proc = (int(*)(void)) &mysasl_config;
cbs[0].context = ob->server_mech;

 rc=sasl_server_init(cbs, "exim");
@@ -122,7 +128,7 @@ if( rc != SASL_OK )
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
       "couldn't initialise Cyrus SASL library.", ablock->name);


-rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
+rc=sasl_server_new(CS ob->server_service, CS expanded_hostname,
                    CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
 if( rc != SASL_OK )
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
@@ -136,7 +142,11 @@ if( rc != SASL_OK )
 i=':';
 listptr=list;


-HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list);
+HDEBUG(D_auth) {
+  debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
+      ob->server_service, expanded_hostname, ob->server_realm);
+  debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
+}


/* the store_get / store_reset mechanism is hierarchical
* the hierarchy is stored for us behind our back. This point
@@ -184,7 +194,7 @@ uschar *output, *out2, *input, *clear, *hname;
uschar *debug = NULL; /* Stops compiler complaining */
sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
sasl_conn_t *conn;
-int rc, firsttime=1, clen;
+int rc, firsttime=1, clen, negotiated_ssf;
unsigned int inlen, outlen;

input=data;
@@ -220,6 +230,10 @@ if (rc != SASL_OK)
rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
NULL, NULL, 0, &conn);

+HDEBUG(D_auth)
+  debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
+      ob->server_service, hname, ob->server_realm);
+
 if( rc != SASL_OK )
   {
   auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
@@ -227,6 +241,23 @@ if( rc != SASL_OK )
   return DEFER;
   }


+if (tls_cipher)
+  {
+  rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, &tls_bits);
+  if (rc != SASL_OK)
+    {
+    HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
+        tls_bits, sasl_errstring(rc, NULL, NULL));
+    auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
+    sasl_done();
+    return DEFER;
+    }
+  else
+    HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_bits);
+  }
+else
+  HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
+
 rc=SASL_CONTINUE;


 while(rc==SASL_CONTINUE)
@@ -325,12 +356,53 @@ while(rc==SASL_CONTINUE)
     /* Get the username and copy it into $auth1 and $1. The former is now the
     preferred variable; the latter is the original variable. */
     rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
+    if (rc != SASL_OK)
+      {
+      HDEBUG(D_auth)
+        debug_printf("Cyrus SASL library will not tell us the username: %s\n",
+            sasl_errstring(rc, NULL, NULL));
+      log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
+         "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
+         sasl_errstring(rc, NULL, NULL));
+      sasl_dispose(&conn);
+      sasl_done();
+      return FAIL;
+      }
+
     auth_vars[0] = expand_nstring[1] = string_copy(out2);
     expand_nlength[1] = Ustrlen(expand_nstring[1]);
     expand_nmax = 1;


     HDEBUG(D_auth)
-      debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
+      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));
+    if (rc != SASL_OK)
+      {
+      HDEBUG(D_auth)
+        debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
+            sasl_errstring(rc, NULL, NULL));
+      log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
+          "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
+          sasl_errstring(rc, NULL, NULL));
+      sasl_dispose(&conn);
+      sasl_done();
+      return FAIL;
+      }
+    HDEBUG(D_auth)
+      debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
+    if (negotiated_ssf > 0)
+      {
+      HDEBUG(D_auth)
+        debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
+      log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
+          "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
+      sasl_dispose(&conn);
+      sasl_done();
+      return FAIL;
+      }
+
     /* close down the connection, freeing up library's memory */
     sasl_dispose(&conn);
     sasl_done();
diff --git a/src/src/expand.c b/src/src/expand.c
index a3dc590..54501de 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -611,6 +611,7 @@ static var_entry var_table[] = {
   { "srs_status",          vtype_stringptr,   &srs_status },
 #endif
   { "thisaddress",         vtype_stringptr,   &filter_thisaddress },
+  { "tls_bits",            vtype_int,         &tls_bits },
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
   { "tls_cipher",          vtype_stringptr,   &tls_cipher },
   { "tls_peerdn",          vtype_stringptr,   &tls_peerdn },
diff --git a/src/src/globals.c b/src/src/globals.c
index a5516b9..6124cb5 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -94,6 +94,7 @@ cluttered in several places (e.g. during logging) if we can always refer to
 them. Also, the tls_ variables are now always visible. */


 BOOL    tls_active             = -1;
+int     tls_bits               = 0;
 BOOL    tls_certificate_verified = FALSE;
 uschar *tls_cipher             = NULL;
 BOOL    tls_on_connect         = FALSE;
diff --git a/src/src/globals.h b/src/src/globals.h
index 4ed3950..869a23e 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -75,6 +75,7 @@ cluttered in several places (e.g. during logging) if we can always refer to
 them. Also, the tls_ variables are now always visible. */


 extern int     tls_active;             /* fd/socket when in a TLS session */
+extern int     tls_bits;               /* bits used in TLS session */
 extern BOOL    tls_certificate_verified; /* Client certificate verified */
 extern uschar *tls_cipher;             /* Cipher used */
 extern BOOL    tls_on_connect;         /* For older MTAs that don't STARTTLS */
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index f77768f..dc09d47 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -854,20 +854,21 @@ construct_cipher_name(gnutls_session session)
 {
 static uschar cipherbuf[256];
 uschar *ver;
-int bits, c, kx, mac;
+int c, kx, mac;


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);
-bits = gnutls_cipher_get_key_size(c);
+/* returns size in "bytes" */
+tls_bits = gnutls_cipher_get_key_size(c) * 8;

mac = gnutls_mac_get(session);
kx = gnutls_kx_get(session);

string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver,
- gnutls_cipher_suite_get_name(kx, c, mac), bits);
+ gnutls_cipher_suite_get_name(kx, c, mac), tls_bits);
tls_cipher = cipherbuf;

DEBUG(D_tls) debug_printf("cipher: %s\n", cipherbuf);
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index e5d12cb..2104711 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -441,7 +441,6 @@ yet reflect that. It should be a safe change anyway, even 0.9.8 versions have
the accessor functions use const in the prototype. */
const SSL_CIPHER *c;
uschar *ver;
-int bits;

switch (ssl->session->ssl_version)
{
@@ -462,10 +461,10 @@ switch (ssl->session->ssl_version)
}

c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
-SSL_CIPHER_get_bits(c, &bits);
+SSL_CIPHER_get_bits(c, &tls_bits);

string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver,
- SSL_CIPHER_get_name(c), bits);
+ SSL_CIPHER_get_name(c), tls_bits);
tls_cipher = cipherbuf;

DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);