*/
#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
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)
])
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
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
imap_auth_t* authenticator = imap_authenticators;
int r = -1;
- while (authenticator)
+ while (*authenticator)
{
if ((r = (*authenticator)(idata)) != IMAP_AUTH_UNAVAIL)
return r;
#include "browser.h"
#include "message.h"
#include "imap_private.h"
+#ifdef USE_SSL
+# include "mutt_ssl.h"
+#endif
#include <unistd.h>
#include <ctype.h>
int imap_open_connection (IMAP_DATA* idata)
{
char buf[LONG_STRING];
+ int rc;
if (mutt_socket_open (idata->conn) < 0)
{
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
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
#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
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
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)
{
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
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;
}
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,
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)
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)
{
int ssl_socket_open (CONNECTION * conn)
{
sslsockdata *data;
- int err;
+ int maxbits;
if (raw_socket_open (conn) < 0)
return -1;
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 */
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;
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);