aboutsummaryrefslogtreecommitdiffstats
path: root/cram.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2001-02-11 04:57:31 +0000
committerEric S. Raymond <esr@thyrsus.com>2001-02-11 04:57:31 +0000
commitc7132be603c07e7851a3a374dc9b6be59ce3c919 (patch)
tree4fb26306e4e3af444180483e3d1d2a08abe76d1b /cram.c
parent4e1db9d3b89d27acf1b80c51c02e73cc7ad69bc1 (diff)
downloadfetchmail-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.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/cram.c b/cram.c
new file mode 100644
index 00000000..ae8800d1
--- /dev/null
+++ b/cram.c
@@ -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 */