2 * cram.c -- CRAM-MD5 authentication (see RFC 2195)
4 * For license terms, see the file COPYING in this directory.
11 #if defined(STDC_HEADERS)
14 #include "fetchmail.h"
20 void hmac_md5 (char *password, size_t pass_len,
21 char *challenge, size_t chal_len,
22 unsigned char *response, size_t resp_len)
25 unsigned char ipad[64];
26 unsigned char opad[64];
34 if (pass_len > sizeof (ipad))
37 MD5Update (&ctx, password, pass_len);
38 MD5Final (hash_passwd, &ctx);
39 password = hash_passwd; pass_len = sizeof (hash_passwd);
42 memset (ipad, 0, sizeof (ipad));
43 memset (opad, 0, sizeof (opad));
44 memcpy (ipad, password, pass_len);
45 memcpy (opad, password, pass_len);
47 for (i=0; i<64; i++) {
53 MD5Update (&ctx, ipad, sizeof (ipad));
54 MD5Update (&ctx, challenge, chal_len);
55 MD5Final (response, &ctx);
58 MD5Update (&ctx, opad, sizeof (opad));
59 MD5Update (&ctx, response, resp_len);
60 MD5Final (response, &ctx);
63 int do_cram_md5 (int sock, char *command, struct query *ctl, char *strip)
64 /* authenticate as per RFC2195 */
70 unsigned char response[16];
74 gen_send (sock, "%s CRAM-MD5", command);
77 * The data encoded in the first ready response contains an
78 * presumptively arbitrary string of random digits, a timestamp, and the
79 * fully-qualified primary host name of the server. The syntax of the
80 * unencoded form must correspond to that of an RFC 822 'msg-id'
81 * [RFC822] as described in [POP3].
84 if ((result = gen_recv (sock, buf1, sizeof (buf1)))) {
88 /* caller may specify a response prefix we should strip if present */
90 if (strip && strncmp(buf1, strip, strlen(strip)) == 0)
91 respdata += strlen(strip);
92 len = from64tobits (msg_id, respdata, sizeof(msg_id));
95 report (stderr, GT_("could not decode BASE64 challenge\n"));
97 } else if ((size_t)len < sizeof (msg_id)) {
100 msg_id[sizeof (msg_id)-1] = 0;
102 if (outlevel >= O_DEBUG) {
103 report (stdout, GT_("decoded as %s\n"), msg_id);
106 /* The client makes note of the data and then responds with a string
107 * consisting of the user name, a space, and a 'digest'. The latter is
108 * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where
109 * the key is a shared secret and the digested text is the timestamp
110 * (including angle-brackets).
113 hmac_md5(ctl->password, strlen(ctl->password),
114 msg_id, strlen (msg_id),
115 response, sizeof (response));
117 snprintf (reply, sizeof(reply),
118 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
120 response[0], response[1], response[2], response[3],
121 response[4], response[5], response[6], response[7],
122 response[8], response[9], response[10], response[11],
123 response[12], response[13], response[14], response[15]);
125 to64frombits (buf1, reply, strlen(reply));
127 /* ship the authentication back, accept the server's responses */
128 /* PMDF5.2 IMAP has a bug that requires this to be a single write */
129 suppress_tags = TRUE;
130 result = gen_transact(sock, buf1, sizeof(buf1));
131 suppress_tags = FALSE;
138 /* cram.c ends here */