mutt stable branch with some hacks
at master 180 lines 5.9 kB view raw
1/* 2 * Copyright (C) 1999-2001,2005 Brendan Cully <brendan@kublai.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19/* IMAP login/authentication code */ 20 21#if HAVE_CONFIG_H 22# include "config.h" 23#endif 24 25#include "mutt.h" 26#include "imap_private.h" 27#include "auth.h" 28#include "md5.h" 29 30#define MD5_BLOCK_LEN 64 31#define MD5_DIGEST_LEN 16 32 33/* forward declarations */ 34static void hmac_md5 (const char* password, char* challenge, 35 unsigned char* response); 36 37/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. */ 38imap_auth_res_t imap_auth_cram_md5 (IMAP_DATA* idata, const char* method) 39{ 40 char ibuf[LONG_STRING*2], obuf[LONG_STRING]; 41 unsigned char hmac_response[MD5_DIGEST_LEN]; 42 int len; 43 int rc; 44 45 if (!mutt_bit_isset (idata->capabilities, ACRAM_MD5)) 46 return IMAP_AUTH_UNAVAIL; 47 48 mutt_message _("Authenticating (CRAM-MD5)..."); 49 50 /* get auth info */ 51 if (mutt_account_getlogin (&idata->conn->account)) 52 return IMAP_AUTH_FAILURE; 53 if (mutt_account_getpass (&idata->conn->account)) 54 return IMAP_AUTH_FAILURE; 55 56 imap_cmd_start (idata, "AUTHENTICATE CRAM-MD5"); 57 58 /* From RFC 2195: 59 * The data encoded in the first ready response contains a presumptively 60 * arbitrary string of random digits, a timestamp, and the fully-qualified 61 * primary host name of the server. The syntax of the unencoded form must 62 * correspond to that of an RFC 822 'msg-id' [RFC822] as described in [POP3]. 63 */ 64 do 65 rc = imap_cmd_step (idata); 66 while (rc == IMAP_CMD_CONTINUE); 67 68 if (rc != IMAP_CMD_RESPOND) 69 { 70 dprint (1, (debugfile, "Invalid response from server: %s\n", ibuf)); 71 goto bail; 72 } 73 74 if ((len = mutt_from_base64 (obuf, idata->buf + 2)) == -1) 75 { 76 dprint (1, (debugfile, "Error decoding base64 response.\n")); 77 goto bail; 78 } 79 80 obuf[len] = '\0'; 81 dprint (2, (debugfile, "CRAM challenge: %s\n", obuf)); 82 83 /* The client makes note of the data and then responds with a string 84 * consisting of the user name, a space, and a 'digest'. The latter is 85 * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the 86 * key is a shared secret and the digested text is the timestamp (including 87 * angle-brackets). 88 * 89 * Note: The user name shouldn't be quoted. Since the digest can't contain 90 * spaces, there is no ambiguity. Some servers get this wrong, we'll work 91 * around them when the bug report comes in. Until then, we'll remain 92 * blissfully RFC-compliant. 93 */ 94 hmac_md5 (idata->conn->account.pass, obuf, hmac_response); 95 /* dubious optimisation I saw elsewhere: make the whole string in one call */ 96 snprintf (obuf, sizeof (obuf), 97 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 98 idata->conn->account.user, 99 hmac_response[0], hmac_response[1], hmac_response[2], hmac_response[3], 100 hmac_response[4], hmac_response[5], hmac_response[6], hmac_response[7], 101 hmac_response[8], hmac_response[9], hmac_response[10], hmac_response[11], 102 hmac_response[12], hmac_response[13], hmac_response[14], hmac_response[15]); 103 dprint(2, (debugfile, "CRAM response: %s\n", obuf)); 104 105 /* XXX - ibuf must be long enough to store the base64 encoding of obuf, 106 * plus the additional debris 107 */ 108 109 mutt_to_base64 ((unsigned char*) ibuf, (unsigned char*) obuf, strlen (obuf), 110 sizeof (ibuf) - 2); 111 safe_strcat (ibuf, sizeof (ibuf), "\r\n"); 112 mutt_socket_write (idata->conn, ibuf); 113 114 do 115 rc = imap_cmd_step (idata); 116 while (rc == IMAP_CMD_CONTINUE); 117 118 if (rc != IMAP_CMD_OK) 119 { 120 dprint (1, (debugfile, "Error receiving server response.\n")); 121 goto bail; 122 } 123 124 if (imap_code (idata->buf)) 125 return IMAP_AUTH_SUCCESS; 126 127 bail: 128 mutt_error _("CRAM-MD5 authentication failed."); 129 mutt_sleep (2); 130 return IMAP_AUTH_FAILURE; 131} 132 133/* hmac_md5: produce CRAM-MD5 challenge response. */ 134static void hmac_md5 (const char* password, char* challenge, 135 unsigned char* response) 136{ 137 struct md5_ctx ctx; 138 unsigned char ipad[MD5_BLOCK_LEN], opad[MD5_BLOCK_LEN]; 139 unsigned char secret[MD5_BLOCK_LEN+1]; 140 unsigned char hash_passwd[MD5_DIGEST_LEN]; 141 unsigned int secret_len, chal_len; 142 int i; 143 144 secret_len = strlen (password); 145 chal_len = strlen (challenge); 146 147 /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5 148 * digests */ 149 if (secret_len > MD5_BLOCK_LEN) 150 { 151 md5_buffer (password, secret_len, hash_passwd); 152 strfcpy ((char*) secret, (char*) hash_passwd, MD5_DIGEST_LEN); 153 secret_len = MD5_DIGEST_LEN; 154 } 155 else 156 strfcpy ((char *) secret, password, sizeof (secret)); 157 158 memset (ipad, 0, sizeof (ipad)); 159 memset (opad, 0, sizeof (opad)); 160 memcpy (ipad, secret, secret_len); 161 memcpy (opad, secret, secret_len); 162 163 for (i = 0; i < MD5_BLOCK_LEN; i++) 164 { 165 ipad[i] ^= 0x36; 166 opad[i] ^= 0x5c; 167 } 168 169 /* inner hash: challenge and ipadded secret */ 170 md5_init_ctx (&ctx); 171 md5_process_bytes (ipad, MD5_BLOCK_LEN, &ctx); 172 md5_process_bytes (challenge, chal_len, &ctx); 173 md5_finish_ctx (&ctx, response); 174 175 /* outer hash: inner hash and opadded secret */ 176 md5_init_ctx (&ctx); 177 md5_process_bytes (opad, MD5_BLOCK_LEN, &ctx); 178 md5_process_bytes (response, MD5_DIGEST_LEN, &ctx); 179 md5_finish_ctx (&ctx, response); 180}