diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2001-02-11 04:57:31 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2001-02-11 04:57:31 +0000 |
commit | c7132be603c07e7851a3a374dc9b6be59ce3c919 (patch) | |
tree | 4fb26306e4e3af444180483e3d1d2a08abe76d1b /cram.c | |
parent | 4e1db9d3b89d27acf1b80c51c02e73cc7ad69bc1 (diff) | |
download | fetchmail-c7132be603c07e7851a3a374dc9b6be59ce3c919.tar.gz fetchmail-c7132be603c07e7851a3a374dc9b6be59ce3c919.tar.bz2 fetchmail-c7132be603c07e7851a3a374dc9b6be59ce3c919.zip |
Initial revision
svn path=/trunk/; revision=3041
Diffstat (limited to 'cram.c')
-rw-r--r-- | cram.c | 150 |
1 files changed, 150 insertions, 0 deletions
@@ -0,0 +1,150 @@ +/* + * cram.c -- CRAM-MD5 authentication (see RFC 2195) + * + * For license terms, see the file COPYING in this directory. + */ + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#if defined(STDC_HEADERS) +#include <stdlib.h> +#endif +#include "fetchmail.h" +#include "socket.h" + +#include "i18n.h" +#include "md5.h" + +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); +} + +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)); + +#ifdef HAVE_SNPRINTF + snprintf (reply, sizeof (reply), +#else + sprintf(reply, +#endif + "%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, "CRAM> %s\n", buf1); + } + + /* PMDF5.2 IMAP has a bug that requires this to be a single write */ + strcat (buf1, "\r\n"); + SockWrite (sock, buf1, strlen (buf1)); + + if ((result = gen_recv (sock, buf1, sizeof (buf1)))) + return result; + + /* + * Ugh. This has to recognize all kinds of success lines. + * These two tests will catch IMAP and POP OK or SMTP-like + * protocols that use 2xx as a success indication. + */ + if (buf1[0] = '2' || strstr(buf1, "OK")) + return(PS_SUCCESS); + else + return(PS_AUTHFAIL); +} + +/* cram.c ends here */ |