From b1a32a3ce673130f4b2f49a341b11c3567081637 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 17 Nov 2019 19:30:42 +0000 Subject: [PATCH] OpenSSL: support authenticator channel-binding. Bug 2467 --- doc/doc-txt/NewStuff | 3 +++ src/src/auths/gsasl_exim.c | 4 ++-- src/src/base64.c | 10 ++++++++-- src/src/functions.h | 1 + src/src/globals.h | 2 +- src/src/tls-gnu.c | 18 +++++++++++------- src/src/tls-openssl.c | 28 ++++++++++++++++++++++++++++ src/src/tls.c | 2 -- 8 files changed, 54 insertions(+), 14 deletions(-) diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index fbd1a5e4e..18c3d3024 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -14,6 +14,9 @@ Version 4.next 2. Variables $tls_in_ver, $tls_out_ver. + 3. Channel-binding for authenticators is now supported under OpenSSL. + Previously it was GnuTLS-only. + Version 4.93 ------------ diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index 06c91ea3f..78a63cd0e 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -292,7 +292,7 @@ if (ob->server_realm) gsasl_property_set(sctx, GSASL_QOPS, "qop-auth"); #ifndef DISABLE_TLS -if (tls_channelbinding_b64) +if (tls_in.channelbinding) { /* Some auth mechanisms can ensure that both sides are talking withing the same security context; for TLS, this means that even if a bad certificate @@ -317,7 +317,7 @@ if (tls_channelbinding_b64) HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n", ablock->name); gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, - CCS tls_channelbinding_b64); + CCS tls_in.channelbinding); } else HDEBUG(D_auth) diff --git a/src/src/base64.c b/src/src/base64.c index 6c8191462..aa46c2b32 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -242,9 +242,9 @@ static uschar *enc64table = US"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; uschar * -b64encode(const uschar * clear, int len) +b64encode_taint(const uschar * clear, int len, BOOL tainted) { -uschar *code = store_get(4*((len+2)/3) + 1, is_tainted(clear)); +uschar *code = store_get(4*((len+2)/3) + 1, tainted); uschar *p = code; while (len-- >0) @@ -283,6 +283,12 @@ while (len-- >0) return code; } +uschar * +b64encode(const uschar * clear, int len) +{ +return b64encode_taint(clear, len, is_tainted(clear)); +} + /* End of base64.c */ /* vi: sw ai sw=2 diff --git a/src/src/functions.h b/src/src/functions.h index 187bdafa6..da21b8779 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -136,6 +136,7 @@ extern gstring *authres_spf(gstring *); #endif extern uschar *b64encode(const uschar *, int); +extern uschar *b64encode_taint(const uschar *, int, BOOL); extern int b64decode(const uschar *, uschar **); extern int bdat_getc(unsigned); extern uschar *bdat_getbuf(unsigned *); diff --git a/src/src/globals.h b/src/src/globals.h index 1754d3e89..8a3d4c56f 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -97,6 +97,7 @@ typedef struct { void *peercert; /* Certificate of peer, binary */ uschar *peerdn; /* DN from peer */ uschar *sni; /* Server Name Indication */ + uschar *channelbinding; /* b64'd data identifying channel, for authenticators */ enum { OCSP_NOT_REQ=0, /* not requested */ OCSP_NOT_RESP, /* no response to request */ @@ -120,7 +121,6 @@ extern BOOL gnutls_allow_auto_pkcs11; /* Let GnuTLS autoload PKCS11 modules * extern uschar *openssl_options; /* OpenSSL compatibility options */ extern const pcre *regex_STARTTLS; /* For recognizing STARTTLS settings */ extern uschar *tls_certificate; /* Certificate file */ -extern uschar *tls_channelbinding_b64; /* string of base64 channel binding */ extern uschar *tls_crl; /* CRL File */ extern int tls_dh_max_bits; /* don't accept higher lib suggestions */ extern uschar *tls_dhparam; /* DH param file */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 7d7f61dd8..f3c3835fe 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -155,7 +155,7 @@ Some of these correspond to variables in globals.c; those variables will be set to point to content in one of these instances, as appropriate for the stage of the process lifetime. -Not handled here: global tls_channelbinding_b64. +Not handled here: global tlsp->tls_channelbinding. */ typedef struct exim_gnutls_state { @@ -467,7 +467,7 @@ Sets: tls_active fd tls_bits strength indicator tls_certificate_verified bool indicator - tls_channelbinding_b64 for some SASL mechanisms + tls_channelbinding for some SASL mechanisms tls_ver a string tls_cipher a string tls_peercert pointer to library internal @@ -499,10 +499,10 @@ tlsp->certificate_verified = state->peer_cert_verified; tlsp->dane_verified = state->peer_dane_verified; #endif -/* note that tls_channelbinding_b64 is not saved to the spool file, since it's +/* note that tls_channelbinding is not saved to the spool file, since it's only available for use for authenticators while this TLS session is running. */ -tls_channelbinding_b64 = NULL; +tlsp->channelbinding = NULL; #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING channel.data = NULL; channel.size = 0; @@ -510,11 +510,15 @@ if ((rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_UNIQUE, & { DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); } else { + /* Declare the taintedness of the binding info. On server, untainted; on + client, tainted - being the Finish msg from the server. */ + old_pool = store_pool; store_pool = POOL_PERM; - tls_channelbinding_b64 = b64encode(CUS channel.data, (int)channel.size); + tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size, + !!state->host); store_pool = old_pool; - DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n"); + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n"); } #endif @@ -3093,7 +3097,7 @@ gnutls_certificate_free_credentials(state->x509_cred); tlsp->active.sock = -1; tlsp->active.tls_ctx = NULL; /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */ -tls_channelbinding_b64 = NULL; +tlsp->channelbinding = NULL; if (state->xfer_buffer) store_free(state->xfer_buffer); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 7a82e1d55..5ea4d964e 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2741,6 +2741,20 @@ DEBUG(D_tls) 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(server_ssl, &c, 0); + int old_pool = store_pool; + + SSL_get_peer_finished(server_ssl, s = store_get((int)len, FALSE), len); + store_pool = POOL_PERM; + tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE); + store_pool = old_pool; + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n"); + } + /* Only used by the server-side tls (tls_in), including tls_getc. Client-side (tls_out) reads (seem to?) go via smtp_read_response()/ip_recv(). @@ -3303,6 +3317,20 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); tlsp->ourcert = crt ? X509_dup(crt) : NULL; } +/*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, TRUE), len); + store_pool = POOL_PERM; + tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE); + store_pool = old_pool; + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n"); + } + tlsp->active.sock = cctx->sock; tlsp->active.tls_ctx = exim_client_ctx; cctx->tls_ctx = exim_client_ctx; diff --git a/src/src/tls.c b/src/src/tls.c index 531d67950..d47156cdc 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -61,8 +61,6 @@ static int ssl_xfer_eof = FALSE; static BOOL ssl_xfer_error = FALSE; #endif -uschar *tls_channelbinding_b64 = NULL; - /************************************************* * Expand string; give error on failure * -- 2.30.2