2 * cram.c -- CRAM-MD5 authentication (see RFC 2195)
4 * For license terms, see the file COPYING in this directory.
12 #include "fetchmail.h"
18 void hmac_md5 (const unsigned char *password, size_t pass_len,
19 const unsigned char *challenge, size_t chal_len,
20 unsigned char *response, size_t resp_len)
23 unsigned char ipad[64];
24 unsigned char opad[64];
25 unsigned char hash_passwd[16];
32 if (pass_len > sizeof (ipad))
35 MD5Update (&ctx, password, pass_len);
36 MD5Final (hash_passwd, &ctx);
37 password = hash_passwd; pass_len = sizeof (hash_passwd);
40 memset (ipad, 0, sizeof (ipad));
41 memset (opad, 0, sizeof (opad));
42 memcpy (ipad, password, pass_len);
43 memcpy (opad, password, pass_len);
45 for (i=0; i<64; i++) {
51 MD5Update (&ctx, ipad, sizeof (ipad));
52 MD5Update (&ctx, challenge, chal_len);
53 MD5Final (response, &ctx);
56 MD5Update (&ctx, opad, sizeof (opad));
57 MD5Update (&ctx, response, resp_len);
58 MD5Final (response, &ctx);
61 int do_cram_md5 (int sock, const char *command, struct query *ctl, const char *strip)
62 /* authenticate as per RFC2195 */
68 unsigned char response[16];
72 gen_send (sock, "%s CRAM-MD5", command);
75 * The data encoded in the first ready response contains an
76 * presumptively arbitrary string of random digits, a timestamp, and the
77 * fully-qualified primary host name of the server. The syntax of the
78 * unencoded form must correspond to that of an RFC 822 'msg-id'
79 * [RFC822] as described in [POP3].
82 if ((result = gen_recv (sock, buf1, sizeof (buf1)))) {
86 /* caller may specify a response prefix we should strip if present */
88 if (strip && strncmp(buf1, strip, strlen(strip)) == 0)
89 respdata += strlen(strip);
90 len = from64tobits (msg_id, respdata, sizeof(msg_id));
93 report (stderr, GT_("could not decode BASE64 challenge\n"));
95 } else if ((size_t)len < sizeof (msg_id)) {
98 msg_id[sizeof (msg_id)-1] = 0;
100 if (outlevel >= O_DEBUG) {
101 report (stdout, GT_("decoded as %s\n"), msg_id);
104 /* The client makes note of the data and then responds with a string
105 * consisting of the user name, a space, and a 'digest'. The latter is
106 * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where
107 * the key is a shared secret and the digested text is the timestamp
108 * (including angle-brackets).
111 hmac_md5((unsigned char *)ctl->password, strlen(ctl->password),
112 (unsigned char *)msg_id, strlen (msg_id),
113 response, sizeof (response));
115 snprintf (reply, sizeof(reply),
116 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
118 response[0], response[1], response[2], response[3],
119 response[4], response[5], response[6], response[7],
120 response[8], response[9], response[10], response[11],
121 response[12], response[13], response[14], response[15]);
123 to64frombits (buf1, reply, strlen(reply));
125 /* ship the authentication back, accept the server's responses */
126 /* PMDF5.2 IMAP has a bug that requires this to be a single write */
127 suppress_tags = TRUE;
128 result = gen_transact(sock, "%s", buf1);
129 suppress_tags = FALSE;
136 /* cram.c ends here */