]> granicus.if.org Git - mutt/commitdiff
STARTTLS patch from Brendan Cully.
authorThomas Roessler <roessler@does-not-exist.org>
Wed, 14 Feb 2001 20:19:36 +0000 (20:19 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Wed, 14 Feb 2001 20:19:36 +0000 (20:19 +0000)
acconfig.h
configure.in
imap/auth.c
imap/imap.c
imap/message.c
mutt_sasl.c
mutt_socket.h
mutt_ssl.c
mutt_ssl.h

index 3dad2c55f13abec349f10b500a7a96cb2cb2517f..42b6c934687622c7b0014b23de247a79cb104e20 100644 (file)
@@ -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
index 82e01d1cdcbd9297e7f10fb84c168585e01c3dd7..238cb7c3506c9ea97056be8cb3ca06bf46154186 100644 (file)
@@ -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
index fa485bfe955793fe703de48592c3f8020d783c40..97ac5d1200ea4632d9fead1dc45039d0dc6790f2 100644 (file)
@@ -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;
index ba96e782ec673a2ef07b8b781b0bd4e86ec873c5..2b4796011e47242b8c66a68f34b651456d9ef908 100644 (file)
@@ -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)
   {
index adaf142b0b55fb038469dcaeb6c4c217c3c95c8b..b3a14e83585a7bdc3262b6017a4c9a3fde559dbb 100644 (file)
@@ -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)
     {
index 581d1c2e420f25273342d8b20026d64d73550f74..eb3e2ce4fd5b5ba232037c9ce9ee09d64455e5b9 100644 (file)
@@ -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
 #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;
index fd776d2d93afe4d8c83e660f672a76f8ea997b52..b20767da8479b2bd7ada5afc1e63ca5e3f722874 100644 (file)
@@ -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
 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;
 
index efd1b64387a216c4ce72ef77dabd5d6c717c0ee0..ac52e1f1b7efeaf1ac20d27973f5ba373994fac0 100644 (file)
@@ -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;
index 73ece1a1db0f9108b43cbd7492a01c8a470843b8..6791703561fddcbbcbd148eb3ef6e216d2b8dd13 100644 (file)
@@ -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);