]> Pileus Git - ~andy/fetchmail/blob - cram.c
Fix typo repsonsible -> responsible.
[~andy/fetchmail] / cram.c
1 /*
2  * cram.c -- CRAM-MD5 authentication (see RFC 2195)
3  *
4  * For license terms, see the file COPYING in this directory.
5  */
6
7 #include  "config.h"
8 #include  <stdio.h>
9 #include  <string.h>
10 #include  <ctype.h>
11 #include  <stdlib.h>
12 #include  "fetchmail.h"
13 #include  "socket.h"
14
15 #include  "gettext.h"
16 #include "fm_md5.h"
17
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)
21 {
22     int i;
23     unsigned char ipad[64];
24     unsigned char opad[64];
25     unsigned char hash_passwd[16];
26
27     MD5_CTX ctx;
28     
29     if (resp_len != 16)
30         return;
31
32     if (pass_len > sizeof (ipad))
33     {
34         MD5Init (&ctx);
35         MD5Update (&ctx, password, pass_len);
36         MD5Final (hash_passwd, &ctx);
37         password = hash_passwd; pass_len = sizeof (hash_passwd);
38     }
39
40     memset (ipad, 0, sizeof (ipad));
41     memset (opad, 0, sizeof (opad));
42     memcpy (ipad, password, pass_len);
43     memcpy (opad, password, pass_len);
44
45     for (i=0; i<64; i++) {
46         ipad[i] ^= 0x36;
47         opad[i] ^= 0x5c;
48     }
49
50     MD5Init (&ctx);
51     MD5Update (&ctx, ipad, sizeof (ipad));
52     MD5Update (&ctx, challenge, chal_len);
53     MD5Final (response, &ctx);
54
55     MD5Init (&ctx);
56     MD5Update (&ctx, opad, sizeof (opad));
57     MD5Update (&ctx, response, resp_len);
58     MD5Final (response, &ctx);
59 }
60
61 int do_cram_md5 (int sock, const char *command, struct query *ctl, const char *strip)
62 /* authenticate as per RFC2195 */
63 {
64     int result;
65     int len;
66     char buf1[1024];
67     char msg_id[768];
68     unsigned char response[16];
69     char reply[1024];
70     char *respdata;
71
72     gen_send (sock, "%s CRAM-MD5", command);
73
74     /* From RFC2195:
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].
80      */
81
82     if ((result = gen_recv (sock, buf1, sizeof (buf1)))) {
83         return result;
84     }
85
86     /* caller may specify a response prefix we should strip if present */
87     respdata = buf1;
88     if (strip && strncmp(buf1, strip, strlen(strip)) == 0)
89         respdata += strlen(strip);
90     len = from64tobits (msg_id, respdata, sizeof(msg_id));
91
92     if (len < 0) {
93         report (stderr, GT_("could not decode BASE64 challenge\n"));
94         return PS_AUTHFAIL;
95     } else if ((size_t)len < sizeof (msg_id)) {
96         msg_id[len] = 0;
97     } else {
98         msg_id[sizeof (msg_id)-1] = 0;
99     }
100     if (outlevel >= O_DEBUG) {
101         report (stdout, GT_("decoded as %s\n"), msg_id);
102     }
103
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).
109      */
110
111     hmac_md5((unsigned char *)ctl->password, strlen(ctl->password),
112               (unsigned char *)msg_id, strlen (msg_id),
113               response, sizeof (response));
114
115     snprintf (reply, sizeof(reply),
116               "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 
117               ctl->remotename,
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]);
122
123     to64frombits (buf1, reply, strlen(reply));
124
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;
130     if (result)
131         return(result);
132     else
133         return(PS_SUCCESS);
134 }
135
136 /* cram.c ends here */