NeoMutt  2020-06-26-250-g349c94
Teaching an old dog new tricks
DOXYGEN
auth_cram.c File Reference

IMAP CRAM-MD5 authentication method. More...

#include "config.h"
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "conn/lib.h"
#include "auth.h"
#include "mutt_socket.h"
+ Include dependency graph for auth_cram.c:

Go to the source code of this file.

Macros

#define MD5_BLOCK_LEN   64
 
#define MD5_DIGEST_LEN   16
 

Functions

static void hmac_md5 (const char *password, char *challenge, unsigned char *response)
 produce CRAM-MD5 challenge response More...
 
enum ImapAuthRes imap_auth_cram_md5 (struct ImapAccountData *adata, const char *method)
 Authenticate using CRAM-MD5 - Implements ImapAuth::authenticate() More...
 

Detailed Description

IMAP CRAM-MD5 authentication method.

Authors
  • Brendan Cully

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file auth_cram.c.

Macro Definition Documentation

◆ MD5_BLOCK_LEN

#define MD5_BLOCK_LEN   64

Definition at line 38 of file auth_cram.c.

◆ MD5_DIGEST_LEN

#define MD5_DIGEST_LEN   16

Definition at line 39 of file auth_cram.c.

Function Documentation

◆ hmac_md5()

static void hmac_md5 ( const char *  password,
char *  challenge,
unsigned char *  response 
)
static

produce CRAM-MD5 challenge response

Parameters
[in]passwordPassword to encrypt
[in]challengeChallenge from server
[out]responseBuffer for the response

Definition at line 47 of file auth_cram.c.

48 {
49  struct Md5Ctx md5ctx;
50  unsigned char ipad[MD5_BLOCK_LEN] = { 0 };
51  unsigned char opad[MD5_BLOCK_LEN] = { 0 };
52  unsigned char secret[MD5_BLOCK_LEN + 1];
53  size_t secret_len;
54 
55  secret_len = strlen(password);
56 
57  /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5
58  * digests */
59  if (secret_len > MD5_BLOCK_LEN)
60  {
61  unsigned char hash_passwd[MD5_DIGEST_LEN];
62  mutt_md5_bytes(password, secret_len, hash_passwd);
63  mutt_str_copy((char *) secret, (char *) hash_passwd, MD5_DIGEST_LEN);
64  secret_len = MD5_DIGEST_LEN;
65  }
66  else
67  mutt_str_copy((char *) secret, password, sizeof(secret));
68 
69  memcpy(ipad, secret, secret_len);
70  memcpy(opad, secret, secret_len);
71 
72  for (int i = 0; i < MD5_BLOCK_LEN; i++)
73  {
74  ipad[i] ^= 0x36;
75  opad[i] ^= 0x5c;
76  }
77 
78  /* inner hash: challenge and ipadded secret */
79  mutt_md5_init_ctx(&md5ctx);
80  mutt_md5_process_bytes(ipad, MD5_BLOCK_LEN, &md5ctx);
81  mutt_md5_process(challenge, &md5ctx);
82  mutt_md5_finish_ctx(&md5ctx, response);
83 
84  /* outer hash: inner hash and opadded secret */
85  mutt_md5_init_ctx(&md5ctx);
86  mutt_md5_process_bytes(opad, MD5_BLOCK_LEN, &md5ctx);
87  mutt_md5_process_bytes(response, MD5_DIGEST_LEN, &md5ctx);
88  mutt_md5_finish_ctx(&md5ctx, response);
89 }
Cursor for the MD5 hashing.
Definition: md5.h:36
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:262
void * mutt_md5_finish_ctx(struct Md5Ctx *md5ctx, void *resbuf)
Process the remaining bytes in the buffer.
Definition: md5.c:286
#define MD5_DIGEST_LEN
Definition: auth_cram.c:39
void mutt_md5_process_bytes(const void *buf, size_t buflen, struct Md5Ctx *md5ctx)
Process a block of data.
Definition: md5.c:373
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:721
#define MD5_BLOCK_LEN
Definition: auth_cram.c:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_auth_cram_md5()

enum ImapAuthRes imap_auth_cram_md5 ( struct ImapAccountData adata,
const char *  method 
)

Authenticate using CRAM-MD5 - Implements ImapAuth::authenticate()

Parameters
adataImap Account data
methodName of this authentication method
Return values
ImapAuthResResult, e.g. IMAP_AUTH_SUCCESS

Definition at line 97 of file auth_cram.c.

98 {
99  char ibuf[2048], obuf[1024];
100  unsigned char hmac_response[MD5_DIGEST_LEN];
101  int len;
102  int rc;
103 
104  if (!(adata->capabilities & IMAP_CAP_AUTH_CRAM_MD5))
105  return IMAP_AUTH_UNAVAIL;
106 
107  // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
108  mutt_message(_("Authenticating (%s)..."), "CRAM-MD5");
109 
110  /* get auth info */
111  if (mutt_account_getlogin(&adata->conn->account) < 0)
112  return IMAP_AUTH_FAILURE;
113  if (mutt_account_getpass(&adata->conn->account) < 0)
114  return IMAP_AUTH_FAILURE;
115 
116  imap_cmd_start(adata, "AUTHENTICATE CRAM-MD5");
117 
118  /* From RFC2195:
119  * The data encoded in the first ready response contains a presumptively
120  * arbitrary string of random digits, a timestamp, and the fully-qualified
121  * primary host name of the server. The syntax of the unencoded form must
122  * correspond to that of an RFC822 'msg-id' [RFC822] as described in [POP3]. */
123  do
124  {
125  rc = imap_cmd_step(adata);
126  } while (rc == IMAP_RES_CONTINUE);
127 
128  if (rc != IMAP_RES_RESPOND)
129  {
130  mutt_debug(LL_DEBUG1, "Invalid response from server: %s\n", ibuf);
131  goto bail;
132  }
133 
134  len = mutt_b64_decode(adata->buf + 2, obuf, sizeof(obuf));
135  if (len == -1)
136  {
137  mutt_debug(LL_DEBUG1, "Error decoding base64 response\n");
138  goto bail;
139  }
140 
141  obuf[len] = '\0';
142  mutt_debug(LL_DEBUG2, "CRAM challenge: %s\n", obuf);
143 
144  /* The client makes note of the data and then responds with a string
145  * consisting of the user name, a space, and a 'digest'. The latter is
146  * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
147  * key is a shared secret and the digested text is the timestamp (including
148  * angle-brackets).
149  *
150  * Note: The user name shouldn't be quoted. Since the digest can't contain
151  * spaces, there is no ambiguity. Some servers get this wrong, we'll work
152  * around them when the bug report comes in. Until then, we'll remain
153  * blissfully RFC-compliant. */
154  hmac_md5(adata->conn->account.pass, obuf, hmac_response);
155  /* dubious optimisation I saw elsewhere: make the whole string in one call */
156  int off = snprintf(obuf, sizeof(obuf), "%s ", adata->conn->account.user);
157  mutt_md5_toascii(hmac_response, obuf + off);
158  mutt_debug(LL_DEBUG2, "CRAM response: %s\n", obuf);
159 
160  /* ibuf must be long enough to store the base64 encoding of obuf,
161  * plus the additional debris */
162  mutt_b64_encode(obuf, strlen(obuf), ibuf, sizeof(ibuf) - 2);
163  mutt_str_cat(ibuf, sizeof(ibuf), "\r\n");
164  mutt_socket_send(adata->conn, ibuf);
165 
166  do
167  {
168  rc = imap_cmd_step(adata);
169  } while (rc == IMAP_RES_CONTINUE);
170 
171  if (rc != IMAP_RES_OK)
172  {
173  mutt_debug(LL_DEBUG1, "Error receiving server response\n");
174  goto bail;
175  }
176 
177  if (imap_code(adata->buf))
178  return IMAP_AUTH_SUCCESS;
179 
180 bail:
181  // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
182  mutt_error(_("%s authentication failed"), "CRAM-MD5");
183  return IMAP_AUTH_FAILURE;
184 }
#define IMAP_RES_RESPOND
+
Definition: private.h:57
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:55
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1076
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
#define mutt_message(...)
Definition: logging.h:83
static void hmac_md5(const char *password, char *challenge, unsigned char *response)
produce CRAM-MD5 challenge response
Definition: auth_cram.c:47
char user[128]
Username.
Definition: connaccount.h:55
#define _(a)
Definition: message.h:28
Authentication method not permitted.
Definition: auth.h:40
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1062
Log at debug level 2.
Definition: logging.h:41
void mutt_md5_toascii(const void *digest, char *resbuf)
Convert a binary MD5 digest into ASCII Hexadecimal.
Definition: md5.c:456
char pass[256]
Password.
Definition: connaccount.h:56
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:88
Authentication failed.
Definition: auth.h:39
#define MD5_DIGEST_LEN
Definition: auth_cram.c:39
ImapCapFlags capabilities
Definition: private.h:185
int mutt_account_getlogin(struct ConnAccount *cac)
Retrieve login info into ConnAccount, if necessary.
Definition: connaccount.c:81
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:390
Log at debug level 1.
Definition: logging.h:40
char * buf
Definition: private.h:189
#define mutt_error(...)
Definition: logging.h:84
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert null-terminated base64 string to raw bytes.
Definition: base64.c:136
#define IMAP_CAP_AUTH_CRAM_MD5
RFC2195: CRAM-MD5 authentication.
Definition: private.h:127
Authentication successful.
Definition: auth.h:38
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1199
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:111
struct Connection * conn
Definition: private.h:171
+ Here is the call graph for this function: