#include "gsasl_exim.h"
-#if GSASL_VERSION_MINOR >= 10
-# define EXIM_GSASL_HAVE_SCRAM_SHA_256
-# define EXIM_GSASL_SCRAM_S_KEY
+#if GSASL_VERSION_MAJOR == 2
-#elif GSASL_VERSION_MINOR == 9
# define EXIM_GSASL_HAVE_SCRAM_SHA_256
+# define EXIM_GSASL_SCRAM_S_KEY
+# if GSASL_VERSION_MINOR >= 1
+# define EXIM_GSASL_HAVE_EXPORTER
+# elif GSASL_VERSION_PATCH >= 1
+# define EXIM_GSASL_HAVE_EXPORTER
+# endif
-# if GSASL_VERSION_PATCH >= 1
+#elif GSASL_VERSION_MAJOR == 1
+# if GSASL_VERSION_MINOR >= 10
+# define EXIM_GSASL_HAVE_SCRAM_SHA_256
# define EXIM_GSASL_SCRAM_S_KEY
-# endif
-# if GSASL_VERSION_PATCH < 2
+
+# elif GSASL_VERSION_MINOR == 9
+# define EXIM_GSASL_HAVE_SCRAM_SHA_256
+
+# if GSASL_VERSION_PATCH >= 1
+# define EXIM_GSASL_SCRAM_S_KEY
+# endif
+# if GSASL_VERSION_PATCH < 2
+# define CHANNELBIND_HACK
+# endif
+
+# else
# define CHANNELBIND_HACK
# endif
-
-#else
-# define CHANNELBIND_HACK
#endif
/* Convenience for testing strings */
if (prop == GSASL_CB_TLS_UNIQUE)
{
uschar * s;
- if ((s = gsasl_callback_hook_get(ctx)))
+ if ((s = gsasl_callback_hook_get(ctx))) /* Gross hack for early lib vers */
{
HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
#ifdef EXIM_GSASL_SCRAM_S_KEY
case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
+#endif
+#ifdef EXIM_GSASL_HAVE_EXPORTER /* v. 2.1.0 */
+ case GSASL_CB_TLS_EXPORTER: return US"CB_TLS_EXPORTER";
#endif
case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
return CUS string_sprintf("(unknown prop: %d)", (int)prop);
}
+static void
+preload_prop(Gsasl_session * sctx, Gsasl_property propcode, const uschar * val)
+{
+DEBUG(D_auth) debug_printf("preloading prop %s val %s\n",
+ gsasl_prop_code_to_name(propcode), val);
+gsasl_property_set(sctx, propcode, CCS val);
+}
+
/*************************************************
* Server entry point *
*************************************************/
/* For interface, see auths/README */
int
-auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
+auth_gsasl_server(auth_instance * ablock, uschar * initial_data)
{
-char *tmps;
-char *to_send, *received;
-Gsasl_session *sctx = NULL;
-auth_gsasl_options_block *ob =
+uschar * tmps;
+char * to_send, * received;
+Gsasl_session * sctx = NULL;
+auth_gsasl_options_block * ob =
(auth_gsasl_options_block *)(ablock->options_block);
struct callback_exim_state cb_state;
int rc, auth_result, exim_error, exim_error_override;
cb_state.currently = CURRENTLY_SERVER;
gsasl_session_hook_set(sctx, &cb_state);
-tmps = CS expand_string(ob->server_service);
-gsasl_property_set(sctx, GSASL_SERVICE, tmps);
-tmps = CS expand_string(ob->server_hostname);
-gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
+tmps = expand_string(ob->server_service);
+preload_prop(sctx, GSASL_SERVICE, tmps);
+tmps = expand_string(ob->server_hostname);
+preload_prop(sctx, GSASL_HOSTNAME, tmps);
if (ob->server_realm)
{
- tmps = CS expand_string(ob->server_realm);
+ tmps = expand_string(ob->server_realm);
if (tmps && *tmps)
- gsasl_property_set(sctx, GSASL_REALM, tmps);
+ preload_prop(sctx, GSASL_REALM, tmps);
}
/* We don't support protection layers. */
-gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
+preload_prop(sctx, GSASL_QOPS, US "qop-auth");
#ifndef DISABLE_TLS
if (tls_in.channelbinding)
HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
ablock->name);
# ifndef CHANNELBIND_HACK
- gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
+ preload_prop(sctx,
+# ifdef EXIM_GSASL_HAVE_EXPORTER
+ tls_in.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
+# endif
+ GSASL_CB_TLS_UNIQUE,
+ tls_in.channelbinding);
# endif
}
else
int
auth_gsasl_client(
- auth_instance *ablock, /* authenticator block */
+ auth_instance * ablock, /* authenticator block */
void * sx, /* connection */
int timeout, /* command timeout */
- uschar *buffer, /* buffer for reading response */
+ uschar * buffer, /* buffer for reading response */
int buffsize) /* size of buffer */
{
-auth_gsasl_options_block *ob =
+auth_gsasl_options_block * ob =
(auth_gsasl_options_block *)(ablock->options_block);
Gsasl_session * sctx = NULL;
struct callback_exim_state cb_state;
HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
ablock->name);
# ifndef CHANNELBIND_HACK
- gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
+ preload_prop(sctx,
+# ifdef EXIM_GSASL_HAVE_EXPORTER
+ tls_out.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
+# endif
+ GSASL_CB_TLS_UNIQUE,
+ tls_out.channelbinding);
# endif
}
else
gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
switch (prop)
{
- case GSASL_CB_TLS_UNIQUE: /*XXX should never get called for this */
- HDEBUG(D_auth)
- debug_printf(" filling in\n");
+#ifdef EXIM_GSASL_HAVE_EXPORTER
+ case GSASL_CB_TLS_EXPORTER: /* Should never get called for this, as pre-set */
+ if (!tls_out.channelbind_exporter) break;
+ HDEBUG(D_auth) debug_printf(" filling in\n");
+ gsasl_property_set(sctx, GSASL_CB_TLS_EXPORTER, CCS tls_out.channelbinding);
+ return GSASL_OK;
+#endif
+ case GSASL_CB_TLS_UNIQUE: /* Should never get called for this, as pre-set */
+#ifdef EXIM_GSASL_HAVE_EXPORTER
+ if (tls_out.channelbind_exporter) break;
+#endif
+ HDEBUG(D_auth) debug_printf(" filling in\n");
gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
return GSASL_OK;
case GSASL_SCRAM_SALTED_PASSWORD:
# endif
#endif
+#if GNUTLS_VERSION_NUMBER >= 0x030702
+# define HAVE_GNUTLS_EXPORTER
+#endif
+
#ifndef DISABLE_OCSP
# include <gnutls/ocsp.h>
#endif
#ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
{
gnutls_datum_t channel = {.data = NULL, .size = 0};
- uschar * buf;
int rc;
-# ifdef HAVE_GNUTLS_PRF_RFC5705
+# ifdef HAVE_GNUTLS_EXPORTER
+ if (gnutls_protocol_get_version(state->session) >= GNUTLS_TLS1_3)
+ {
+ rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_EXPORTER, &channel);
+ tlsp->channelbind_exporter = TRUE;
+ }
+ else
+# elif defined(HAVE_GNUTLS_PRF_RFC5705)
/* Older libraries may not have GNUTLS_TLS1_3 defined! */
if (gnutls_protocol_get_version(state->session) > GNUTLS_TLS1_2)
{
- buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED);
+ uschar * buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED);
rc = gnutls_prf_rfc5705(state->session,
(size_t)24, "EXPORTER-Channel-Binding", (size_t)0, "",
32, CS buf);
{
int old_pool = store_pool;
/* Declare the taintedness of the binding info. On server, untainted; on
- client, tainted - being the Finish msg from the server. */
+ client, tainted if we used the Finish msg from the server. */
store_pool = POOL_PERM;
tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
- state->host ? GET_TAINTED : GET_UNTAINTED);
+ !tlsp->channelbind_exporter && state->host ? GET_TAINTED : GET_UNTAINTED);
store_pool = old_pool;
DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
}
# define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
#if !defined(LIBRESSL_VERSION_NUMBER) \
|| LIBRESSL_VERSION_NUMBER >= 0x20010000L
# if !defined(OPENSSL_NO_ECDH)
# define OPENSSL_HAVE_KEYLOG_CB
# define OPENSSL_HAVE_NUM_TICKETS
# define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# define EXIM_HAVE_EXP_CHNL_BNGNG
# else
# define OPENSSL_BAD_SRVR_OURCERT
# endif
#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x010002000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
# define DISABLE_OCSP
}
+/* Channel-binding info for authenticators
+See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/
+for pre-TLS1.3
+*/
+
+static void
+tls_get_channel_binding(SSL * ssl, tls_support * tlsp, const void * taintval)
+{
+uschar c, * s;
+size_t len;
+
+#ifdef EXIM_HAVE_EXPORT_CHNL_BNGNG
+if (SSL_version(ssl) >= TLS1_3_VERSION)
+ {
+ /* It's not documented by OpenSSL how big the output buffer must be.
+ The OpenSSL testcases use 80 bytes but don't say why. The GnuTLS impl only
+ serves out 32B. RFC 9266 says it is 32B.
+ Interop fails unless we use the same each end. */
+ len = 32;
+
+ tlsp->channelbind_exporter = TRUE;
+ taintval = GET_UNTAINTED;
+ if (SSL_export_keying_material(ssl,
+ s = store_get((int)len, taintval), len,
+ "EXPORTER-Channel-Binding", (size_t) 24,
+ NULL, 0, 0) != 1)
+ len = 0;
+ }
+else
+#endif
+ {
+ len = SSL_get_peer_finished(ssl, &c, 0);
+ len = SSL_get_peer_finished(ssl, s = store_get((int)len, taintval), len);
+ }
+
+if (len > 0)
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ tlsp->channelbinding = b64encode_taint(CUS s, (int)len, taintval);
+ store_pool = old_pool;
+ DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
+ }
+}
+
+
/*************************************************
* Start a TLS session in a server *
*************************************************/
adjust the input functions to read via TLS, and initialize things. */
#ifdef SSL_get_extms_support
+/*XXX what does this return for tls1.3 ? */
tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1;
#endif
peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn));
tls_in.ourcert = crt ? X509_dup(crt) : NULL;
}
-/* Channel-binding info for authenticators
-See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
- {
- uschar c, * s;
- size_t len = SSL_get_peer_finished(ssl, &c, 0);
- int old_pool = store_pool;
-
- SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len);
- store_pool = POOL_PERM;
- tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
- }
+tls_get_channel_binding(ssl, &tls_in, GET_UNTAINTED);
/* Only used by the server-side tls (tls_in), including tls_getc.
Client-side (tls_out) reads (seem to?) go via
}
/*XXX will this work with continued-TLS? */
-/* Channel-binding info for authenticators */
- {
- uschar c, * s;
- size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
- int old_pool = store_pool;
-
- SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len);
- store_pool = POOL_PERM;
- tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
- }
+tls_get_channel_binding(exim_client_ctx->ssl, tlsp, GET_TAINTED);
tlsp->active.sock = cctx->sock;
tlsp->active.tls_ctx = exim_client_ctx;