]> Pileus Git - ~andy/fetchmail/blob - cram.c
Minor bug fixes for socket.c
[~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 #if defined(STDC_HEADERS)
12 #include  <stdlib.h>
13 #endif
14 #include  "fetchmail.h"
15 #include  "socket.h"
16
17 #include  "i18n.h"
18 #include "fm_md5.h"
19
20 void hmac_md5 (const unsigned char *password,  size_t pass_len,
21                const unsigned char *challenge, size_t chal_len,
22                unsigned char *response,  size_t resp_len)
23 {
24     int i;
25     unsigned char ipad[64];
26     unsigned char opad[64];
27     unsigned char hash_passwd[16];
28
29     MD5_CTX ctx;
30     
31     if (resp_len != 16)
32         return;
33
34     if (pass_len > sizeof (ipad))
35     {
36         MD5Init (&ctx);
37         MD5Update (&ctx, password, pass_len);
38         MD5Final (hash_passwd, &ctx);
39         password = hash_passwd; pass_len = sizeof (hash_passwd);
40     }
41
42     memset (ipad, 0, sizeof (ipad));
43     memset (opad, 0, sizeof (opad));
44     memcpy (ipad, password, pass_len);
45     memcpy (opad, password, pass_len);
46
47     for (i=0; i<64; i++) {
48         ipad[i] ^= 0x36;
49         opad[i] ^= 0x5c;
50     }
51
52     MD5Init (&ctx);
53     MD5Update (&ctx, ipad, sizeof (ipad));
54     MD5Update (&ctx, challenge, chal_len);
55     MD5Final (response, &ctx);
56
57     MD5Init (&ctx);
58     MD5Update (&ctx, opad, sizeof (opad));
59     MD5Update (&ctx, response, resp_len);
60     MD5Final (response, &ctx);
61 }
62
63 int do_cram_md5 (int sock, const char *command, struct query *ctl, const char *strip)
64 /* authenticate as per RFC2195 */
65 {
66     int result;
67     int len;
68     char buf1[1024];
69     char msg_id[768];
70     unsigned char response[16];
71     char reply[1024];
72     char *respdata;
73
74     gen_send (sock, "%s CRAM-MD5", command);
75
76     /* From RFC2195:
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].
82      */
83
84     if ((result = gen_recv (sock, buf1, sizeof (buf1)))) {
85         return result;
86     }
87
88     /* caller may specify a response prefix we should strip if present */
89     respdata = buf1;
90     if (strip && strncmp(buf1, strip, strlen(strip)) == 0)
91         respdata += strlen(strip);
92     len = from64tobits (msg_id, respdata, sizeof(msg_id));
93
94     if (len < 0) {
95         report (stderr, GT_("could not decode BASE64 challenge\n"));
96         return PS_AUTHFAIL;
97     } else if ((size_t)len < sizeof (msg_id)) {
98         msg_id[len] = 0;
99     } else {
100         msg_id[sizeof (msg_id)-1] = 0;
101     }
102     if (outlevel >= O_DEBUG) {
103         report (stdout, GT_("decoded as %s\n"), msg_id);
104     }
105
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).
111      */
112
113     hmac_md5((unsigned char *)ctl->password, strlen(ctl->password),
114               (unsigned char *)msg_id, strlen (msg_id),
115               response, sizeof (response));
116
117     snprintf (reply, sizeof(reply),
118               "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 
119               ctl->remotename,
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]);
124
125     to64frombits (buf1, reply, strlen(reply));
126
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, "%s", buf1);
131     suppress_tags = FALSE;
132     if (result)
133         return(result);
134     else
135         return(PS_SUCCESS);
136 }
137
138 /* cram.c ends here */