summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-docbook/spec.xfpt10
-rw-r--r--doc/doc-txt/ChangeLog4
-rw-r--r--doc/doc-txt/NewStuff2
-rw-r--r--src/src/auths/cyrus_sasl.c84
-rw-r--r--src/src/expand.c1
-rw-r--r--src/src/globals.c1
-rw-r--r--src/src/globals.h1
-rw-r--r--src/src/tls-gnu.c8
-rw-r--r--src/src/tls-openssl.c5
9 files changed, 104 insertions, 12 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 8afccbf1d..9c39b4aa2 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -11824,6 +11824,16 @@ command in a filter file. Its use is explained in the description of that
command, which can be found in the separate document entitled &'Exim's
interfaces to mail filtering'&.
+.new
+.vitem &$tls_bits$&
+.vindex "&$tls_bits$&"
+Contains an approximation of the TLS cipher's bit-strength; the meaning of
+this depends upon the TLS implementation used.
+If TLS has not been negotiated, the value will be 0.
+The value of this is automatically fed into the Cyrus SASL authenticator
+when acting as a server, to specify the "external SSF" (a SASL term).
+.wen
+
.vitem &$tls_certificate_verified$&
.vindex "&$tls_certificate_verified$&"
This variable is set to &"1"& if a TLS certificate was verified when the
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 699da323c..3f43ef83d 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -28,6 +28,10 @@ PP/05 Swapped $auth1/$auth2 for gsasl GSSAPI mechanism, to be more consistent
PP/06 Local/Makefile support for USE_(GNUTLS|OPENSSL)_PC=foo to use
`pkg-config foo` for cflags/libs for the TLS implementation.
+PP/07 New expansion variable $tls_bits; Cyrus SASL server connection
+ properties get this fed in as external SSF. A number of robustness
+ and debugging improvements to the cyrus_sasl authenticator.
+
Exim version 4.77
-----------------
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index 487ce30b3..057656c24 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -29,6 +29,8 @@ Version 4.78
"LOOKUP_LIBS" directly. Similarly for handling the TLS library support
without adjusting "TLS_INCLUDE" and "TLS_LIBS".
+ 4. New expansion variable $tls_bits.
+
Version 4.77
------------
diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c
index df7abc928..e61625e28 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 = (int(*)(void))&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 a3dc590cc..54501de0b 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 a5516b91d..6124cb585 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 58fdb0a0c..a51e3bc50 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 2d1a327de..2f952e47b 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -854,8 +854,9 @@ construct_cipher_name(gnutls_session session)
{
static uschar cipherbuf[256];
uschar *ver;
-int bits, c, kx, mac, rc;
+int c, kx, mac;
#ifdef GNUTLS_CB_TLS_UNIQUE
+int rc;
gnutls_datum_t channel;
#endif
@@ -864,13 +865,14 @@ ver = string_copy(
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 e5d12cb84..2104711bb 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);