NeoMutt  2021-10-29-43-g6b8931
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 "adata.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 39 of file auth_cram.c.

◆ MD5_DIGEST_LEN

#define MD5_DIGEST_LEN   16

Definition at line 40 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 48 of file auth_cram.c.

49 {
50  struct Md5Ctx md5ctx;
51  unsigned char ipad[MD5_BLOCK_LEN] = { 0 };
52  unsigned char opad[MD5_BLOCK_LEN] = { 0 };
53  unsigned char secret[MD5_BLOCK_LEN + 1];
54  size_t secret_len;
55 
56  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  mutt_str_copy((char *) secret, password, sizeof(secret));
69 
70  memcpy(ipad, secret, secret_len);
71  memcpy(opad, secret, secret_len);
72 
73  for (int i = 0; i < MD5_BLOCK_LEN; i++)
74  {
75  ipad[i] ^= 0x36;
76  opad[i] ^= 0x5c;
77  }
78 
79  /* inner hash: challenge and ipadded secret */
80  mutt_md5_init_ctx(&md5ctx);
81  mutt_md5_process_bytes(ipad, MD5_BLOCK_LEN, &md5ctx);
82  mutt_md5_process(challenge, &md5ctx);
83  mutt_md5_finish_ctx(&md5ctx, response);
84 
85  /* outer hash: inner hash and opadded secret */
86  mutt_md5_init_ctx(&md5ctx);
87  mutt_md5_process_bytes(opad, MD5_BLOCK_LEN, &md5ctx);
88  mutt_md5_process_bytes(response, MD5_DIGEST_LEN, &md5ctx);
89  mutt_md5_finish_ctx(&md5ctx, response);
90 }
#define MD5_BLOCK_LEN
Definition: auth_cram.c:39
#define MD5_DIGEST_LEN
Definition: auth_cram.c:40
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_process(const char *str, struct Md5Ctx *md5ctx)
Process a NULL-terminated string.
Definition: md5.c:355
void * mutt_md5_finish_ctx(struct Md5Ctx *md5ctx, void *resbuf)
Process the remaining bytes in the buffer.
Definition: md5.c:286
void mutt_md5_init_ctx(struct Md5Ctx *md5ctx)
Initialise the MD5 computation.
Definition: md5.c:262
void * mutt_md5_bytes(const void *buffer, size_t len, void *resbuf)
Calculate the MD5 hash of a buffer.
Definition: md5.c:336
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:560
Cursor for the MD5 hashing.
Definition: md5.h:37
+ Here is the call 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()

Definition at line 48 of file auth_cram.c.

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