Hi I noticed the GNU SASL support in Mailutils didn't set TLS channel bindings, so the SCRAM-*-PLUS mechanisms would never complete successfully. The attached fixes that, and also add support for the new TLS version 1.3 channel binding 'tls-exporter' which is implemented in an unreleased version of GNU SASL. It should work with older GnuTLS+GNU SASL versions too.
I'm not sure if this is the right way to bridge data between the TLS layer and the SASL layer, what do you think? At least the following sequence of commands works: certtool --generate-privkey --outfile cakey.pem printf "ca\ncn=GSASL test CA\n" > cacert.cfg certtool --generate-self-signed --load-privkey cakey.pem --template cacert.cfg --outfile cacert.pem certtool --generate-privkey --outfile key.pem printf "cn=GSASL test client\nip_address=127.0.0.1\n" > cert.cfg certtool --generate-certificate --load-ca-privkey cakey.pem --load-ca-certificate cacert.pem --load-privkey key.pem --template cert.cfg --outfile cert.pem printf "jas\tfoo" > foo.txt printf "gsasl {\n cram-passwd "foo.txt";\n};\nserver 127.0.0.1:1430 {\n tls {\n ssl-certificate-file cert.pem;\n ssl-key-file key.pem;\n ssl-ca-file cacert.pem;\n };\n};\n" > mailutils.rc Start the server: ~/src/mailutils/imap4d/imap4d --config-file mailutils.rc -d --foreground --debug-level=4711 Start the client (forcing TLS 1.3 because my system GnuTLS is too old for tls-exporter): ~/src/gsasl/src/gsasl -pfoo -d -m SCRAM-SHA-256-PLUS --verbose --x509-ca-file=cacert.pem --priority NORMAL:-VERS-TLS1.3 --imap 127.0.0.1 1430 The server log: 2022-08-02T18:05:50.936238+02:00 latte imap4d[415942]: error setting mail group: Operationen inte tillåten 2022-08-02T18:05:50.942383+02:00 latte imap4d[415942]: opening server "default" inet://127.0.0.1:1430 2022-08-02T18:05:50.942601+02:00 latte imap4d[415942]: imap4d (GNU Mailutils 3.15) started 2022-08-02T18:05:54.771846+02:00 latte imap4d[415971]: TLS established using AES-256-GCM-AEAD (TLS1.2) 2022-08-02T18:05:54.774101+02:00 latte imap4d[415971]: unsupported callback property 15 2022-08-02T18:05:54.774325+02:00 latte imap4d[415971]: unsupported callback property 16 2022-08-02T18:05:54.790117+02:00 latte imap4d[415971]: unsupported callback property 23 2022-08-02T18:05:54.814039+02:00 latte imap4d[415971]: Getting auth info for user jas 2022-08-02T18:05:54.814485+02:00 latte imap4d[415971]: source=system, name=jas, passwd=x, uid=1000, gid=1000, gecos=Simon Josefsson,,,, dir=/home/jas, shell=/bin/bash, mailbox=/var/mail/jas, quota=0, change_uid=1 2022-08-02T18:05:54.814721+02:00 latte imap4d[415971]: user `jas' logged in (source: system) 2022-08-02T18:05:54.856597+02:00 latte imap4d[415971]: session terminating for user: jas 2022-08-02T18:05:54.859222+02:00 latte imap4d[415942]: process 415971 finished with code 0 (Normal termination) Output from client tool: Trying ‘127.0.0.1’... * OK IMAP4rev1 . CAPABILITY * CAPABILITY IMAP4rev1 NAMESPACE ID IDLE LITERAL+ UNSELECT STARTTLS AUTH=ANONYMOUS AUTH=EXTERNAL AUTH=LOGIN AUTH=PLAIN AUTH=SECURID AUTH=DIGEST-MD5 AUTH=CRAM-MD5 AUTH=SCRAM-SHA-1 AUTH=SCRAM-SHA-1-PLUS AUTH=SCRAM-SHA-256 AUTH=SCRAM-SHA-256-PLUS AUTH=SAML20 AUTH=OPENID20 AUTH=GSSAPI AUTH=GS2-KRB5 . OK CAPABILITY Completed . STARTTLS . OK STARTTLS Begin TLS negotiation TLS X.509-verifikation: The certificate is trusted. TLS-sessionsinfo: (TLS1.2)-(ECDHE-SECP256R1)-(RSA-SHA256)-(AES-256-GCM) TLS X.509-certifikat 0: subject `CN=GSASL test client', issuer `CN=GSASL test CA', serial 0x2a518d9131fa1ced7963dff3086cd9fe747c07e6, RSA key 3072 bits, signerat med RSA-SHA256, activated `2022-08-02 16:04:46 UTC', expires `2023-08-02 16:04:46 UTC', pin-sha256="ngZK+jwMvGSxU77BHFi7aay7gCbShjpetPxrlYXEHxM=" . CAPABILITY * CAPABILITY IMAP4rev1 NAMESPACE ID IDLE LITERAL+ UNSELECT AUTH=ANONYMOUS AUTH=EXTERNAL AUTH=LOGIN AUTH=PLAIN AUTH=SECURID AUTH=DIGEST-MD5 AUTH=CRAM-MD5 AUTH=SCRAM-SHA-1 AUTH=SCRAM-SHA-1-PLUS AUTH=SCRAM-SHA-256 AUTH=SCRAM-SHA-256-PLUS AUTH=SAML20 AUTH=OPENID20 AUTH=GSSAPI AUTH=GS2-KRB5 . OK CAPABILITY Completed . AUTHENTICATE SCRAM-SHA-256-PLUS + Using system username `jas' as authentication identity. cD10bHMtdW5pcXVlLCxuPWphcyxyPU1BM3hDWkltalVMbE1FK2ZVQjZRakdQZQ== + cj1NQTN4Q1pJbWpVTGxNRStmVUI2UWpHUGVSMDhoVDRqc1FxYjdPN2NUQmdGWnNoWXEscz1xbENteDVhVXJtMHUxOWNqLGk9NDA5Ng== Yz1jRDEwYkhNdGRXNXBjWFZsTEN5OUpucGpXa0RybzRZbnpSOD0scj1NQTN4Q1pJbWpVTGxNRStmVUI2UWpHUGVSMDhoVDRqc1FxYjdPN2NUQmdGWnNoWXEscD1reXlXeUtFUnVIMjM1NXNkMzhhS1pSK1MxNnRVOHZMd2FUQWFUNEJJb1E4PQ== + dj1SQmlJR21ock5ON0RSN2tqaUx0NlJtay9KbkRiVjVFUjZFTEFPUkpkbHdVPQ== . OK AUTHENTICATE SCRAM-SHA-256-PLUS authentication successful Klientautentisering avslutad (server betrodd)... Session avslutad... . LOGOUT * BYE Session terminating. . OK LOGOUT Completed /Simon
From cc9aaad5fb570b6538b32429ddb8f9a57dda6862 Mon Sep 17 00:00:00 2001 From: Simon Josefsson <si...@josefsson.org> Date: Tue, 2 Aug 2022 18:10:52 +0200 Subject: [PATCH] Support TLS channel bindings with GNU SASL. --- am/gsasl.m4 | 1 + am/tls.m4 | 2 ++ imap4d/auth_gsasl.c | 21 ++++++++++++++++ imap4d/imap4d.h | 2 ++ imap4d/io.c | 6 +++++ include/mailutils/stream.h | 14 ++++++++++- libmu_auth/tlsfdstr.c | 50 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 95 insertions(+), 1 deletion(-) diff --git a/am/gsasl.m4 b/am/gsasl.m4 index f3eef99a6..fae1c7ce8 100644 --- a/am/gsasl.m4 +++ b/am/gsasl.m4 @@ -40,6 +40,7 @@ AC_DEFUN([MU_CHECK_GSASL], [mu_cv_lib_gsasl=-lgsasl], [mu_cv_lib_gsasl=no]) if test $mu_cv_lib_gsasl != no; then + AC_CHECK_DECLS([GSASL_CB_TLS_EXPORTER], [], [], [[#include <gsasl.h>]]) LIBS="$LIBS $mu_cv_lib_gsasl" AC_TRY_RUN([ #include <gsasl.h> diff --git a/am/tls.m4 b/am/tls.m4 index 17fb6f074..526ba99cd 100644 --- a/am/tls.m4 +++ b/am/tls.m4 @@ -53,7 +53,9 @@ AC_DEFUN([MU_CHECK_GNUTLS], [mu_cv_lib_gnutls=no]) LIBS=$saved_LIBS m4_if([$1],,,[if test $mu_cv_lib_gnutls != no; then + AC_CHECK_DECLS([GNUTLS_CB_TLS_EXPORTER], [], [], [[#include <gnutls/gnutls.h>]]) LIBS="$LIBS $TLS_LIBS" + AC_CHECK_FUNCS([gnutls_session_channel_binding]) AC_TRY_RUN([ #include <gnutls/gnutls.h> diff --git a/imap4d/auth_gsasl.c b/imap4d/auth_gsasl.c index 307affdb7..4c58cce8b 100644 --- a/imap4d/auth_gsasl.c +++ b/imap4d/auth_gsasl.c @@ -324,6 +324,27 @@ callback (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop) case GSASL_VALIDATE_SECURID: #endif + case GSASL_CB_TLS_UNIQUE: +#if HAVE_DECL_GSASL_CB_TLS_EXPORTER + case GSASL_CB_TLS_EXPORTER: +#endif + { + char *b64tlscb; + rc = io_tls_channel_binding + (prop == GSASL_CB_TLS_UNIQUE + ? MU_IOCTL_TLS_GET_CHANNEL_BINDING_TLS_UNIQUE + : MU_IOCTL_TLS_GET_CHANNEL_BINDING_TLS_EXPORTER, &b64tlscb); + if (rc) + { + mu_diag_output (MU_DIAG_ERR, + _("couldn't extract channel binding")); + return GSASL_NO_CALLBACK; + } + gsasl_property_set (sctx, prop, b64tlscb); + free (b64tlscb); + return GSASL_OK; + } + case GSASL_VALIDATE_SIMPLE: rc = cb_validate (ctx, sctx); break; diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h index 5c7927cde..c3662d903 100644 --- a/imap4d/imap4d.h +++ b/imap4d/imap4d.h @@ -251,6 +251,8 @@ void io_setio (int, int, struct mu_tls_config *); void io_flush (void); void io_enable_crlf (int); +int io_tls_channel_binding (int opcode, char **b64cb); + imap4d_tokbuf_t imap4d_tokbuf_init (void); void imap4d_tokbuf_destroy (imap4d_tokbuf_t *tok); int imap4d_tokbuf_argc (imap4d_tokbuf_t tok); diff --git a/imap4d/io.c b/imap4d/io.c index 6309149ad..cbce10a7b 100644 --- a/imap4d/io.c +++ b/imap4d/io.c @@ -20,6 +20,12 @@ mu_stream_t iostream; +int +io_tls_channel_binding (int opcode, char **b64cb) +{ + return mu_stream_ioctl (iostream, MU_IOCTL_TLSSTREAM, opcode, b64cb); +} + static void log_cipher (mu_stream_t stream) { diff --git a/include/mailutils/stream.h b/include/mailutils/stream.h index df6298056..4c6c62395 100644 --- a/include/mailutils/stream.h +++ b/include/mailutils/stream.h @@ -245,7 +245,19 @@ enum mu_buffer_type On success, the following keys are defined: "protocol", "cipher", "mac" */ #define MU_IOCTL_TLS_GET_CIPHER_INFO 0 - + + /* Get tls-unique channel binding. + Arg: char ** + On success, newly allocated string with base64 encoded tls-unique data + */ +#define MU_IOCTL_TLS_GET_CHANNEL_BINDING_TLS_UNIQUE 1 + + /* Get tls-exporter channel binding. + Arg: char ** + On success, newly allocated string with base64 encoded tls-exporter data + */ +#define MU_IOCTL_TLS_GET_CHANNEL_BINDING_TLS_EXPORTER 2 + #define MU_TRANSPORT_INPUT 0 #define MU_TRANSPORT_OUTPUT 1 #define MU_TRANSPORT_VALID_TYPE(n) \ diff --git a/libmu_auth/tlsfdstr.c b/libmu_auth/tlsfdstr.c index 3ad73533b..8b18dfc62 100644 --- a/libmu_auth/tlsfdstr.c +++ b/libmu_auth/tlsfdstr.c @@ -430,6 +430,46 @@ get_cipher_info (gnutls_session_t session, mu_property_t *pprop) return 0; } +#if HAVE_GNUTLS_SESSION_CHANNEL_BINDING +static int +get_channel_binding (struct _mu_stream *stream, + gnutls_channel_binding_t cbtype, + char **b64cbstr) +{ + struct _mu_tlsfd_stream *sp = (struct _mu_tlsfd_stream *) stream; + gnutls_datum_t cb, b64cb; + + if (!b64cbstr) + return EINVAL; + + sp->tls_err = gnutls_session_channel_binding(sp->session, cbtype, &cb); + if (sp->tls_err != GNUTLS_E_SUCCESS) + { + mu_debug (MU_DEBCAT_TLS, MU_DEBUG_ERROR, + ("gnutls_session_channel_binding: %s", + gnutls_strerror (sp->tls_err))); + return MU_ERR_TLS; + } + + sp->tls_err = gnutls_base64_encode2(&cb, &b64cb); + gnutls_free (cb.data); + if (sp->tls_err != GNUTLS_E_SUCCESS) + { + mu_debug (MU_DEBCAT_TLS, MU_DEBUG_ERROR, + ("gnutls_base64_encode2: %s", + gnutls_strerror (sp->tls_err))); + return MU_ERR_TLS; + } + + *b64cbstr = strdup ((char*) b64cb.data); + gnutls_free (b64cb.data); + if (!*b64cbstr) + return ENOMEM; + + return 0; +} +#endif + static int _tlsfd_ioctl (struct _mu_stream *stream, int code, int opcode, void *ptr) { @@ -555,6 +595,16 @@ _tlsfd_ioctl (struct _mu_stream *stream, int code, int opcode, void *ptr) case MU_IOCTL_TLS_GET_CIPHER_INFO: return get_cipher_info (sp->session, ptr); +#if HAVE_GNUTLS_SESSION_CHANNEL_BINDING + case MU_IOCTL_TLS_GET_CHANNEL_BINDING_TLS_UNIQUE: + return get_channel_binding (stream, GNUTLS_CB_TLS_UNIQUE, ptr); + +#if HAVE_DECL_GNUTLS_CB_TLS_EXPORTER + case MU_IOCTL_TLS_GET_CHANNEL_BINDING_TLS_EXPORTER: + return get_channel_binding (stream, GNUTLS_CB_TLS_EXPORTER, ptr); +#endif +#endif + default: return EINVAL; } -- 2.30.2
signature.asc
Description: PGP signature