bug-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Support for SCRAM-*-PLUS with channel bindings


From: Simon Josefsson
Subject: Support for SCRAM-*-PLUS with channel bindings
Date: Tue, 02 Aug 2022 18:13:31 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux)

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 <simon@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

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]