<<< Date Index >>>     <<< Thread Index >>>

[PATCH] SSL client certificate support



Here's a patch to allow mutt to use SSL client certificates to
authenticate itself. To use, set ssl_client_cert to the path to your
certificate file (containing both the certificate and the private
key). It works with the SASL EXTERNAL authentication mechanism, so
you'll need to have SASL enabled as well.

Someone on IRC asked about it, so I thought I'd whip it up. I tested
it against Cyrus 2.1.
Index: globals.h
===================================================================
RCS file: /home/roessler/cvs/mutt/globals.h,v
retrieving revision 3.10
diff -u -p -r3.10 globals.h
--- globals.h   19 Jul 2004 21:44:23 -0000      3.10
+++ globals.h   25 Jul 2004 04:33:07 -0000
@@ -108,6 +108,7 @@ WHERE char *SpamSep;
 #if defined(USE_SSL) || defined(USE_NSS)
 WHERE char *SslCertFile INITVAL (NULL);
 WHERE char *SslEntropyFile INITVAL (NULL);
+WHERE char *SslClientCert INITVAL (NULL);
 #endif
 WHERE char *StChars;
 WHERE char *Status;
Index: init.h
===================================================================
RCS file: /home/roessler/cvs/mutt/init.h,v
retrieving revision 3.55
diff -u -p -r3.55 init.h
--- init.h      19 Jul 2004 21:44:23 -0000      3.55
+++ init.h      25 Jul 2004 04:33:10 -0000
@@ -1849,6 +1849,12 @@ struct option_t MuttVars[] = {
   ** This variables specifies whether to attempt to use TLSv1 in the
   ** SSL authentication process.
   */
+  { "ssl_client_cert", DT_PATH, R_NONE, UL &SslClientCert, 0 },
+  /*
+  ** .pp
+  ** The file containing a client certificate and its associated private
+  ** key.
+  */
 #endif
 
   { "pipe_split",      DT_BOOL, R_NONE, OPTPIPESPLIT, 0 },
Index: mutt_sasl.c
===================================================================
RCS file: /home/roessler/cvs/mutt/mutt_sasl.c,v
retrieving revision 3.7
diff -u -p -r3.7 mutt_sasl.c
--- mutt_sasl.c 12 Apr 2004 19:20:13 -0000      3.7
+++ mutt_sasl.c 25 Jul 2004 04:33:10 -0000
@@ -294,8 +294,8 @@ dprint(1,(debugfile, "local ip: %s, remo
    * If someone does it'd probably be trivial to write mutt_nss_get_ssf().
    * I have a feeling more SSL code could be shared between those two files,
    * but I haven't looked into it yet, since I still don't know the APIs. */
-#if defined(USE_SSL) && !defined(USE_NSS)
-  if (conn->account.flags & M_ACCT_SSL)
+#if defined(USE_SSL)
+  if (conn->ssf)
   {
 #ifdef USE_SASL2 /* I'm not sure this actually has an effect, at least with 
SASLv2 */
     dprint (2, (debugfile, "External SSF: %d\n", conn->ssf));
@@ -311,8 +311,8 @@ dprint(1,(debugfile, "local ip: %s, remo
       return -1;
     }
 #ifdef USE_SASL2
-    dprint (2, (debugfile, "External authentication name: %s\n","NULL"));
-    if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, NULL) != SASL_OK)
+    dprint (2, (debugfile, "External authentication name: %s\n", 
conn->account.user));
+    if (sasl_setprop (*saslconn, SASL_AUTH_EXTERNAL, conn->account.user) != 
SASL_OK)
      {
       dprint (1, (debugfile, "mutt_sasl_client_new: Error setting external 
properties\n"));
       return -1;
Index: mutt_ssl.c
===================================================================
RCS file: /home/roessler/cvs/mutt/mutt_ssl.c,v
retrieving revision 3.3
diff -u -p -r3.3 mutt_ssl.c
--- mutt_ssl.c  3 Sep 2003 17:10:37 -0000       3.3
+++ mutt_ssl.c  25 Jul 2004 04:33:11 -0000
@@ -68,13 +68,15 @@ sslsockdata;
 /* local prototypes */
 int ssl_init (void);
 static int add_entropy (const char *file);
-static int ssl_check_certificate (sslsockdata * data);
 static int ssl_socket_read (CONNECTION* conn, char* buf, size_t len);
 static int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len);
 static int ssl_socket_open (CONNECTION * conn);
 static int ssl_socket_close (CONNECTION * conn);
 static int tls_close (CONNECTION* conn);
-int ssl_negotiate (sslsockdata*);
+static int ssl_check_certificate (sslsockdata * data);
+static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn);
+static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+static int ssl_negotiate (sslsockdata*);
 
 /* mutt_ssl_starttls: Negotiate TLS over an already opened connection.
  *   TODO: Merge this code better with ssl_socket_open. */
@@ -94,6 +96,8 @@ int mutt_ssl_starttls (CONNECTION* conn)
     goto bail_ssldata;
   }
 
+  ssl_get_client_cert(ssldata, conn);
+
   if (! (ssldata->ssl = SSL_new (ssldata->ctx)))
   {
     dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL\n"));
@@ -279,6 +283,8 @@ static int ssl_socket_open (CONNECTION *
     SSL_CTX_set_options(data->ctx, SSL_OP_NO_SSLv3);
   }
 
+  ssl_get_client_cert(data, conn);
+
   data->ssl = SSL_new (data->ctx);
   SSL_set_fd (data->ssl, conn->fd);
 
@@ -296,7 +302,7 @@ static int ssl_socket_open (CONNECTION *
 
 /* ssl_negotiate: After SSL state has been initialised, attempt to negotiate
  *   SSL over the wire, including certificate checks. */
-int ssl_negotiate (sslsockdata* ssldata)
+static int ssl_negotiate (sslsockdata* ssldata)
 {
   int err;
   const char* errmsg;
@@ -666,4 +672,32 @@ static int ssl_check_certificate (sslsoc
   unset_option(OPTUNBUFFEREDINPUT);
   mutt_menuDestroy (&menu);
   return (done == 2);
+}
+
+static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn)
+{
+  if (SslClientCert)
+  {
+    dprint (2, (debugfile, "Using client certificate %s\n", SslClientCert));
+    SSL_CTX_set_default_passwd_cb_userdata(ssldata->ctx, &conn->account);
+    SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb);
+    SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, 
SSL_FILETYPE_PEM);
+    SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
+  }
+}
+
+static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+  ACCOUNT *account = (ACCOUNT*)userdata;
+
+  if (mutt_account_getuser (account))
+    return 0;
+
+  dprint (2, (debugfile, "ssl_passwd_cb: getting password for %s@%s:%u\n",
+             account->user, account->host, account->port));
+  
+  if (mutt_account_getpass (account))
+    return 0;
+
+  return snprintf(buf, size, "%s", account->pass);
 }

Attachment: pgpKnPINFMUTN.pgp
Description: PGP signature