aboutsummaryrefslogtreecommitdiffstats
path: root/imap.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>1999-06-08 07:20:17 +0000
committerEric S. Raymond <esr@thyrsus.com>1999-06-08 07:20:17 +0000
commit6f232ed6b5343dea62abdbbd7d38b48858bee373 (patch)
tree8898d48f48c81a3ded8b0aeb73246af5b22d52e5 /imap.c
parent92397efcc624980fdf8467449b69d88d4acc4530 (diff)
downloadfetchmail-6f232ed6b5343dea62abdbbd7d38b48858bee373.tar.gz
fetchmail-6f232ed6b5343dea62abdbbd7d38b48858bee373.tar.bz2
fetchmail-6f232ed6b5343dea62abdbbd7d38b48858bee373.zip
CRAM-MD5 authentication support a la RFC2195.
svn path=/trunk/; revision=2485
Diffstat (limited to 'imap.c')
-rw-r--r--imap.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/imap.c b/imap.c
index 1b62f65b..94e4da30 100644
--- a/imap.c
+++ b/imap.c
@@ -37,6 +37,8 @@
#include <gssapi/gssapi_generic.h>
#endif
+#include "md5.h"
+
#if OPIE
#include <opie.h>
#endif /* OPIE */
@@ -588,6 +590,127 @@ static int do_gssauth(int sock, char *hostname, char *username)
}
#endif /* GSSAPI */
+static void hmac_md5 (unsigned char *password, size_t pass_len,
+ unsigned char *challenge, size_t chal_len,
+ unsigned char *response, size_t resp_len)
+{
+ int i;
+ unsigned char ipad[64];
+ unsigned char opad[64];
+ unsigned char hash_passwd[16];
+
+ MD5_CTX ctx;
+
+ if (resp_len != 16)
+ return;
+
+ if (pass_len > sizeof (ipad))
+ {
+ MD5Init (&ctx);
+ MD5Update (&ctx, password, pass_len);
+ MD5Final (hash_passwd, &ctx);
+ password = hash_passwd; pass_len = sizeof (hash_passwd);
+ }
+
+ memset (ipad, 0, sizeof (ipad));
+ memset (opad, 0, sizeof (opad));
+ memcpy (ipad, password, pass_len);
+ memcpy (opad, password, pass_len);
+
+ for (i=0; i<64; i++) {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+
+ MD5Init (&ctx);
+ MD5Update (&ctx, ipad, sizeof (ipad));
+ MD5Update (&ctx, challenge, chal_len);
+ MD5Final (response, &ctx);
+
+ MD5Init (&ctx);
+ MD5Update (&ctx, opad, sizeof (opad));
+ MD5Update (&ctx, response, resp_len);
+ MD5Final (response, &ctx);
+}
+
+
+static int do_cram_md5 (int sock, struct query *ctl)
+/* authenticate as per RFC2195 */
+{
+ int result;
+ int len;
+ unsigned char buf1[1024];
+ unsigned char msg_id[768];
+ unsigned char response[16];
+ unsigned char reply[1024];
+
+ gen_send (sock, "AUTHENTICATE CRAM-MD5");
+
+ /* From RFC2195:
+ * The data encoded in the first ready response contains an
+ * 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 (result = gen_recv (sock, buf1, sizeof (buf1))) {
+ return result;
+ }
+
+ len = from64tobits (msg_id, buf1);
+ if (len < 0) {
+ report (stderr, _("could not decode BASE64 challenge\n"));
+ return PS_AUTHFAIL;
+ } else if (len < sizeof (msg_id)) {
+ msg_id[len] = 0;
+ } else {
+ msg_id[sizeof (msg_id)-1] = 0;
+ }
+ if (outlevel >= O_DEBUG) {
+ report (stdout, "decoded as %s\n", msg_id);
+ }
+
+ /* 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).
+ */
+
+ hmac_md5 (ctl->password, strlen (ctl->password),
+ msg_id, strlen (msg_id),
+ response, sizeof (response));
+
+ snprintf (reply, sizeof (reply),
+ "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ ctl->remotename,
+ response[0], response[1], response[2], response[3],
+ response[4], response[5], response[6], response[7],
+ response[8], response[9], response[10], response[11],
+ response[12], response[13], response[14], response[15]);
+
+ if (outlevel >= O_DEBUG) {
+ report (stdout, "replying with %s\n", reply);
+ }
+
+ to64frombits (buf1, reply, strlen (reply));
+ if (outlevel >= O_MONITOR) {
+ report (stdout, "IMAP> %s\n", buf1);
+ }
+ SockWrite (sock, buf1, strlen (buf1));
+ SockWrite (sock, "\r\n", 2);
+
+ if (result = gen_recv (sock, buf1, sizeof (buf1)))
+ return result;
+
+ if (strstr (buf1, "OK")) {
+ return PS_SUCCESS;
+ } else {
+ return PS_AUTHFAIL;
+ }
+}
+
int imap_canonicalize(char *result, char *passwd)
/* encode an IMAP password as per RFC1730's quoting conventions */
{
@@ -695,6 +818,19 @@ int imap_getauth(int sock, struct query *ctl, char *greeting)
}
#endif /* KERBEROS_V4 */
+ if (strstr (capabilities, "AUTH=CRAM-MD5"))
+ {
+ if (outlevel >= O_DEBUG)
+ report (stdout, _("CRAM-MD5 authentication is supported\n"));
+ if ((ok = do_cram_md5 (sock, ctl)))
+ {
+ if (outlevel >= O_MONITOR)
+ report (stdout, "IMAP> *\n");
+ SockWrite (sock, "*\r\n", 3);
+ }
+ return ok;
+ }
+
#ifdef __UNUSED__ /* The Cyrus IMAP4rev1 server chokes on this */
/* this handles either AUTH=LOGIN or AUTH-LOGIN */
if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN"))) {