From: Brendan Cully Date: Mon, 30 Aug 2004 20:05:40 +0000 (+0000) Subject: Here's a patch to allow mutt to use SSL client certificates to X-Git-Tag: mutt-1-5-15-rel~77 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=61818cd30a7d56f5a815eb80663b897d3c462962;p=mutt 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. --- diff --git a/globals.h b/globals.h index 15ad2491..327328c2 100644 --- 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 cf40ff3b..8aa118e2 100644 --- 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 }, diff --git a/mutt_sasl.c b/mutt_sasl.c index 7babfe5b..aa41f2c8 100644 --- a/mutt_sasl.c +++ b/mutt_sasl.c @@ -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; diff --git a/mutt_ssl.c b/mutt_ssl.c index 1f847a4c..171b7e67 100644 --- a/mutt_ssl.c +++ b/mutt_ssl.c @@ -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); +}