summaryrefslogtreecommitdiffstats
path: root/imap
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 /imap
parente15e685b533f3817a9adb99824f03f03342c64d9 (diff)
Brendan Cully's SASL patch. I hope I didn't miss any files.
Diffstat (limited to 'imap')
-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
11 files changed, 568 insertions, 440 deletions
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);
+
+ 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);
+}
diff --git a/imap/auth_gss.c b/imap/auth_gss.c
index 48a8ba1f..73e3f8b8 100644
--- a/imap/auth_gss.c
+++ b/imap/auth_gss.c
@@ -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
index 00000000..757c3538
--- /dev/null
+++ b/imap/auth_login.c
@@ -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
index 00000000..ae77a17b
--- /dev/null
+++ b/imap/auth_sasl.c
@@ -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.
+ *
+ *