]> granicus.if.org Git - mutt/commitdiff
Here's a patch to allow mutt to use SSL client certificates to
authorBrendan Cully <brendan@kublai.com>
Mon, 30 Aug 2004 20:05:40 +0000 (20:05 +0000)
committerBrendan Cully <brendan@kublai.com>
Mon, 30 Aug 2004 20:05:40 +0000 (20:05 +0000)
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.

globals.h
init.h
mutt_sasl.c
mutt_ssl.c

index 15ad24917cc7aa5f8e96c6347bdc5a61ff6e43ee..327328c2ceecd1304a2e0746038ecf6f58f582e8 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -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;
diff --git a/init.h b/init.h
index cf40ff3b80954502981b933b97abe1e929a6ca8b..8aa118e2efec4eb0d96d8821759673470c8c4886 100644 (file)
--- a/init.h
+++ b/init.h
@@ -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 7babfe5bdae56e8a061954e8d9bc873bc7cea99e..aa41f2c86a735b99ca5a0dcf04654ba58c4fb540 100644 (file)
@@ -294,8 +294,8 @@ dprint(1,(debugfile, "local ip: %s, remote ip:%s\n", iplocalport, ipremoteport))
    * 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, remote ip:%s\n", iplocalport, ipremoteport))
       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 1f847a4cff168add261449f5d7f11907a6119624..171b7e6734fab2bbeef30c3544ee980b65db748e 100644 (file)
@@ -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 * conn)
     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 * conn)
 
 /* 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;
@@ -315,7 +321,7 @@ int ssl_negotiate (sslsockdata* ssldata)
       errmsg = _("I/O error");
       break;
     case SSL_ERROR_SSL:
-      errmsg = _("unspecified protocol error");
+      errmsg = ERR_error_string (ERR_get_error (), NULL);
       break;
     default:
       errmsg = _("unknown error");
@@ -667,3 +673,31 @@ static int ssl_check_certificate (sslsockdata * data)
   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);
+}