Commit ccd5c1a2 authored by Thomas Roessler's avatar Thomas Roessler

STARTTLS patch from Brendan Cully.

parent 6dedb2b6
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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)
{
......
/*
* 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)
{
......
/*
* 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;
......
/*
* 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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment