]> granicus.if.org Git - neomutt/commitdiff
Brendan Cully's SASL patch. I hope I didn't miss any files.
authorThomas Roessler <roessler@does-not-exist.org>
Mon, 31 Jul 2000 07:18:28 +0000 (07:18 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Mon, 31 Jul 2000 07:18:28 +0000 (07:18 +0000)
24 files changed:
Makefile.am
acconfig.h
account.c
account.h
configure.in
globals.h
imap/Makefile.am
imap/auth.c
imap/auth.h [new file with mode: 0644]
imap/auth_anon.c [new file with mode: 0644]
imap/auth_cram.c [new file with mode: 0644]
imap/auth_gss.c
imap/auth_login.c [new file with mode: 0644]
imap/auth_sasl.c [new file with mode: 0644]
imap/command.c
imap/imap.c
imap/imap_private.h
init.h
m4/gettext.m4
main.c
mutt_sasl.c [new file with mode: 0644]
mutt_sasl.h [new file with mode: 0644]
mutt_socket.c
mutt_socket.h

index fb23509d54bd349bbe950a6afa6e23f7d6df3002..e906d61a814f1cc763b845312e90ebbbb33a3d05 100644 (file)
@@ -63,15 +63,15 @@ non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c \
        contrib/pgp2.rc contrib/pgp5.rc contrib/gpg.rc \
        imap/imap_ssl.c imap/imap_ssl.h README.SSL
 
-EXTRA_mutt_SOURCES = account.c mutt_socket.c pop.c pgp.c pgpinvoke.c pgpkey.c \
-       pgplib.c sha1dgst.c gnupgparse.c resize.c dotlock.c remailer.c \
-       browser.h mbyte.h remailer.h
+EXTRA_mutt_SOURCES = account.c mutt_sasl.c mutt_socket.c pop.c pgp.c \
+       pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c gnupgparse.c resize.c \
+       dotlock.c remailer.c browser.h mbyte.h remailer.h
 
 EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP TODO configure acconfig.h account.h \
        attach.h buffy.h charset.h copy.h dotlock.h functions.h gen_defs \
        globals.h hash.h history.h init.h keymap.h \
        mailbox.h mapping.h mime.h mutt.h mutt_curses.h mutt_menu.h  \
-       mutt_regex.h mutt_socket.h mx.h pager.h pgp.h protos.h  \
+       mutt_regex.h mutt_sasl.h mutt_socket.h mx.h pager.h pgp.h protos.h  \
        reldate.h rfc1524.h rfc2047.h rfc2231.h rfc822.h sha.h sha_locl.h \
        sort.h mime.types VERSION prepare _regex.h OPS.MIX      \
        README.SECURITY remailer.c remailer.h browser.h \
index c31c44c3ec04dd535f96f843c45559ae1c843fc0..21747e6b649d5dd5b8a6a52df230ac99a903da7a 100644 (file)
 /* Do you want support for the IMAP protocol? (--enable-imap) */
 #undef USE_IMAP
 
+/* Do you want to use the Cyrus SASL library for POP/IMAP authentication?
+ * (--with-sasl) */
+#undef USE_SASL
+
 /* Do you want support for IMAP GSSAPI authentication? (--with-gss) */
 #undef USE_GSS
 
 /* Do you have the Heimdal version of Kerberos V? (for gss support) */
 #undef HAVE_HEIMDAL
 
-/* Do you want support for SSL? (--enable-ssl) */
+/* Do you want support for SSL? (--with-ssl) */
 #undef USE_SSL
 
 /* Avoid SSL routines which used patent-encumbered RC5 algorithms */
index 52bb58a13da7473c8bc788f5a124deccbb8f557c..f095763e7bcd255fcad8a5e0067a6db96f6a19cd 100644 (file)
--- a/account.c
+++ b/account.c
@@ -52,3 +52,57 @@ int mutt_account_match (const ACCOUNT* a1, const ACCOUNT* a2)
 
   return 1;
 }
+
+/* mutt_account_getuser: retrieve username into ACCOUNT, if neccessary */
+int mutt_account_getuser (ACCOUNT* account)
+{
+  /* already set */
+  if (account->flags & M_ACCT_USER)
+    return 0;
+#ifdef USE_IMAP
+  else if ((account->type == M_ACCT_TYPE_IMAP) && ImapUser)
+    strfcpy (account->user, ImapUser, sizeof (account->user));
+#endif
+#ifdef USE_POP
+  else if ((account->type == M_ACCT_TYPE_POP) && PopUser)
+    strfcpy (account->user, PopUser, sizeof (account->user));
+#endif
+  /* prompt (defaults to unix username), copy into account->user */
+  else
+  {
+    strfcpy (account->user, NONULL (Username), sizeof (account->user));
+    if (mutt_get_field (_("Username: "), account->user,
+        sizeof (account->user), 0))
+      return -1;
+  }
+
+  account->flags |= M_ACCT_USER;
+
+  return 0;
+}
+
+/* mutt_account_getpass: fetch password into ACCOUNT, if neccessary */
+int mutt_account_getpass (ACCOUNT* account)
+{
+  if (account->flags & M_ACCT_PASS)
+    return 0;
+#ifdef USE_IMAP
+  else if ((account->type == M_ACCT_TYPE_IMAP) && ImapPass)
+    strfcpy (account->pass, ImapPass, sizeof (account->pass));
+#endif
+#ifdef USE_POP
+  else if ((account->type == M_ACCT_TYPE_POP) && PopPass)
+    strfcpy (account->pass, PopPass, sizeof (account->pass));
+#endif
+  else
+  {
+    account->pass[0] = '\0';
+    if (mutt_get_field (_("Password: "), account->pass,
+        sizeof (account->pass), M_PASS))
+      return -1;
+  }
+
+  account->flags |= M_ACCT_PASS;
+
+  return 0;
+}
index 20c5e63a14bc369c521182f87fbace384314f779..9e21c2693f006b4afe758b9d27070ffa7065b0b5 100644 (file)
--- a/account.h
+++ b/account.h
@@ -32,9 +32,8 @@ enum
 /* account flags */
 #define M_ACCT_PORT (1<<0)
 #define M_ACCT_USER (1<<1)
-#define M_ACCT_SSL  (1<<2)
-#define M_ACCT_CRAM (1<<3)
-#define M_ACCT_PASS (1<<4)
+#define M_ACCT_PASS (1<<2)
+#define M_ACCT_SSL  (1<<3)
 
 typedef struct
 {
@@ -46,7 +45,8 @@ typedef struct
   unsigned char flags;
 } ACCOUNT;
 
-/* imap_account_match: compare account info (host/port/user) */
 int mutt_account_match (const ACCOUNT* a1, const ACCOUNT* m2);
+int mutt_account_getuser (ACCOUNT* account);
+int mutt_account_getpass (ACCOUNT* account);
 
 #endif /* _MUTT_ACCOUNT_H_ */
index 0894fbc6219edee25dff2ef5e63454494db73fff..a82976f6eed12226c08d348b7fac4135d38b5eae 100644 (file)
@@ -663,6 +663,37 @@ AM_CONDITIONAL(USE_SSL, test x$need_ssl = xyes)
 
 dnl -- end imap dependencies --
 
+AC_ARG_WITH(sasl, [  --with-sasl[=DIR]          Use Cyrus SASL library for POP/IMAP authentication],
+       [
+       if test "$need_socket" != "yes"
+       then
+         AC_MSG_ERROR([SASL support is only useful with POP or IMAP support])
+       fi
+
+       if test "$with_sasl" != "no"
+       then
+         if test "$with_sasl" != "yes"
+         then
+           CPPFLAGS="$CPPFLAGS -I$with_sasl/include"
+           LDFLAGS="$LDFLAGS -L$with_sasl/lib"
+         fi
+
+         saved_LIBS="$LIBS"
+
+         AC_CHECK_LIB(sasl, sasl_client_init,,
+           AC_MSG_ERROR([could not find libsasl]),)
+
+         MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt_sasl.o"
+         MUTTLIBS="$MUTTLIBS -lsasl"
+         LIBS="$saved_LIBS"
+         AC_DEFINE(USE_SASL)
+         need_sasl=yes
+       fi
+       ])
+AM_CONDITIONAL(USE_SASL, test x$need_sasl = xyes)
+
+dnl -- end socket --
+
 AC_ARG_ENABLE(debug, [  --enable-debug             Enable debugging support],
        [ if test x$enableval = xyes ; then
                AC_DEFINE(DEBUG)
@@ -702,7 +733,7 @@ AC_ARG_ENABLE(buffy-size, [  --enable-buffy-size        Use file size attribute
                 AC_DEFINE(BUFFY_SIZE)
         fi])
 
-AC_ARG_ENABLE(mailtool, [  --enable-mailtool            Enable Sun mailtool attachments support ],
+AC_ARG_ENABLE(mailtool, [  --enable-mailtool          Enable Sun mailtool attachments support ],
        [if test x$enableval = xyes; then
                 AC_DEFINE(SUN_ATTACHMENT)
         fi])
@@ -717,7 +748,7 @@ AC_ARG_WITH(exec-shell, [  --with-exec-shell=SHELL    Specify alternate shell (O
                AC_DEFINE_UNQUOTED(EXECSHELL, "$withval")
        fi])
 
-AC_ARG_ENABLE(exact-address, [  --enable-exact-address     enable regeneration of email addresses],
+AC_ARG_ENABLE(exact-address, [  --enable-exact-address     Enable regeneration of email addresses],
        [if test $enableval = yes; then
                AC_DEFINE(EXACT_ADDRESS)
        fi])
index 2e403189273eeb97d23122bf3907606f23bbe957..3fab5fe09f1e3f834918bc9a9dd63baf88152fcd 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -50,7 +50,6 @@ WHERE char *Homedir;
 WHERE char *Hostname;
 #ifdef USE_IMAP
 WHERE char *ImapUser INITVAL (NULL);
-WHERE char *ImapCRAMKey INITVAL (NULL);
 WHERE char *ImapPass INITVAL (NULL);
 WHERE short ImapCheckTimeout;
 WHERE char *ImapHomeNamespace INITVAL (NULL);
index 5ff4d388bbb5b55a7703da12bb28b806bd5b5ef3..8f5c648649cdd23c54abadc00fb2619c2ecfb25f 100644 (file)
@@ -6,6 +6,12 @@ if USE_GSS
 GSSSOURCES = auth_gss.c
 endif
 
+if USE_SASL
+SASLSOURCES = auth_sasl.c
+else
+CRAMSOURCES = auth_cram.c
+endif
+
 if USE_SSL
 SSLSOURCES = imap_ssl.c
 SSLHEADERS = imap_ssl.h
@@ -16,7 +22,8 @@ EXTRA_DIST = BUGS README TODO imap_ssl.c imap_ssl.h auth_gss.c
 INCLUDES = -I$(top_srcdir) -I../intl
 
 noinst_LIBRARIES = libimap.a
-noinst_HEADERS = imap_private.h md5.h message.h $(SSLHEADERS)
+noinst_HEADERS = auth.h imap_private.h md5.h message.h $(SSLHEADERS)
 
-libimap_a_SOURCES = auth.c browse.c command.c imap.c imap.h md5c.c message.c \
-       utf7.c util.c $(GSSSOURCES) $(SSLSOURCES)
+libimap_a_SOURCES = auth.c auth_anon.c auth_login.c c browse.c \
+       command.c imap.c imap.h md5c.c message.c utf7.c \
+       util.c $(GSSSOURCES) $(SASLSOURCES) $(CRAMSOURCES) $(SSLSOURCES)
index 0c85776140419e433c49fa62ca7388f87d170ad6..26f2feff22bc64dd3dbd012354b241b3e4ff5536 100644 (file)
 
 #include "mutt.h"
 #include "imap_private.h"
-#include "md5.h"
+#include "auth.h"
 
-#define MD5_BLOCK_LEN 64
-#define MD5_DIGEST_LEN 16
-
-/* external authenticator prototypes */
+static imap_auth_t imap_authenticators[] = {
+  imap_auth_anon,
+#ifdef USE_SASL
+  imap_auth_sasl,
+#endif
 #ifdef USE_GSS
-int imap_auth_gss (IMAP_DATA* idata, const char* user);
+  imap_auth_gss,
 #endif
+  /* SASL includes CRAM-MD5 (and GSSAPI, but that's not enabled by default) */
+#ifndef USE_SASL
+  imap_auth_cram_md5,
+#endif
+  imap_auth_login,
 
-/* forward declarations */
-static void hmac_md5 (const char* password, char* challenge,
-  unsigned char* response);
-static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
-  const char* pass);
-static int imap_auth_anon (IMAP_DATA *idata);
-
-/* hmac_md5: produce CRAM-MD5 challenge response. */
-static void hmac_md5 (const char* password, char* challenge,
-  unsigned char* response)
-{
-  MD5_CTX ctx;
-  unsigned char ipad[MD5_BLOCK_LEN], opad[MD5_BLOCK_LEN];
-  unsigned char secret[MD5_BLOCK_LEN+1];
-  unsigned char hash_passwd[MD5_DIGEST_LEN];
-  unsigned int secret_len, chal_len;
-  int i;
-
-  secret_len = strlen (password);
-  chal_len = strlen (challenge);
-
-  /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5
-   * digests */
-  if (secret_len > MD5_BLOCK_LEN)
-  {
-    MD5Init (&ctx);
-    MD5Update (&ctx, (unsigned char*) password, secret_len);
-    MD5Final (hash_passwd, &ctx);
-    strfcpy ((char*) secret, (char*) hash_passwd, MD5_DIGEST_LEN);
-    secret_len = MD5_DIGEST_LEN;
-  }
-  else
-    strfcpy ((char *) secret, password, sizeof (secret));
-
-  memset (ipad, 0, sizeof (ipad));
-  memset (opad, 0, sizeof (opad));
-  memcpy (ipad, secret, secret_len);
-  memcpy (opad, secret, secret_len);
-
-  for (i = 0; i < MD5_BLOCK_LEN; i++)
-  {
-    ipad[i] ^= 0x36;
-    opad[i] ^= 0x5c;
-  }
-
-  /* inner hash: challenge and ipadded secret */
-  MD5Init (&ctx);
-  MD5Update (&ctx, ipad, MD5_BLOCK_LEN);
-  MD5Update (&ctx, (unsigned char*) challenge, chal_len);
-  MD5Final (response, &ctx);
-
-  /* outer hash: inner hash and opadded secret */
-  MD5Init (&ctx);
-  MD5Update (&ctx, opad, MD5_BLOCK_LEN);
-  MD5Update (&ctx, response, MD5_DIGEST_LEN);
-  MD5Final (response, &ctx);
-}
-
-/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. Used unconditionally if the
- *   server supports it */
-static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
-  const char* pass)
-{
-  char ibuf[LONG_STRING], obuf[LONG_STRING];
-  unsigned char hmac_response[MD5_DIGEST_LEN];
-  int len;
-
-  dprint (2, (debugfile, "Attempting CRAM-MD5 login...\n"));
-  mutt_message _("Authenticating (CRAM-MD5)...");
-
-  imap_cmd_start (idata, "AUTHENTICATE CRAM-MD5");
-
-  /* From RFC 2195:
-   * The data encoded in the first ready response contains a presumptively
-   * arbitrary string of random digits, a timestamp, and the fully-qualified
-   * primary host name of the server. The syntax of the unencoded form must
-   * correspond to that of an RFC 822 'msg-id' [RFC822] as described in [POP3].
-   */
-  if (mutt_socket_readln (ibuf, sizeof (ibuf), idata->conn) < 0)
-  {
-    dprint (1, (debugfile, "Error receiving server response.\n"));
-
-    return -1;
-  }
-
-  if (ibuf[0] != '+')
-  {
-    dprint (1, (debugfile, "Invalid response from server: %s\n", ibuf));
-
-    return -1;
-  }
-
-  if ((len = mutt_from_base64 (obuf, ibuf + 2)) == -1)
-  {
-    dprint (1, (debugfile, "Error decoding base64 response.\n"));
-
-    return -1;
-  }
-
-  obuf[len] = '\0';
-  dprint (2, (debugfile, "CRAM challenge: %s\n", obuf));
-
-  /* The client makes note of the data and then responds with a string
-   * consisting of the user name, a space, and a 'digest'. The latter is
-   * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
-   * key is a shared secret and the digested text is the timestamp (including
-   * angle-brackets).
-   * 
-   * Note: The user name shouldn't be quoted. Since the digest can't contain
-   *   spaces, there is no ambiguity. Some servers get this wrong, we'll work
-   *   around them when the bug report comes in. Until then, we'll remain
-   *   blissfully RFC-compliant.
-   */
-  hmac_md5 (pass, obuf, hmac_response);
-  /* dubious optimisation I saw elsewhere: make the whole string in one call */
-  snprintf (obuf, sizeof (obuf),
-    "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-    user,
-    hmac_response[0], hmac_response[1], hmac_response[2], hmac_response[3],
-    hmac_response[4], hmac_response[5], hmac_response[6], hmac_response[7],
-    hmac_response[8], hmac_response[9], hmac_response[10], hmac_response[11],
-    hmac_response[12], hmac_response[13], hmac_response[14], hmac_response[15]);
-  dprint(2, (debugfile, "CRAM response: %s\n", obuf));
-
-  mutt_to_base64 ((unsigned char*) ibuf, (unsigned char*) obuf, strlen (obuf));
-  strcpy (ibuf + strlen (ibuf), "\r\n");
-  mutt_socket_write (idata->conn, ibuf);
-
-  if (mutt_socket_readln (ibuf, LONG_STRING, idata->conn) < 0)
-  {
-    dprint (1, (debugfile, "Error receiving server response.\n"));
-
-    return -1;
-  }
-
-  if (imap_code (ibuf))
-  {
-    dprint (2, (debugfile, "CRAM login complete.\n"));
-
-    return 0;
-  }
-
-  dprint (2, (debugfile, "CRAM login failed.\n"));
-  return -1;
-}
-
-/* this is basically a stripped-down version of the cram-md5 method. */
-
-static int imap_auth_anon (IMAP_DATA* idata)
-{
-  char buf[LONG_STRING];
-
-  dprint (2, (debugfile, "Attempting anonymous login...\n"));
-  mutt_message _("Authenticating (anonymous)...");
-
-  imap_cmd_start (idata, "AUTHENTICATE ANONYMOUS");
-
-  if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0)
-  {
-    dprint (1, (debugfile, "Error receiving server response.\n"));
-
-    return -1;
-  }
-
-  if (buf[0] != '+')
-  {
-    dprint (1, (debugfile, "Invalid response from server.\n"));
-
-    return -1;
-  }
-
-  strfcpy (buf, "ZHVtbXkK\r\n", sizeof (buf));         /* base64 ("dummy") */
-
-  mutt_socket_write (idata->conn, buf);
-
-  if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0)
-  {
-    dprint (1, (debugfile, "Error receiving server response.\n"));
-
-    return -1;
-  }
-
-  if (imap_code (buf))
-  {
-    dprint (2, (debugfile, "Anonymous login complete.\n"));
-
-    return 0;
-  }
-
-  dprint (2, (debugfile, "Anonymous login failed.\n"));
+  NULL
+};
 
-  return -1;
-}
-
-/* imap_authenticate: loop until success or user abort. At each loop, all
- *   supported authentication methods are tried, from strongest to weakest.
- *   Currently available:
- *     GSSAPI: strongest available form, requires Kerberos V infrastructure,
- *       or possibly alternatively Heimdal.
- *     CRAM-MD5: like APOP or CHAP. Safe against replay and sniffing, but
- *       requires that your key be stored on the server, readable by the
- *       server account. UW-IMAP supports this method since at least 4.5, if
- *       the key file exists on the server.
- *     LOGIN: ugh. Don't use this if you can help it. Uses cleartext password
- *       exchange, furthermore uses unix login techniques so this same password
- *       can be used to log in to the server or others that share the
- *       credentials database.
- *   Unavailable:
- *     KERBEROS_V4. Superceded by GSSAPI.
- */
+/* imap_authenticate: oh how simple! loops through authenticators. */
 int imap_authenticate (IMAP_DATA* idata)
 {
-  char buf[LONG_STRING];
-  char user[SHORT_STRING], q_user[SHORT_STRING];
-  char ckey[SHORT_STRING];
-  char pass[SHORT_STRING], q_pass[SHORT_STRING];
-
-  CONNECTION* conn = idata->conn;
-  
-  int r = 1;
+  imap_auth_t* authenticator = imap_authenticators;
+  int r = -1;
 
-  while (r != 0)
+  while (authenticator)
   {
-    if (! (conn->account.flags & M_ACCT_USER))
-    {
-      if (!ImapUser)
-      {
-       strfcpy (user, NONULL(Username), sizeof (user));
-       if (mutt_get_field (_("IMAP Username: "), user, sizeof (user), 0) != 0)
-       {
-         user[0] = 0;
-         return (-1);
-       }
-      }
-      else
-       strfcpy (user, ImapUser, sizeof (user));
-    }
-    else
-      strfcpy (user, conn->account.user, sizeof (user));
-
-    if (!user[0])
-    {
-      if (!mutt_bit_isset (idata->capabilities, AUTH_ANON))
-      {
-       mutt_error _("Anonymous authentication not supported.");
-       return -1;
-      }
-      
-      return imap_auth_anon (idata);
-    }
-    
-#ifdef USE_GSS
-    /* attempt GSSAPI authentication, if available */
-    if (mutt_bit_isset (idata->capabilities, AGSSAPI))
-    {
-      if ((r = imap_auth_gss (idata, user)))
-      {
-        mutt_error _("GSSAPI authentication failed.");
-        sleep (1);
-      }
-      else
-        return 0;
-    }
-    else
-      dprint (2, (debugfile, "GSSAPI authentication is not available\n"));
-#endif
-
-    /* attempt CRAM-MD5 if available */
-    if (mutt_bit_isset (idata->capabilities, ACRAM_MD5))
-    {
-      if (!(conn->account.flags & M_ACCT_CRAM))
-      {
-       if (!ImapCRAMKey)
-       {
-         ckey[0] = '\0';
-         snprintf (buf, sizeof (buf), _("CRAM key for %s@%s: "), user,
-                   conn->account.host);
-         if (mutt_get_field (buf, ckey, sizeof (ckey), M_PASS) != 0)
-           return -1;
-       }
-       else
-         strfcpy (ckey, ImapCRAMKey, sizeof (ckey));
-      }
-      else
-        strfcpy (ckey, conn->account.pass, sizeof (ckey));
-
-      if (*ckey)
-      {
-       if ((r = imap_auth_cram_md5 (idata, user, ckey)))
-       {
-         mutt_error _("CRAM-MD5 authentication failed.");
-         sleep (1);
-         if (!(conn->account.flags & M_ACCT_CRAM))
-           FREE (&ImapCRAMKey);
-         conn->account.flags &= ~M_ACCT_CRAM;
-       }
-       else
-       {
-         strfcpy (conn->account.pass, ckey, sizeof (conn->account.pass));
-         conn->account.flags |= M_ACCT_CRAM;
-         return 0;
-       }
-      }
-      else
-      {
-       mutt_message _("Skipping CRAM-MD5 authentication.");
-       sleep (1);
-      }
-    }
-    else
-      dprint (2, (debugfile, "CRAM-MD5 authentication is not available\n"));
-        
-    if (! (conn->account.flags & M_ACCT_PASS))
-    {
-      if (!ImapPass)
-      {
-       pass[0]=0;
-       snprintf (buf, sizeof (buf), _("Password for %s@%s: "), user,
-          conn->account.host);
-       if (mutt_get_field (buf, pass, sizeof (pass), M_PASS) != 0 ||
-           !pass[0])
-         return -1;
-      }
-      else
-       strfcpy (pass, ImapPass, sizeof (pass));
-    }
-    else
-      strfcpy (pass, conn->account.pass, sizeof (pass));
-
-    imap_quote_string (q_user, sizeof (q_user), user);
-    imap_quote_string (q_pass, sizeof (q_pass), pass);
-
-    mutt_message _("Logging in...");
-
-#ifdef DEBUG
-    /* don't print the password unless we're at the ungodly debugging level
-     * of 5 or higher */
-
-    if (debuglevel < IMAP_LOG_PASS)
-      dprint (2, (debugfile, "Sending LOGIN command for %s...\n", user));
-#endif
-
-    snprintf (buf, sizeof (buf), "LOGIN %s %s", q_user, q_pass);
-    r = imap_exec (buf, sizeof (buf), idata, buf,
-      IMAP_CMD_FAIL_OK | IMAP_CMD_PASS);
-    if (r == -1)
-    {
-      /* connection or protocol problem */
-      imap_error ("imap_authenticate", buf);
-      return (-1);
-    }
-    else if (r == -2)
-    {
-      /* Login failed, try again */
-      mutt_error _("Login failed.");
-      sleep (1);
-
-      if (!(conn->account.flags & M_ACCT_USER))
-       FREE (&ImapUser);
-      if (!(conn->account.flags & M_ACCT_PASS))
-       FREE (&ImapPass);
-      conn->account.flags &= ~M_ACCT_PASS;
-    }
-    else
-    {
-      /* If they have a successful login, we may as well cache the 
-       * user/password. */
-      if (!(conn->account.flags & M_ACCT_USER))
-       strfcpy (conn->account.user, user, sizeof (conn->account.user));
-      if (!(conn->account.flags & M_ACCT_PASS))
-       strfcpy (conn->account.pass, pass, sizeof (conn->account.pass));
-      
-      conn->account.flags |= (M_ACCT_USER | M_ACCT_PASS);
-    }
+    if ((r = (*authenticator)(idata)) != IMAP_AUTH_UNAVAIL)
+      return r;
+    authenticator++;
   }
-  return 0;
+
+  return r;
 }
diff --git a/imap/auth.h b/imap/auth.h
new file mode 100644 (file)
index 0000000..bf3f2c2
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* common defs for authenticators. A good place to set up a generic callback
+ * system */
+
+#ifndef _IMAP_AUTH_H
+#define _IMAP_AUTH_H 1
+
+typedef enum
+{
+  IMAP_AUTH_SUCCESS = 0,
+  IMAP_AUTH_FAILURE,
+  IMAP_AUTH_UNAVAIL
+} imap_auth_res_t;
+
+typedef imap_auth_res_t (*imap_auth_t)(IMAP_DATA* idata);
+
+/* external authenticator prototypes */
+imap_auth_res_t imap_auth_anon (IMAP_DATA* idata);
+imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata);
+imap_auth_res_t imap_auth_login (IMAP_DATA* idata);
+#ifdef USE_GSS
+imap_auth_res_t imap_auth_gss (IMAP_DATA* idata);
+#endif
+#ifdef USE_SASL
+imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata);
+#endif
+
+#endif /* _IMAP_AUTH_H */
diff --git a/imap/auth_anon.c b/imap/auth_anon.c
new file mode 100644 (file)
index 0000000..09ec71d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 1999-2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* IMAP login/authentication code */
+
+#include "mutt.h"
+#include "imap_private.h"
+#include "auth.h"
+
+/* this is basically a stripped-down version of the cram-md5 method. */
+imap_auth_res_t imap_auth_anon (IMAP_DATA* idata)
+{
+  char buf[LONG_STRING];
+
+  if (!mutt_bit_isset (idata->capabilities, AUTH_ANON))
+    return IMAP_AUTH_UNAVAIL;
+
+  if (mutt_account_getuser (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+
+  if (idata->conn->account.user[0] != '\0')
+    return IMAP_AUTH_UNAVAIL;
+
+  mutt_message _("Authenticating (anonymous)...");
+
+  imap_cmd_start (idata, "AUTHENTICATE ANONYMOUS");
+
+  if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0)
+  {
+    dprint (1, (debugfile, "Error receiving server response.\n"));
+    goto bail;
+  }
+
+  if (buf[0] != '+')
+  {
+    dprint (1, (debugfile, "Invalid response from server.\n"));
+    goto bail;
+  }
+
+  strfcpy (buf, "ZHVtbXkK\r\n", sizeof (buf));         /* base64 ("dummy") */
+
+  mutt_socket_write (idata->conn, buf);
+
+  if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0)
+  {
+    dprint (1, (debugfile, "Error receiving server response.\n"));
+    goto bail;
+  }
+
+  if (imap_code (buf))
+    return IMAP_AUTH_SUCCESS;
+
+ bail:
+  mutt_error _("Anonymous authentication failed.");
+  sleep (2);
+  return IMAP_AUTH_FAILURE;
+}
diff --git a/imap/auth_cram.c b/imap/auth_cram.c
new file mode 100644 (file)
index 0000000..9736ee3
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 1999-2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* IMAP login/authentication code */
+
+#include "mutt.h"
+#include "imap_private.h"
+#include "auth.h"
+#include "md5.h"
+
+#define MD5_BLOCK_LEN 64
+#define MD5_DIGEST_LEN 16
+
+/* forward declarations */
+static void hmac_md5 (const char* password, char* challenge,
+  unsigned char* response);
+
+/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. */
+imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata)
+{
+  char ibuf[LONG_STRING], obuf[LONG_STRING];
+  unsigned char hmac_response[MD5_DIGEST_LEN];
+  int len;
+
+  if (!mutt_bit_isset (idata->capabilities, ACRAM_MD5))
+    return IMAP_AUTH_UNAVAIL;
+
+  mutt_message _("Authenticating (CRAM-MD5)...");
+
+  /* get auth info */
+  if (mutt_account_getuser (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+  if (mutt_account_getpass (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+
+  imap_cmd_start (idata, "AUTHENTICATE CRAM-MD5");
+
+  /* From RFC 2195:
+   * The data encoded in the first ready response contains a presumptively
+   * arbitrary string of random digits, a timestamp, and the fully-qualified
+   * primary host name of the server. The syntax of the unencoded form must
+   * correspond to that of an RFC 822 'msg-id' [RFC822] as described in [POP3].
+   */
+  if (mutt_socket_readln (ibuf, sizeof (ibuf), idata->conn) < 0)
+  {
+    dprint (1, (debugfile, "Error receiving server response.\n"));
+    goto bail;
+  }
+
+  if (ibuf[0] != '+')
+  {
+    dprint (1, (debugfile, "Invalid response from server: %s\n", ibuf));
+    goto bail;
+  }
+
+  if ((len = mutt_from_base64 (obuf, ibuf + 2)) == -1)
+  {
+    dprint (1, (debugfile, "Error decoding base64 response.\n"));
+    goto bail;
+  }
+
+  obuf[len] = '\0';
+  dprint (2, (debugfile, "CRAM challenge: %s\n", obuf));
+
+  /* The client makes note of the data and then responds with a string
+   * consisting of the user name, a space, and a 'digest'. The latter is
+   * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
+   * key is a shared secret and the digested text is the timestamp (including
+   * angle-brackets).
+   * 
+   * Note: The user name shouldn't be quoted. Since the digest can't contain
+   *   spaces, there is no ambiguity. Some servers get this wrong, we'll work
+   *   around them when the bug report comes in. Until then, we'll remain
+   *   blissfully RFC-compliant.
+   */
+  hmac_md5 (idata->conn->account.pass, obuf, hmac_response);
+  /* dubious optimisation I saw elsewhere: make the whole string in one call */
+  snprintf (obuf, sizeof (obuf),
+    "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+    idata->conn->account.user,
+    hmac_response[0], hmac_response[1], hmac_response[2], hmac_response[3],
+    hmac_response[4], hmac_response[5], hmac_response[6], hmac_response[7],
+    hmac_response[8], hmac_response[9], hmac_response[10], hmac_response[11],
+    hmac_response[12], hmac_response[13], hmac_response[14], hmac_response[15]);
+  dprint(2, (debugfile, "CRAM response: %s\n", obuf));
+
+  mutt_to_base64 ((unsigned char*) ibuf, (unsigned char*) obuf, strlen (obuf));
+  strcpy (ibuf + strlen (ibuf), "\r\n");
+  mutt_socket_write (idata->conn, ibuf);
+
+  if (mutt_socket_readln (ibuf, LONG_STRING, idata->conn) < 0)
+  {
+    dprint (1, (debugfile, "Error receiving server response.\n"));
+    goto bail;
+  }
+
+  if (imap_code (ibuf))
+    return IMAP_AUTH_SUCCESS;
+
+ bail:
+  mutt_error _("CRAM-MD5 authentication failed.");
+  sleep (2);
+  return IMAP_AUTH_FAILURE;
+}
+
+/* hmac_md5: produce CRAM-MD5 challenge response. */
+static void hmac_md5 (const char* password, char* challenge,
+  unsigned char* response)
+{
+  MD5_CTX ctx;
+  unsigned char ipad[MD5_BLOCK_LEN], opad[MD5_BLOCK_LEN];
+  unsigned char secret[MD5_BLOCK_LEN+1];
+  unsigned char hash_passwd[MD5_DIGEST_LEN];
+  unsigned int secret_len, chal_len;
+  int i;
+
+  secret_len = strlen (password);
+  chal_len = strlen (challenge);
+
+  /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5
+   * digests */
+  if (secret_len > MD5_BLOCK_LEN)
+  {
+    MD5Init (&ctx);
+    MD5Update (&ctx, (unsigned char*) password, secret_len);
+    MD5Final (hash_passwd, &ctx);
+    strfcpy ((char*) secret, (char*) hash_passwd, MD5_DIGEST_LEN);
+    secret_len = MD5_DIGEST_LEN;
+  }
+  else
+    strfcpy ((char *) secret, password, sizeof (secret));
+
+  memset (ipad, 0, sizeof (ipad));
+  memset (opad, 0, sizeof (opad));
+  memcpy (ipad, secret, secret_len);
+  memcpy (opad, secret, secret_len);
+
+  for (i = 0; i < MD5_BLOCK_LEN; i++)
+  {
+    ipad[i] ^= 0x36;
+    opad[i] ^= 0x5c;
+  }
+
+  /* inner hash: challenge and ipadded secret */
+  MD5Init (&ctx);
+  MD5Update (&ctx, ipad, MD5_BLOCK_LEN);
+  MD5Update (&ctx, (unsigned char*) challenge, chal_len);
+  MD5Final (response, &ctx);
+
+  /* outer hash: inner hash and opadded secret */
+  MD5Init (&ctx);
+  MD5Update (&ctx, opad, MD5_BLOCK_LEN);
+  MD5Update (&ctx, response, MD5_DIGEST_LEN);
+  MD5Final (response, &ctx);
+}
index 48a8ba1f27364b4ff5d557e11628f34ba6755a76..73e3f8b8158b8b848fbafd60d84d1dadf3d31acf 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "mutt.h"
 #include "imap_private.h"
+#include "auth.h"
 
 #include <netinet/in.h>
 
@@ -39,9 +40,8 @@
 #define GSS_AUTH_P_INTEGRITY 2
 #define GSS_AUTH_P_PRIVACY   4
 
-/* imap_auth_gss: AUTH=GSSAPI support. Used unconditionally if the server
- *   supports it */
-int imap_auth_gss (IMAP_DATA* idata, const char* user)
+/* imap_auth_gss: AUTH=GSSAPI support. */
+imap_auth_res_t imap_auth_gss (IMAP_DATA* idata)
 {
   gss_buffer_desc request_buf, send_token;
   gss_buffer_t sec_token;
@@ -54,8 +54,14 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
   unsigned long buf_size;
 
-  dprint (2, (debugfile, "Attempting GSS login...\n"));
+  if (!mutt_bit_isset (idata->capabilities, AGSSAPI))
+    return IMAP_AUTH_UNAVAIL;
 
+  mutt_message _("Authenticating (GSS)...");
+
+  if (mutt_account_getuser (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+  
   /* get an IMAP service ticket for the server */
   snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->account.host);
   request_buf.value = buf1;
@@ -65,7 +71,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   if (maj_stat != GSS_S_COMPLETE)
   {
     dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1));
-    return -1;
+    goto bail;
   }
 #ifdef DEBUG   
   else if (debuglevel >= 2)
@@ -87,16 +93,14 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   {
     dprint (1, (debugfile, "Error receiving server response.\n"));
     gss_release_name (&min_stat, &target_name);
-
-    return -1;
+    goto bail;
   }
 
   if (buf1[0] != '+')
   {
     dprint (2, (debugfile, "Invalid response from server: %s\n", buf1));
     gss_release_name (&min_stat, &target_name);
-
-    return -1;
+    goto bail;
   }
 
   /* now start the security context initialisation loop... */
@@ -126,8 +130,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
       /* end authentication attempt */
       mutt_socket_write (idata->conn, "*\r\n");
       mutt_socket_readln (buf1, sizeof (buf1), idata->conn);
-
-      return -1;
+      goto bail;
     }
 
     /* send token */
@@ -143,8 +146,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
       {
         dprint (1, (debugfile, "Error receiving server response.\n"));
         gss_release_name (&min_stat, &target_name);
-
-        return -1;
+       goto bail;
       }
 
       request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
@@ -160,8 +162,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   if (mutt_socket_readln (buf1, sizeof (buf1), idata->conn) < 0)
   {
     dprint (1, (debugfile, "Error receiving server response.\n"));
-
-    return -1;
+    goto bail;
   }
   request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
   request_buf.value = buf2;
@@ -172,9 +173,8 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   {
     dprint (2, (debugfile, "Couldn't unwrap security level data\n"));
     gss_release_buffer (&min_stat, &send_token);
-
     mutt_socket_write(idata->conn, "*\r\n");
-    return -1;
+    goto bail;
   }
   dprint (2, (debugfile, "Credential exchange complete\n"));
 
@@ -184,9 +184,8 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   {
     dprint (2, (debugfile, "Server requires integrity or privace\n"));
     gss_release_buffer (&min_stat, &send_token);
-
     mutt_socket_write(idata->conn, "*\r\n");
-    return -1;
+    goto bail;
   }
 
   /* we don't care about buffer size if we don't wrap content. But here it is */
@@ -204,21 +203,21 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   memcpy (buf1, &buf_size, 4);
   buf1[0] = GSS_AUTH_P_NONE;
   /* server decides if principal can log in as user */
-  strncpy (buf1 + 4, user, sizeof (buf1) - 4);
+  strncpy (buf1 + 4, idata->conn->account.user, sizeof (buf1) - 4);
   request_buf.value = buf1;
-  request_buf.length = 4 + strlen (user) + 1;
+  request_buf.length = 4 + strlen (idata->conn->account.user) + 1;
   maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
     &cflags, &send_token);
   if (maj_stat != GSS_S_COMPLETE)
   {
     dprint (2, (debugfile, "Error creating login request\n"));
-
     mutt_socket_write(idata->conn, "*\r\n");
-    return -1;
+    goto bail;
   }
 
   mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length);
-  dprint (2, (debugfile, "Requesting authorisation as %s\n", user));
+  dprint (2, (debugfile, "Requesting authorisation as %s\n",
+    idata->conn->account.user));
   strncat (buf1, "\r\n", sizeof (buf1));
   mutt_socket_write (idata->conn, buf1);
 
@@ -226,9 +225,8 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
   if (mutt_socket_readln (buf1, GSS_BUFSIZE, idata->conn) < 0)
   {
     dprint (1, (debugfile, "Error receiving server response.\n"));
-
     mutt_socket_write(idata->conn, "*\r\n");
-    return -1;
+    goto bail;
   }
   if (imap_code (buf1))
   {
@@ -238,8 +236,7 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
     if (maj_stat != GSS_S_COMPLETE)
     {
       dprint (1, (debugfile, "Error releasing credentials\n"));
-
-      return -1;
+      goto bail;
     }
     /* send_token may contain a notification to the server to flush
      * credentials. RFC 1731 doesn't specify what to do, and since this
@@ -247,13 +244,11 @@ int imap_auth_gss (IMAP_DATA* idata, const char* user)
      * enough to flush its own credentials */
     gss_release_buffer (&min_stat, &send_token);
 
-    dprint (2, (debugfile, "GSS login complete\n"));
-
-    return 0;
+    return IMAP_AUTH_SUCCESS;
   }
 
-  /* logon failed */
-  dprint (2, (debugfile, "GSS login failed.\n"));
-
-  return -1;
+ bail:
+  mutt_error _("GSS authentication failed.");
+  sleep (2);
+  return IMAP_AUTH_FAILURE;
 }
diff --git a/imap/auth_login.c b/imap/auth_login.c
new file mode 100644 (file)
index 0000000..757c353
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 1999-2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* plain LOGIN support */
+
+#include "mutt.h"
+#include "imap_private.h"
+#include "auth.h"
+
+/* imap_auth_login: Plain LOGIN support */
+imap_auth_res_t imap_auth_login (IMAP_DATA* idata)
+{
+  char q_user[SHORT_STRING], q_pass[SHORT_STRING];
+  char buf[LONG_STRING];
+  int rc;
+
+  if (mutt_account_getuser (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+  if (mutt_account_getpass (&idata->conn->account))
+    return IMAP_AUTH_FAILURE;
+
+  mutt_message _("Logging in...");
+
+  imap_quote_string (q_user, sizeof (q_user), idata->conn->account.user);
+  imap_quote_string (q_pass, sizeof (q_pass), idata->conn->account.pass);
+
+#ifdef DEBUG
+  /* don't print the password unless we're at the ungodly debugging level
+   * of 5 or higher */
+
+  if (debuglevel < IMAP_LOG_PASS)
+    dprint (2, (debugfile, "Sending LOGIN command for %s...\n",
+      idata->conn->account.user));
+#endif
+
+  snprintf (buf, sizeof (buf), "LOGIN %s %s", q_user, q_pass);
+  rc = imap_exec (buf, sizeof (buf), idata, buf,
+    IMAP_CMD_FAIL_OK | IMAP_CMD_PASS);
+  
+  if (!rc)
+    return IMAP_AUTH_SUCCESS;
+
+  else if (rc == -1)
+    dprint (1, (debugfile, "imap_auth_login: Error logging in.\n"));
+
+  mutt_error _("Login failed.");
+  sleep (2);
+  return IMAP_AUTH_FAILURE;
+}
diff --git a/imap/auth_sasl.c b/imap/auth_sasl.c
new file mode 100644 (file)
index 0000000..ae77a17
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* SASL login/authentication code */
+
+#include "mutt.h"
+#include "mutt_sasl.h"
+#include "imap_private.h"
+#include "auth.h"
+
+#include <sasl.h>
+#include <saslutil.h>
+
+/* imap_auth_sasl: Default authenticator if available. */
+imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata)
+{
+  sasl_conn_t* saslconn;
+  int rc;
+  char buf[LONG_STRING];
+  const char* mech;
+  char* pc;
+  unsigned int len;
+
+  if (mutt_sasl_start () != SASL_OK)
+    return IMAP_AUTH_FAILURE;
+
+  /* TODO: set fourth option to SASL_SECURITY_LAYER once we have a wrapper
+   *  (ie more than auth code) for SASL. */
+  rc = sasl_client_new ("imap", idata->conn->account.host,
+    mutt_sasl_get_callbacks (&idata->conn->account), 0, &saslconn);
+
+  if (rc != SASL_OK)
+  {
+    dprint (1, (debugfile, "imap_auth_sasl: Error allocating SASL connection.\n"));
+    return IMAP_AUTH_FAILURE;
+  }
+
+  rc = sasl_client_start (saslconn, idata->capstr, NULL, NULL, &pc, &len,
+    &mech);
+
+  if (rc != SASL_OK && rc != SASL_CONTINUE)
+  {
+    dprint (1, (debugfile, "imap_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n"));
+    /* SASL doesn't support LOGIN, so fall back */
+
+    return IMAP_AUTH_UNAVAIL;
+  }
+
+  mutt_message _("Authenticating (SASL)...");
+
+  snprintf (buf, sizeof (buf), "AUTHENTICATE %s", mech);
+  imap_cmd_start (idata, buf);
+
+  /* I believe IMAP requires a server response (even if empty) before the
+   * client can send data, so len should always be zero at this point */
+  if (len != 0)
+  {
+    dprint (1, (debugfile, "imap_auth_sasl: SASL produced invalid exchange!\n"));
+    goto bail;
+  }
+
+  while (rc == SASL_CONTINUE)
+  {
+    if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0)
+      goto bail;
+
+    if (!mutt_strncmp (buf, "+ ", 2))
+      if (sasl_decode64 (buf+2, strlen (buf+2), buf, &len) != SASL_OK)
+      {
+       dprint (1, (debugfile, "imap_auth_sasl: error base64-decoding server response.\n"));
+       goto bail;
+      }
+
+    rc = sasl_client_step (saslconn, buf, len, NULL, &pc, &len);
+
+    if (rc != SASL_CONTINUE)
+      break;
+
+    /* send out response, or line break if none needed */
+    if (sasl_encode64 (pc, len, buf, sizeof (buf), &len) != SASL_OK)
+    {
+      dprint (1, (debugfile, "imap_auth_sasl: error base64-encoding client response.\n"));
+      goto bail;
+    }
+      
+    strfcpy (buf + len, "\r\n", sizeof (buf) - len);
+    mutt_socket_write (idata->conn, buf);
+  }
+
+  if (rc != SASL_OK)
+    goto bail;
+
+  if (imap_code (buf))
+  {
+    /* later we'll want to keep saslconn, when we support a protection layer.
+     * For now it shouldn't hurt to dispose of it at this point. */
+    sasl_dispose (&saslconn);
+    return IMAP_AUTH_SUCCESS;
+  }
+
+ bail:
+  mutt_error _("SASL authentication failed.");
+  sleep(2);
+  sasl_dispose (&saslconn);
+
+  return IMAP_AUTH_FAILURE;
+}
index 12bacce27fe8f8b59f66ea44e608157d2f0bddb4..02d2bb3c2acdd8bd0d61dd4f02d4004b2cbadf4c 100644 (file)
@@ -274,6 +274,8 @@ static void cmd_parse_capabilities (IMAP_DATA* idata, char* s)
 {
   int x;
 
+  idata->capstr = safe_strdup (imap_next_word (s));
+  
   while (*s) 
   {
     for (x = 0; x < CAPMAX; x++)
@@ -283,7 +285,7 @@ static void cmd_parse_capabilities (IMAP_DATA* idata, char* s)
        break;
       }
     s = imap_next_word (s);
-  }   
+  }
 }
 
 /* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
index fc834d1d050779956ec93169fa18b2e02c2adbd0..991e300c92d443bfd7a0a97ef24bf998a1578def 100644 (file)
@@ -41,7 +41,7 @@
 static int imap_get_delim (IMAP_DATA *idata);
 static char* imap_get_flags (LIST** hflags, char* s);
 static int imap_check_acl (IMAP_DATA *idata);
-static int imap_check_capabilities (IMAP_DATA *idata);
+static int imap_check_capabilities (IMAP_DATAidata);
 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
   const char* str, char* flags);
 
@@ -86,7 +86,7 @@ void imap_logout_all (void)
   {
     tmp = conn;
 
-    if (conn->account.type == M_ACCT_TYPE_IMAP && conn->up)
+    if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0)
     {
       mutt_message (_("Closing connection to %s..."), conn->account.host);
       imap_logout ((IMAP_DATA*) conn->data);
@@ -482,7 +482,8 @@ static int imap_check_acl (IMAP_DATA *idata)
   return 0;
 }
 
-static int imap_check_capabilities (IMAP_DATA *idata)
+/* imap_check_capabilities: make sure we can log in to this server. */
+static int imap_check_capabilities (IMAP_DATA* idata)
 {
   char buf[LONG_STRING];
 
@@ -491,13 +492,16 @@ static int imap_check_capabilities (IMAP_DATA *idata)
     imap_error ("imap_check_capabilities", buf);
     return -1;
   }
+
   if (!(mutt_bit_isset(idata->capabilities,IMAP4)
       ||mutt_bit_isset(idata->capabilities,IMAP4REV1)))
   {
     mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
     sleep (5); /* pause a moment to let the user see the error */
+
     return -1;
   }
+
   return 0;
 }
 
@@ -562,44 +566,35 @@ int imap_open_connection (IMAP_DATA* idata)
   idata->state = IMAP_CONNECTED;
 
   if (mutt_socket_readln (buf, sizeof (buf), idata->conn) < 0)
-  {
-    mutt_socket_close (idata->conn);
-    idata->state = IMAP_DISCONNECTED;
-
-    return -1;
-  }
+    goto bail;
 
   if (mutt_strncmp ("* OK", buf, 4) == 0)
   {
-    if (imap_check_capabilities(idata) != 0 
-       || imap_authenticate (idata) != 0)
-    {
-      mutt_socket_close (idata->conn);
-      idata->state = IMAP_DISCONNECTED;
-      return -1;
-    }
+    if (imap_check_capabilities (idata) || imap_authenticate (idata))
+      goto bail;
   }
   else if (mutt_strncmp ("* PREAUTH", buf, 9) == 0)
   {
     if (imap_check_capabilities(idata) != 0)
-    {
-      mutt_socket_close (idata->conn);
-      idata->state = IMAP_DISCONNECTED;
-      return -1;
-    }
+      goto bail;
   } 
   else
   {
     imap_error ("imap_open_connection()", buf);
-    mutt_socket_close (idata->conn);
-    idata->state = IMAP_DISCONNECTED;
-    return -1;
+    goto bail;
   }
 
+  FREE (&idata->capstr);
   idata->state = IMAP_AUTHENTICATED;
 
   imap_get_delim (idata);
   return 0;
+
+ bail:
+  FREE (&idata->capstr);
+  mutt_socket_close (idata->conn);
+  idata->state = IMAP_DISCONNECTED;
+  return -1;
 }
 
 /* imap_get_flags: Make a simple list out of a FLAGS response.
index 3f56380fd16a644fe3d4e0a292ec17217ad7da47..e404f19d0c5eeea317c5c6846e340e56a4ac8f99 100644 (file)
@@ -110,7 +110,7 @@ enum
   TORDEREDSUBJECT,             /* THREAD=ORDEREDSUBJECT */
   UIDPLUS,                     /* RFC 2859: IMAP4 UIDPLUS extension */
   AUTH_ANON,                   /* AUTH=ANONYMOUS */
-  
+
   CAPMAX
 };
 
@@ -145,6 +145,15 @@ typedef struct
   unsigned char status;
   unsigned char check_status;
   unsigned char capabilities[(CAPMAX + 7)/8];
+  /* let me explain capstr: SASL needs the capability string (not bits).
+   * we have 3 options:
+   *   1. rerun CAPABILITY inside SASL function.
+   *   2. build appropriate CAPABILITY string by reverse-engineering from bits.
+   *   3. keep a copy until after authentication.
+   * I've chosen (3) for now. (2) might not be too bad, but it involves
+   * tracking all possible capabilities. bah. (1) I don't like because
+   * it's just no fun to get the same information twice */
+  char* capstr;
   char seq[SEQLEN+1];
 
   /* The following data is all specific to the currently SELECTED mbox */
diff --git a/init.h b/init.h
index 6f8b613429a3514928cf8125e783e8e47fd394bc..42d8bb877edf984dc4a820db80bab6ae815bdb44 100644 (file)
--- a/init.h
+++ b/init.h
@@ -685,15 +685,6 @@ struct option_t MuttVars[] = {
   ** .pp
   ** This variable defaults to your user name on the local machine.
   */
-  { "imap_cramkey",    DT_STR,  R_NONE, UL &ImapCRAMKey, UL 0 },
-  /*
-  ** .pp
-  ** Sets your CRAM secret, for use with the CRAM-MD5 IMAP authentication
-  ** method (this is the IMAP equivelent of APOP). This method will be
-  ** attempted automatically if the server supports it, in preference to the
-  ** less secure login technique. If you use CRAM-MD5, you do not need to set
-  ** \fIimap_pass\fP.
-  */
   { "imap_pass",       DT_STR,  R_NONE, UL &ImapPass, UL 0 },
   /*
   ** .pp
index ad04c73c365af3e8e2f570dbefa5514b014c05d3..5297916219dc97fa4b4b333272a2a45ab0736990 100644 (file)
@@ -17,7 +17,7 @@ AC_DEFUN(MUTT_AM_WITH_NLS,
   [AC_MSG_CHECKING([whether NLS is requested])
     dnl Default is enabled NLS
     AC_ARG_ENABLE(nls,
-      [  --disable-nls           do not use Native Language Support],
+      [  --disable-nls              Do not use Native Language Support],
       USE_NLS=$enableval, USE_NLS=yes)
     AC_MSG_RESULT($USE_NLS)
     AC_SUBST(USE_NLS)
@@ -29,7 +29,7 @@ AC_DEFUN(MUTT_AM_WITH_NLS,
       AC_DEFINE(ENABLE_NLS)
       AC_MSG_CHECKING([whether included gettext is requested])
       AC_ARG_WITH(included-gettext,
-        [  --with-included-gettext use the GNU gettext library included here],
+        [  --with-included-gettext    Use the GNU gettext library included here],
         nls_cv_force_use_gnu_gettext=$withval,
         nls_cv_force_use_gnu_gettext=no)
       AC_MSG_RESULT($nls_cv_force_use_gnu_gettext)
@@ -84,7 +84,7 @@ AC_DEFUN(MUTT_AM_WITH_NLS,
         if test "$CATOBJEXT" = "NONE"; then
          AC_MSG_CHECKING([whether catgets can be used])
          AC_ARG_WITH(catgets,
-           [  --with-catgets          use catgets functions if available],
+           [  --with-catgets             Use catgets functions if available],
            nls_cv_use_catgets=$withval, nls_cv_use_catgets=no)
          AC_MSG_RESULT($nls_cv_use_catgets)
 
diff --git a/main.c b/main.c
index 44d681939c8bd05b4816dfc6985b3d3ef2cedb1a..64c6ce329e6aec1b478017aefd17d60d7bbad42d 100644 (file)
--- a/main.c
+++ b/main.c
@@ -210,6 +210,12 @@ static void show_version (void)
 #endif
        );
   puts (
+#ifdef USE_POP
+       "+USE_POP  "
+#else
+       "-USE_POP  "
+#endif
+
 #ifdef USE_IMAP
         "+USE_IMAP  "
 #else
@@ -222,12 +228,6 @@ static void show_version (void)
        "-USE_GSS  "
 #endif
 
-#ifdef HAVE_HEIMDAHL
-       "+HAVE_HEIMDAHL  "
-#else
-       "-HAVE_HEIMDAHL  "
-#endif
-       
        
 #ifdef USE_SSL
        "+USE_SSL  "
@@ -235,14 +235,13 @@ static void show_version (void)
        "-USE_SSL  "
 #endif
 
-       "\n"
-       
-#ifdef USE_POP
-       "+USE_POP  "
+#ifdef USE_SASL
+       "+USE_SASL  "
 #else
-       "-USE_POP  "
+       "-USE_SASL  "
 #endif
-
+       "\n"
+       
 #ifdef HAVE_REGCOMP
        "+HAVE_REGCOMP  "
 #else
diff --git a/mutt_sasl.c b/mutt_sasl.c
new file mode 100644 (file)
index 0000000..b02b5a2
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* common SASL helper routines */
+
+#include "mutt.h"
+#include "account.h"
+#include "mutt_sasl.h"
+#include "mutt_socket.h"
+
+#include <sasl.h>
+
+static sasl_callback_t mutt_sasl_callbacks[3];
+
+/* callbacks */
+static int mutt_sasl_cb_log (void* context, int priority, const char* message);
+static int mutt_sasl_cb_authname (void* context, int id, const char** result,
+  unsigned int* len);
+static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
+  sasl_secret_t** psecret);
+
+/* mutt_sasl_start: called before doing a SASL exchange - initialises library
+ *   (if neccessary). */
+int mutt_sasl_start (void)
+{
+  static unsigned char sasl_init = 0;
+
+  sasl_callback_t* callback, callbacks[2];
+  int rc;
+
+  if (!sasl_init) {
+    /* set up default logging callback */
+    callback = callbacks;
+
+    callback->id = SASL_CB_LOG;
+    callback->proc = mutt_sasl_cb_log;
+    callback->context = NULL;
+    callback++;
+
+    callback->id = SASL_CB_LIST_END;
+    callback->proc = NULL;
+    callback->context = NULL;
+
+    rc = sasl_client_init (callbacks);
+
+    if (rc != SASL_OK)
+    {
+      dprint (1, (debugfile, "mutt_sasl_start: libsasl initialisation failed.\n"));
+      return SASL_FAIL;
+    }
+
+    sasl_init = 1;
+  }
+
+  return SASL_OK;
+}
+
+sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account)
+{
+  sasl_callback_t* callback;
+
+  callback = mutt_sasl_callbacks;
+
+  callback->id = SASL_CB_AUTHNAME;
+  callback->proc = mutt_sasl_cb_authname;
+  callback->context = account;
+  callback++;
+
+  callback->id = SASL_CB_PASS;
+  callback->proc = mutt_sasl_cb_pass;
+  callback->context = account;
+  callback++;
+
+  callback->id = SASL_CB_LIST_END;
+  callback->proc = NULL;
+  callback->context = NULL;
+
+  return mutt_sasl_callbacks;
+}
+
+/* mutt_sasl_cb_log: callback to log SASL messages */
+static int mutt_sasl_cb_log (void* context, int priority, const char* message)
+{
+  dprint (priority, (debugfile, "SASL: %s\n", message));
+
+  return SASL_OK;
+}
+
+/* mutt_sasl_cb_authname: callback to retrieve authname from ACCOUNT */
+static int mutt_sasl_cb_authname (void* context, int id, const char** result,
+  unsigned* len)
+{
+  ACCOUNT* account = (ACCOUNT*) context;
+
+  *result = NULL;
+  if (len)
+    *len = 0;
+
+  if (!account)
+    return SASL_BADPARAM;
+
+  dprint (2, (debugfile, "mutt_sasl_cb_authname: getting user for %s:%u\n",
+             account->host, account->port));
+
+  if (mutt_account_getuser (account))
+    return SASL_FAIL;
+
+  *result = account->user;
+
+  if (len)
+    *len = strlen (*result);
+
+  return SASL_OK;
+}
+
+static int mutt_sasl_cb_pass (sasl_conn_t* conn, void* context, int id,
+  sasl_secret_t** psecret)
+{
+  ACCOUNT* account = (ACCOUNT*) context;
+  int len;
+
+  if (!account || !psecret)
+    return SASL_BADPARAM;
+
+  dprint (2, (debugfile,
+    "mutt_sasl_cb_pass: getting password for %s@%s:%u\n", account->user,
+    account->host, account->port));
+
+  if (mutt_account_getpass (account))
+    return SASL_FAIL;
+
+  len = strlen (account->pass);
+
+  *psecret = (sasl_secret_t*) malloc (sizeof (sasl_secret_t) + len);
+  (*psecret)->len = len;
+  strcpy ((*psecret)->data, account->pass);
+
+  return SASL_OK;
+}
diff --git a/mutt_sasl.h b/mutt_sasl.h
new file mode 100644 (file)
index 0000000..e979e16
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2000 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
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */ 
+
+/* common SASL helper routines */
+
+#ifndef _MUTT_SASL_H_
+#define _MUTT_SASL_H_ 1
+
+#include "mutt_socket.h"
+
+#include <sasl.h>
+
+int mutt_sasl_start (void);
+sasl_callback_t* mutt_sasl_get_callbacks (ACCOUNT* account);
+
+#endif /* _MUTT_SASL_H_ */
index 0d2d127039630e34570ebe64bcd72863faddb499..a588fe1e1cecf69697928468021e2bd9c3fe4216 100644 (file)
@@ -22,7 +22,7 @@
 #include "globals.h"
 #include "mutt_socket.h"
 #ifdef USE_SSL
-#include "imap_ssl.h"
+# include "imap_ssl.h"
 #endif
 
 #include <unistd.h>
@@ -44,20 +44,17 @@ static CONNECTION* socket_new_conn ();
 /* Wrappers */
 int mutt_socket_open (CONNECTION* conn) 
 {
-  int rc;
-  
-  rc = conn->open (conn);
-  if (!rc)
-    conn->up = 1;
-
-  return rc;
+  return conn->open (conn);
 }
 
 int mutt_socket_close (CONNECTION* conn)
 {
-  conn->up = 0;
+  int rc;
+
+  rc = conn->close (conn);
+  conn->fd = -1;
 
-  return conn->close (conn);
+  return rc;
 }
 
 int mutt_socket_write_d (CONNECTION *conn, const char *buf, int dbg)
@@ -146,11 +143,10 @@ void mutt_socket_free (CONNECTION* conn)
 }
 
 /* mutt_conn_find: find a connection off the list of connections whose
- *   account matches account. If newconn is true, don't reuse one, but add
- *   it to the list. If start is not null, only search for connections after
- *   the given connection (allows higher level socket code to make more
- *   fine-grained searches than account info - eg in IMAP we may wish
- *   to find a connection which is not in IMAP_SELECTED state) */
+ *   account matches account. If start is not null, only search for
+ *   connections after the given connection (allows higher level socket code
+ *   to make more fine-grained searches than account info - eg in IMAP we may
+ *   wish to find a connection which is not in IMAP_SELECTED state) */
 CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account)
 {
   CONNECTION* conn;
index 6b8590dacd9985e5dcf21ebd57d006a84ab3522b..4e15183645c3fd1afb14b9426284dadd94b2073c 100644 (file)
@@ -21,6 +21,7 @@
 #define _MUTT_SOCKET_H_ 1
 
 #include "account.h"
+#include "lib.h"
 
 /* logging levels */
 #define M_SOCK_LOG_CMD  2
@@ -44,10 +45,6 @@ typedef struct _connection
   int (*write) (struct _connection *conn, const char *buf);
   int (*open) (struct _connection *conn);
   int (*close) (struct _connection *conn);
-
-  /* status bits */
-  
-  int up : 1; /* is the connection up? */
 } CONNECTION;
 
 int mutt_socket_open (CONNECTION* conn);