diff options
author | Thomas Roessler <roessler@does-not-exist.org> | 2001-02-14 20:19:36 +0000 |
---|---|---|
committer | Thomas Roessler <roessler@does-not-exist.org> | 2001-02-14 20:19:36 +0000 |
commit | ccd5c1a264eba6e4d7f6f0c6a911f495bf5c9727 (patch) | |
tree | 6139cdc6cf17d8b2ca0f2282f0c089d2d8b33138 | |
parent | 6dedb2b6ece9c17553080a6eda28d4142af9b19a (diff) |
STARTTLS patch from Brendan Cully.
-rw-r--r-- | acconfig.h | 3 | ||||
-rw-r--r-- | configure.in | 8 | ||||
-rw-r--r-- | imap/auth.c | 2 | ||||
-rw-r--r-- | imap/imap.c | 34 | ||||
-rw-r--r-- | imap/message.c | 7 | ||||
-rw-r--r-- | mutt_sasl.c | 19 | ||||
-rw-r--r-- | mutt_socket.h | 7 | ||||
-rw-r--r-- | mutt_ssl.c | 156 | ||||
-rw-r--r-- | mutt_ssl.h | 2 |
9 files changed, 174 insertions, 64 deletions
@@ -36,6 +36,9 @@ */ #undef NFS_ATTRIBUTE_HACK +/* Define to `int*' if <unistd.h> doesn't have it. */ +#undef socklen_t + /* Include code for socket support. Set automatically if you enable pop or * IMAP */ #undef USE_SOCKET diff --git a/configure.in b/configure.in index 82e01d1c..238cb7c3 100644 --- a/configure.in +++ b/configure.in @@ -558,6 +558,10 @@ dnl -- end socket dependencies -- if test "$need_socket" = "yes" then + AC_MSG_CHECKING([for socklen_t]) + AC_EGREP_HEADER(socklen_t, sys/socket.h, AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) + AC_DEFINE(socklen_t, int*)) AC_CHECK_FUNC(gethostent, , AC_CHECK_LIB(nsl, gethostent)) AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt)) AC_CHECK_FUNCS(getaddrinfo) @@ -630,7 +634,7 @@ AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authenticati ]) AM_CONDITIONAL(USE_GSS, test x$need_gss = xyes) -AC_ARG_WITH(ssl, [ --with-ssl[=PFX] Compile in SSL socket support for POP/IMAP], +AC_ARG_WITH(ssl, [ --with-ssl[=PFX] Compile in SSL support for POP/IMAP], [ if test "$with_ssl" != "no" then if test "$need_socket" != "yes"; then @@ -664,7 +668,7 @@ AC_ARG_WITH(ssl, [ --with-ssl[=PFX] Compile in SSL socket support for AM_CONDITIONAL(USE_SSL, test x$need_ssl = xyes) dnl SSL support via NSS -AC_ARG_WITH(nss, [ --with-nss[=PFX] Compile in SSL socket support for POP/IMAP via NSS], +AC_ARG_WITH(nss, [ --with-nss[=PFX] Compile in SSL support for POP/IMAP via NSS], [ if test "$with_nss" != no then if test "$need_socket" != "yes"; then diff --git a/imap/auth.c b/imap/auth.c index fa485bfe..97ac5d12 100644 --- a/imap/auth.c +++ b/imap/auth.c @@ -48,7 +48,7 @@ int imap_authenticate (IMAP_DATA* idata) imap_auth_t* authenticator = imap_authenticators; int r = -1; - while (authenticator) + while (*authenticator) { if ((r = (*authenticator)(idata)) != IMAP_AUTH_UNAVAIL) return r; diff --git a/imap/imap.c b/imap/imap.c index ba96e782..2b479601 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -29,6 +29,9 @@ #include "browser.h" #include "message.h" #include "imap_private.h" +#ifdef USE_SSL +# include "mutt_ssl.h" +#endif #include <unistd.h> #include <ctype.h> @@ -309,6 +312,7 @@ IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags) int imap_open_connection (IMAP_DATA* idata) { char buf[LONG_STRING]; + int rc; if (mutt_socket_open (idata->conn) < 0) { @@ -324,8 +328,36 @@ int imap_open_connection (IMAP_DATA* idata) if (mutt_strncmp ("* OK", idata->cmd.buf, 4) == 0) { - if (imap_check_capabilities (idata) || imap_authenticate (idata)) + /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */ + if (imap_check_capabilities (idata)) + goto bail; +#if defined(USE_SSL) && !defined(USE_NSS) + /* Attempt STARTTLS if available. TODO: make STARTTLS configurable. */ + if (mutt_bit_isset (idata->capabilities, STARTTLS)) + { + if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1) + goto bail; + if (rc != -2) + { + if (mutt_ssl_starttls (idata->conn)) + { + dprint (1, (debugfile, "imap_open_connection: STARTTLS failed\n")); + goto bail; + } + else + { + /* RFC 2595 demands we recheck CAPABILITY after TLS is negotiated. */ + if (imap_exec (idata, "CAPABILITY", 0)) + goto bail; + } + } + } +#endif + if (imap_authenticate (idata)) goto bail; + if (idata->conn->ssf) + dprint (2, (debugfile, "Communication encrypted at %d bits\n", + idata->conn->ssf)); } else if (mutt_strncmp ("* PREAUTH", idata->cmd.buf, 9) == 0) { diff --git a/imap/message.c b/imap/message.c index adaf142b..b3a14e83 100644 --- a/imap/message.c +++ b/imap/message.c @@ -1,6 +1,6 @@ /* * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> - * Copyright (C) 1999-2000 Brendan Cully <brendan@kublai.com> + * Copyright (C) 1999-2001 Brendan Cully <brendan@kublai.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -89,8 +89,9 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) for (msgno = msgbegin; msgno <= msgend ; msgno++) { - mutt_message (_("Fetching message headers... [%d/%d]"), msgno + 1, - msgend + 1); + if (ReadInc && (!msgno || ((msgno+1) % ReadInc == 0))) + mutt_message (_("Fetching message headers... [%d/%d]"), msgno + 1, + msgend + 1); if (msgno + 1 > fetchlast) { diff --git a/mutt_sasl.c b/mutt_sasl.c index 581d1c2e..eb3e2ce4 100644 --- a/mutt_sasl.c +++ b/mutt_sasl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Brendan Cully <brendan@kublai.com> + * Copyright (C) 2000-1 Brendan Cully <brendan@kublai.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,14 +21,11 @@ #include "mutt.h" #include "account.h" #include "mutt_sasl.h" -#ifdef USE_SSL -# include "mutt_ssl.h" -#endif #include "mutt_socket.h" #include <sasl.h> +#include <sys/socket.h> #include <netinet/in.h> -#include <netdb.h> /* arbitrary. SASL will probably use a smaller buffer anyway. OTOH it's * been a while since I've had access to an SASL server which negotiated @@ -129,11 +126,11 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn) socklen_t size; size = sizeof (local); - if (getsockname (conn->fd, &local, &size)) + if (getsockname (conn->fd, (struct sockaddr*) &local, &size)) return -1; size = sizeof(remote); - if (getpeername(conn->fd, &remote, &size)) + if (getpeername(conn->fd, (struct sockaddr*) &remote, &size)) return -1; #ifdef SASL_IP_LOCAL @@ -178,7 +175,7 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn) if (conn->account.flags & M_ACCT_SSL) { memset (&extprops, 0, sizeof (extprops)); - extprops.ssf = mutt_ssl_get_ssf (conn); + extprops.ssf = conn->ssf; dprint (2, (debugfile, "External SSF: %d\n", extprops.ssf)); if (sasl_setprop (*saslconn, SASL_SSF_EXTERNAL, &extprops) != SASL_OK) { @@ -271,9 +268,11 @@ void mutt_sasl_setup_conn (CONNECTION* conn, sasl_conn_t* saslconn) sasldata->saslconn = saslconn; /* get ssf so we know whether we have to (en|de)code read/write */ sasl_getprop (saslconn, SASL_SSF, (void**) &sasldata->ssf); - dprint (2, (debugfile, "SASL protection strength: %u\n", *sasldata->ssf)); + dprint (3, (debugfile, "SASL protection strength: %u\n", *sasldata->ssf)); + /* Add SASL SSF to transport SSF */ + conn->ssf += *sasldata->ssf; sasl_getprop (saslconn, SASL_MAXOUTBUF, (void**) &sasldata->pbufsize); - dprint (2, (debugfile, "SASL protection buffer size: %u\n", *sasldata->pbufsize)); + dprint (3, (debugfile, "SASL protection buffer size: %u\n", *sasldata->pbufsize)); /* clear input buffer */ sasldata->buf = NULL; diff --git a/mutt_socket.h b/mutt_socket.h index fd776d2d..b20767da 100644 --- a/mutt_socket.h +++ b/mutt_socket.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1998 Brandon Long <blong@fiction.net> - * Copyright (C) 1999-2000 Brendan Cully <brendan@kublai.com> + * Copyright (C) 1999-2001 Brendan Cully <brendan@kublai.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,12 +31,15 @@ typedef struct _connection { ACCOUNT account; + /* security strength factor, in bits */ + unsigned int ssf; + void *data; + char inbuf[LONG_STRING]; int bufpos; int fd; int available; - void *data; struct _connection *next; @@ -68,19 +68,70 @@ typedef struct _sslsockdata } sslsockdata; -/* mutt_ssl_get_ssf: Return bit strength of connection encryption. SASL - * uses this to determine how much additional protection to provide, - * if necessary. */ -int mutt_ssl_get_ssf (CONNECTION* conn) +/* 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); +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); +int ssl_negotiate (sslsockdata*); + +/* mutt_ssl_starttls: Negotiate TLS over an already opened connection. + * TODO: Merge this code better with ssl_socket_open. */ +int mutt_ssl_starttls (CONNECTION* conn) { - sslsockdata* ssldata = (sslsockdata*) conn->sockdata; + sslsockdata* ssldata; int maxbits; - return SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl), &maxbits); -} + if (ssl_init()) + goto bail; + ssldata = (sslsockdata*) safe_calloc (1, sizeof (sslsockdata)); + /* the ssl_use_xxx protocol options don't apply. We must use TLS in TLS. */ + if (! (ssldata->ctx = SSL_CTX_new (TLSv1_client_method ()))) + { + dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL_CTX\n")); + goto bail_ssldata; + } + + if (! (ssldata->ssl = SSL_new (ssldata->ctx))) + { + dprint (1, (debugfile, "mutt_ssl_starttls: Error allocating SSL\n")); + goto bail_ctx; + } + + if (SSL_set_fd (ssldata->ssl, conn->fd) != 1) + { + dprint (1, (debugfile, "mutt_ssl_starttls: Error setting fd\n")); + goto bail_ssl; + } + + if (ssl_negotiate (ssldata)) + goto bail_ssl; + + /* hmm. watch out if we're starting TLS over any method other than raw. */ + conn->sockdata = ssldata; + conn->read = ssl_socket_read; + conn->write = ssl_socket_write; + conn->close = ssl_socket_close; + + conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (ssldata->ssl), + &maxbits); + + return 0; + + bail_ssl: + FREE (&ssldata->ssl); + bail_ctx: + FREE (&ssldata->ctx); + bail_ssldata: + FREE (&ssldata); + bail: + return -1; +} -static int add_entropy (const char *file); /* * OpenSSL library needs to be fed with sufficient entropy. On systems * with /dev/urandom, this is done transparently by the library itself, @@ -94,29 +145,42 @@ static int add_entropy (const char *file); int ssl_init (void) { char path[_POSIX_PATH_MAX]; + static unsigned char init_complete = 0; - if (HAVE_ENTROPY()) return 0; - - /* load entropy from files */ - add_entropy (SslEntropyFile); - add_entropy (RAND_file_name (path, sizeof (path))); + if (init_complete) + return 0; + + if (! HAVE_ENTROPY()) + { + /* load entropy from files */ + add_entropy (SslEntropyFile); + add_entropy (RAND_file_name (path, sizeof (path))); - /* load entropy from egd sockets */ + /* load entropy from egd sockets */ #ifdef HAVE_RAND_EGD - add_entropy (getenv ("EGDSOCKET")); - snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir)); - add_entropy (path); - add_entropy ("/tmp/entropy"); + add_entropy (getenv ("EGDSOCKET")); + snprintf (path, sizeof(path), "%s/.entropy", NONULL(Homedir)); + add_entropy (path); + add_entropy ("/tmp/entropy"); #endif - /* shuffle $RANDFILE (or ~/.rnd if unset) */ - RAND_write_file (RAND_file_name (path, sizeof (path))); - mutt_clear_error (); - if (HAVE_ENTROPY()) return 0; + /* shuffle $RANDFILE (or ~/.rnd if unset) */ + RAND_write_file (RAND_file_name (path, sizeof (path))); + mutt_clear_error (); + if (! HAVE_ENTROPY()) + { + mutt_error (_("Failed to find enough entropy on your system")); + sleep (2); + return -1; + } + } - mutt_error (_("Failed to find enough entropy on your system")); - sleep (2); - return -1; + /* I don't think you can do this just before reading the error. The call + * itself might clobber the last SSL error. */ + SSL_load_error_strings(); + SSL_library_init(); + init_complete = 1; + return 0; } static int add_entropy (const char *file) @@ -161,12 +225,6 @@ static int ssl_socket_open_err (CONNECTION *conn) return -1; } -static int ssl_check_certificate (sslsockdata * data); - -static int ssl_socket_read (CONNECTION * conn); -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); int ssl_socket_setup (CONNECTION * conn) { @@ -199,7 +257,7 @@ int ssl_socket_write (CONNECTION* conn, const char* buf, size_t len) int ssl_socket_open (CONNECTION * conn) { sslsockdata *data; - int err; + int maxbits; if (raw_socket_open (conn) < 0) return -1; @@ -207,7 +265,6 @@ int ssl_socket_open (CONNECTION * conn) data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata)); conn->sockdata = data; - SSL_library_init(); data->ctx = SSL_CTX_new (SSLv23_client_method ()); /* disable SSL protocols as needed */ @@ -227,35 +284,46 @@ int ssl_socket_open (CONNECTION * conn) data->ssl = SSL_new (data->ctx); SSL_set_fd (data->ssl, conn->fd); - if ((err = SSL_connect (data->ssl)) != 1) + if (ssl_negotiate(data)) + { + ssl_socket_close (conn); + return -1; + } + + conn->ssf = SSL_CIPHER_get_bits (SSL_get_current_cipher (data->ssl), + &maxbits); + + return 0; +} + +/* ssl_negotiate: After SSL state has been initialised, attempt to negotiate + * SSL over the wire, including certificate checks. */ +int ssl_negotiate (sslsockdata* ssldata) +{ + if (SSL_connect (ssldata->ssl) != 1) { unsigned long e; - SSL_load_error_strings(); while ((e = ERR_get_error()) != 0) { - mutt_error ("%s", ERR_reason_error_string(e)); + mutt_error ("SSL failed: %s", ERR_reason_error_string(e)); sleep (1); } - ssl_socket_close (conn); return -1; } - data->cert = SSL_get_peer_certificate (data->ssl); - if (!data->cert) + ssldata->cert = SSL_get_peer_certificate (ssldata->ssl); + if (!ssldata->cert) { mutt_error (_("Unable to get certificate from peer")); sleep (1); return -1; } - if (!ssl_check_certificate (data)) - { - ssl_socket_close (conn); + if (!ssl_check_certificate (ssldata)) return -1; - } mutt_message (_("SSL connection using %s (%s)"), - SSL_get_cipher_version (data->ssl), SSL_get_cipher_name (data->ssl)); + SSL_get_cipher_version (ssldata->ssl), SSL_get_cipher_name (ssldata->ssl)); sleep (1); return 0; @@ -24,7 +24,7 @@ extern char *SslCertFile; extern char *SslEntropyFile; -int mutt_ssl_get_ssf (CONNECTION* conn); +int mutt_ssl_starttls (CONNECTION* conn); extern int ssl_socket_setup (CONNECTION *conn); |