summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Roessler <roessler@does-not-exist.org>2000-07-31 07:18:28 +0000
committerThomas Roessler <roessler@does-not-exist.org>2000-07-31 07:18:28 +0000
commit61333b001c09fb24709398d2ad2dae16b8451080 (patch)
treec9cfc5f76b32aa8f3a2780d213079be6f68a0c30
parente15e685b533f3817a9adb99824f03f03342c64d9 (diff)
Brendan Cully's SASL patch. I hope I didn't miss any files.
-rw-r--r--Makefile.am8
-rw-r--r--acconfig.h6
-rw-r--r--account.c54
-rw-r--r--account.h8
-rw-r--r--configure.in35
-rw-r--r--globals.h1
-rw-r--r--imap/Makefile.am13
-rw-r--r--imap/auth.c399
-rw-r--r--imap/auth.h45
-rw-r--r--imap/auth_anon.c72
-rw-r--r--imap/auth_cram.c170
-rw-r--r--imap/auth_gss.c65
-rw-r--r--imap/auth_login.c64
-rw-r--r--imap/auth_sasl.c122
-rw-r--r--imap/command.c4
-rw-r--r--imap/imap.c43
-rw-r--r--imap/imap_private.h11
-rw-r--r--init.h9
-rw-r--r--m4/gettext.m46
-rw-r--r--main.c23
-rw-r--r--mutt_sasl.c154
-rw-r--r--mutt_sasl.h31
-rw-r--r--mutt_socket.c26
-rw-r--r--mutt_socket.h5
24 files changed, 879 insertions, 495 deletions
diff --git a/Makefile.am b/Makefile.am
index fb23509d..e906d61a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/acconfig.h b/acconfig.h
index c31c44c3..21747e6b 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -46,13 +46,17 @@
/* 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 */
diff --git a/account.c b/account.c
index 52bb58a1..f095763e 100644
--- 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;
+}
diff --git a/account.h b/account.h
index 20c5e63a..9e21c269 100644
--- 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_ */
diff --git a/configure.in b/configure.in
index 0894fbc6..a82976f6 100644
--- a/configure.in
+++ b/configure.in
@@ -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])
diff --git a/globals.h b/globals.h
index 2e403189..3fab5fe0 100644
--- 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);
diff --git a/imap/Makefile.am b/imap/Makefile.am
index 5ff4d388..8f5c6486 100644
--- a/imap/Makefile.am
+++ b/imap/Makefile.am
@@ -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)
diff --git a/imap/auth.c b/imap/auth.c
index 0c857761..26f2feff 100644
--- a/imap/auth.c
+++ b/imap/auth.c
@@ -22,390 +22,37 @@
#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
index 00000000..bf3f2c21
--- /dev/null
+++ b/imap/auth.h
@@ -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
index 00000000..09ec71db
--- /dev/null
+++ b/imap/auth_anon.c
@@ -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
index 00000000..9736ee36
--- /dev/null
+++ b/imap/auth_cram.c
@@ -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);
+