NeoMutt  2024-12-12-19-ge4b57e
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
auth_cram.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <stdio.h>
33#include <string.h>
34#include "private.h"
35#include "mutt/lib.h"
36#include "conn/lib.h"
37#include "adata.h"
38#include "auth.h"
39
40#define MD5_BLOCK_LEN 64
41#define MD5_DIGEST_LEN 16
42
49static void hmac_md5(const char *password, const char *challenge, unsigned char *response)
50{
51 struct Md5Ctx md5ctx = { 0 };
52 unsigned char ipad[MD5_BLOCK_LEN] = { 0 };
53 unsigned char opad[MD5_BLOCK_LEN] = { 0 };
54 unsigned char secret[MD5_BLOCK_LEN + 1] = { 0 };
55
56 size_t secret_len = strlen(password);
57
58 /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5
59 * digests */
60 if (secret_len > MD5_BLOCK_LEN)
61 {
62 unsigned char hash_passwd[MD5_DIGEST_LEN];
63 mutt_md5_bytes(password, secret_len, hash_passwd);
64 mutt_str_copy((char *) secret, (char *) hash_passwd, MD5_DIGEST_LEN);
65 secret_len = MD5_DIGEST_LEN;
66 }
67 else
68 {
69 mutt_str_copy((char *) secret, password, sizeof(secret));
70 }
71
72 memcpy(ipad, secret, secret_len);
73 memcpy(opad, secret, secret_len);
74
75 for (int i = 0; i < MD5_BLOCK_LEN; i++)
76 {
77 ipad[i] ^= 0x36;
78 opad[i] ^= 0x5c;
79 }
80
81 /* inner hash: challenge and ipadded secret */
82 mutt_md5_init_ctx(&md5ctx);
84 mutt_md5_process(challenge, &md5ctx);
85 mutt_md5_finish_ctx(&md5ctx, response);
86
87 /* outer hash: inner hash and opadded secret */
88 mutt_md5_init_ctx(&md5ctx);
90 mutt_md5_process_bytes(response, MD5_DIGEST_LEN, &md5ctx);
91 mutt_md5_finish_ctx(&md5ctx, response);
92}
93
97enum ImapAuthRes imap_auth_cram_md5(struct ImapAccountData *adata, const char *method)
98{
100 return IMAP_AUTH_UNAVAIL;
101
102 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
103 mutt_message(_("Authenticating (%s)..."), "CRAM-MD5");
104
105 /* get auth info */
106 if (mutt_account_getlogin(&adata->conn->account) < 0)
107 return IMAP_AUTH_FAILURE;
108 if (mutt_account_getpass(&adata->conn->account) < 0)
109 return IMAP_AUTH_FAILURE;
110
111 imap_cmd_start(adata, "AUTHENTICATE CRAM-MD5");
112
113 struct Buffer *ibuf = buf_pool_get();
114 struct Buffer *obuf = buf_pool_get();
115 unsigned char hmac_response[MD5_DIGEST_LEN];
116 int rc_step;
118
119 /* From RFC2195:
120 * The data encoded in the first ready response contains a presumptively
121 * arbitrary string of random digits, a timestamp, and the fully-qualified
122 * primary host name of the server. The syntax of the unencoded form must
123 * correspond to that of an RFC822 'msg-id' [RFC822] as described in [POP3]. */
124 do
125 {
126 rc_step = imap_cmd_step(adata);
127 } while (rc_step == IMAP_RES_CONTINUE);
128
129 if (rc_step != IMAP_RES_RESPOND)
130 {
131 mutt_debug(LL_DEBUG1, "Invalid response from server\n");
132 goto bail;
133 }
134
135 if (mutt_b64_decode(adata->buf + 2, obuf->data, obuf->dsize) == -1)
136 {
137 mutt_debug(LL_DEBUG1, "Error decoding base64 response\n");
138 goto bail;
139 }
140
141 mutt_debug(LL_DEBUG2, "CRAM challenge: %s\n", buf_string(obuf));
142
143 /* The client makes note of the data and then responds with a string
144 * consisting of the user name, a space, and a 'digest'. The latter is
145 * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
146 * key is a shared secret and the digested text is the timestamp (including
147 * angle-brackets).
148 *
149 * Note: The user name shouldn't be quoted. Since the digest can't contain
150 * spaces, there is no ambiguity. Some servers get this wrong, we'll work
151 * around them when the bug report comes in. Until then, we'll remain
152 * blissfully RFC-compliant. */
153 hmac_md5(adata->conn->account.pass, buf_string(obuf), hmac_response);
154 /* dubious optimisation I saw elsewhere: make the whole string in one call */
155 int off = buf_printf(obuf, "%s ", adata->conn->account.user);
156 mutt_md5_toascii(hmac_response, obuf->data + off);
157 mutt_debug(LL_DEBUG2, "CRAM response: %s\n", buf_string(obuf));
158
159 /* ibuf must be long enough to store the base64 encoding of obuf,
160 * plus the additional debris */
161 mutt_b64_encode(obuf->data, obuf->dsize, ibuf->data, ibuf->dsize - 2);
162 buf_addstr(ibuf, "\r\n");
163 mutt_socket_send(adata->conn, buf_string(ibuf));
164
165 do
166 {
167 rc_step = imap_cmd_step(adata);
168 } while (rc_step == IMAP_RES_CONTINUE);
169
170 if (rc_step != IMAP_RES_OK)
171 {
172 mutt_debug(LL_DEBUG1, "Error receiving server response\n");
173 goto bail;
174 }
175
176 if (imap_code(adata->buf))
178
179bail:
180 if (rc != IMAP_AUTH_SUCCESS)
181 {
182 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
183 mutt_error(_("%s authentication failed"), "CRAM-MD5");
184 }
185
186 buf_pool_release(&ibuf);
187 buf_pool_release(&obuf);
188 return rc;
189}
IMAP authenticator multiplexor.
ImapAuthRes
Results of IMAP Authentication.
Definition: auth.h:39
@ IMAP_AUTH_FAILURE
Authentication failed.
Definition: auth.h:41
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:40
@ IMAP_AUTH_UNAVAIL
Authentication method not permitted.
Definition: auth.h:42
#define MD5_BLOCK_LEN
Definition: auth_cram.c:40
static void hmac_md5(const char *password, const char *challenge, unsigned char *response)
Produce CRAM-MD5 challenge response.
Definition: auth_cram.c:49
#define MD5_DIGEST_LEN
Definition: auth_cram.c:41
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to null-terminated base64 string.
Definition: base64.c:87
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert null-terminated base64 string to raw bytes.
Definition: base64.c:135
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
Connection Library.
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:130
int mutt_account_getlogin(struct ConnAccount *cac)
Retrieve login info into ConnAccount, if necessary.
Definition: connaccount.c:100
enum ImapAuthRes imap_auth_cram_md5(struct ImapAccountData *adata, const char *method)
Authenticate using CRAM-MD5 - Implements ImapAuth::authenticate() -.
Definition: auth_cram.c:97
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1114
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1128
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1255
#define IMAP_CAP_AUTH_CRAM_MD5
RFC2195: CRAM-MD5 authentication.
Definition: private.h:126
#define IMAP_RES_RESPOND
+
Definition: private.h:57
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:55
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void mutt_md5_process_bytes(const void *buf, size_t buflen, struct Md5Ctx *md5ctx)
Process a block of data.
Definition: md5.c:373
void * mutt_md5_bytes(const void *buffer, size_t len, void *resbuf)
Calculate the MD5 hash of a buffer.
Definition: md5.c:336
void mutt_md5_process(const char *str, struct Md5Ctx *md5ctx)
Process a NULL-terminated string.
Definition: md5.c:355
void mutt_md5_init_ctx(struct Md5Ctx *md5ctx)
Initialise the MD5 computation.
Definition: md5.c:261
void * mutt_md5_finish_ctx(struct Md5Ctx *md5ctx, void *resbuf)
Process the remaining bytes in the buffer.
Definition: md5.c:285
void mutt_md5_toascii(const void *digest, char *resbuf)
Convert a binary MD5 digest into ASCII Hexadecimal.
Definition: md5.c:456
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:581
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
Pop-specific Account data.
GUI display the mailboxes in a side panel.
#define mutt_socket_send(conn, buf)
Definition: socket.h:57
String manipulation buffer.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37
char user[128]
Username.
Definition: connaccount.h:56
char pass[256]
Password.
Definition: connaccount.h:57
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
IMAP-specific Account data -.
Definition: adata.h:40
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
char * buf
Definition: adata.h:59
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
Cursor for the MD5 hashing.
Definition: md5.h:37