| 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 */ |