1 /* $NetBSD: mech_digestmd5.c,v 1.13 2018/01/30 15:28:39 shm Exp $ */
2
3 /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Mateusz Kocielski.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the NetBSD
20 * Foundation, Inc. and its contributors.
21 * 4. Neither the name of The NetBSD Foundation nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37 #include <sys/cdefs.h>
38 __RCSID("$NetBSD: mech_digestmd5.c,v 1.13 2018/01/30 15:28:39 shm Exp $");
39
40 #include <sys/param.h>
41
42 #include <assert.h>
43 #include <ctype.h>
44 #include <md5.h>
45 #include <saslc.h>
46 #include <stdbool.h>
47 #include <stdio.h>
48 #include <string.h>
49
50 #include <openssl/evp.h>
51
52 #include "buffer.h"
53 #include "crypto.h"
54 #include "error.h"
55 #include "list.h"
56 #include "mech.h"
57 #include "msg.h"
58 #include "saslc_private.h"
59
60 /* See RFC 2831. */
61
62 /*
63 * TODO:
64 *
65 * 1) Add support for Subsequent Authentication (see RFC 2831 section 2.2).
66 */
67
68 /* properties */
69 #define SASLC_DIGESTMD5_AUTHCID SASLC_PROP_AUTHCID
70 #define SASLC_DIGESTMD5_AUTHZID SASLC_PROP_AUTHZID
71 #define SASLC_DIGESTMD5_CIPHERMASK SASLC_PROP_CIPHERMASK
72 #define SASLC_DIGESTMD5_HOSTNAME SASLC_PROP_HOSTNAME
73 #define SASLC_DIGESTMD5_MAXBUF SASLC_PROP_MAXBUF
74 #define SASLC_DIGESTMD5_PASSWD SASLC_PROP_PASSWD
75 #define SASLC_DIGESTMD5_QOPMASK SASLC_PROP_QOPMASK
76 #define SASLC_DIGESTMD5_REALM SASLC_PROP_REALM
77 #define SASLC_DIGESTMD5_SERVICE SASLC_PROP_SERVICE
78 #define SASLC_DIGESTMD5_SERVNAME SASLC_PROP_SERVNAME
79 /*
80 * XXX: define this if you want to be able to set a fixed cnonce for
81 * debugging purposes.
82 */
83 #define SASLC_DIGESTMD5_CNONCE "CNONCE"
84 /*
85 * XXX: define this if you want to test the saslc_sess_encode() and
86 * saslc_sess_decode() routines against themselves, i.e., have them
87 * use the same key.
88 */
89 #define SASLC_DIGESTMD5_SELFTEST "SELFTEST"
90
91 #define DEFAULT_QOP_MASK (F_QOP_NONE | F_QOP_INT | F_QOP_CONF)
92 #define DEFAULT_CIPHER_MASK (F_CIPHER_DES | F_CIPHER_3DES | \
93 F_CIPHER_RC4 | F_CIPHER_RC4_40 | \
94 F_CIPHER_RC4_56 | F_CIPHER_AES)
95
96 #define DEFAULT_MAXBUF 0x10000
97 #define MAX_MAXBUF 0xffffff
98 #define INVALID_MAXBUF(m) ((m) <= sizeof(md5hash_t) && (m) > MAX_MAXBUF)
99
100 #define NONCE_LEN 33 /* Minimum recommended length is 64bits (rfc2831).
101 cyrus-sasl uses 33 bytes. */
102
103 typedef enum {
104 CHALLENGE_IGNORE = -1, /* must be -1 */
105 CHALLENGE_REALM = 0,
106 CHALLENGE_NONCE = 1,
107 CHALLENGE_QOP = 2,
108 CHALLENGE_STALE = 3,
109 CHALLENGE_MAXBUF = 4,
110 CHALLENGE_CHARSET = 5,
111 CHALLENGE_ALGORITHM = 6,
112 CHALLENGE_CIPHER = 7
113 } challenge_t;
114
115 typedef enum {
116 /*
117 * NB: Values used to index cipher_tbl[] and cipher_ctx_tbl[]
118 * in cipher_context_create().
119 */
120 CIPHER_DES = 0,
121 CIPHER_3DES = 1,
122 CIPHER_RC4 = 2,
123 CIPHER_RC4_40 = 3,
124 CIPHER_RC4_56 = 4,
125 CIPHER_AES = 5
126 } cipher_t;
127
128 #define F_CIPHER_DES (1 << CIPHER_DES)
129 #define F_CIPHER_3DES (1 << CIPHER_3DES)
130 #define F_CIPHER_RC4 (1 << CIPHER_RC4)
131 #define F_CIPHER_RC4_40 (1 << CIPHER_RC4_40)
132 #define F_CIPHER_RC4_56 (1 << CIPHER_RC4_56)
133 #define F_CIPHER_AES (1 << CIPHER_AES)
134
135 static const named_flag_t cipher_tbl[] = {
136 /* NB: to be indexed by cipher_t values */
137 { "des", F_CIPHER_DES },
138 { "3des", F_CIPHER_3DES },
139 { "rc4", F_CIPHER_RC4 },
140 { "rc4-40", F_CIPHER_RC4_40 },
141 { "rc4-56", F_CIPHER_RC4_56 },
142 { "aes", F_CIPHER_AES },
143 { NULL, 0 }
144 };
145
146 static inline const char *
cipher_name(cipher_t cipher)147 cipher_name(cipher_t cipher)
148 {
149
150 assert(cipher < __arraycount(cipher_tbl) - 1); /* NULL terminated */
151 if (cipher < __arraycount(cipher_tbl) - 1)
152 return cipher_tbl[cipher].name;
153 return NULL;
154 }
155
156 static inline unsigned int
cipher_list_flags(list_t * list)157 cipher_list_flags(list_t *list)
158 {
159
160 return saslc__list_flags(list, cipher_tbl);
161 }
162
163 typedef struct { /* data parsed from challenge */
164 bool utf8;
165 bool algorithm;
166 bool stale;
167 char * nonce;
168 list_t * realm;
169 uint32_t cipher_flags;
170 uint32_t qop_flags;
171 size_t maxbuf;
172 } cdata_t;
173
174 typedef struct { /* response data */
175 /* NB: the qop is in saslc__mech_sess_t */
176 char *authcid;
177 char *authzid;
178 char *cnonce;
179 char *digesturi;
180 char *passwd;
181 char *realm;
182 cipher_t cipher;
183 int nonce_cnt;
184 size_t maxbuf;
185 } rdata_t;
186
187 typedef uint8_t md5hash_t[MD5_DIGEST_LENGTH];
188
189 typedef struct {
190 md5hash_t kic; /* client->server integrity key */
191 md5hash_t kis; /* server->client integrity key */
192 md5hash_t kcc; /* client->server confidentiality key */
193 md5hash_t kcs; /* server->client confidentiality key */
194 } keys_t;
195
196 typedef struct cipher_context_t {
197 size_t blksize; /* block size for cipher */
198 EVP_CIPHER_CTX *evp_ctx; /* openssl EVP context */
199 } cipher_context_t;
200
201 typedef struct coder_context_t {
202 uint8_t *key; /* key for coding */
203 uint32_t seqnum; /* 4 byte sequence number */
204
205 void *buf_ctx; /* buffer context */
206 cipher_context_t *cph_ctx; /* cipher context */
207 saslc_sess_t *sess; /* session: for error setting */
208 } coder_context_t;
209
210 /* mech state */
211 typedef struct {
212 saslc__mech_sess_t mech_sess; /* must be first */
213 cdata_t cdata; /* data parsed from challenge string */
214 rdata_t rdata; /* data used for response string */
215 keys_t keys; /* keys */
216 coder_context_t dec_ctx; /* decode context */
217 coder_context_t enc_ctx; /* encode context */
218 } saslc__mech_digestmd5_sess_t;
219
220 /**
221 * @brief if possible convert a UTF-8 string to a ISO8859-1 string.
222 * @param utf8 original UTF-8 string.
223 * @param iso8859 pointer to pointer to the malloced ISO8859-1 string.
224 * @return -1 if the string cannot be translated.
225 *
226 * NOTE: this allocates memory for its output and the caller is
227 * responsible for freeing it.
228 */
229 static int
utf8_to_8859_1(char * utf8,char ** iso8859)230 utf8_to_8859_1(char *utf8, char **iso8859)
231 {
232 unsigned char *s, *d, *end, *src;
233 size_t cnt;
234
235 src = (unsigned char *)utf8;
236 cnt = 0;
237 end = src + strlen(utf8);
238 for (s = src; s < end; ++s) {
239 if (*s > 0xC3) /* abort if outside 8859-1 range */
240 return -1;
241 /*
242 * Look for valid 2 byte UTF-8 encoding with, 8 bits
243 * of info. Quit if invalid pair found.
244 */
245 if (*s >= 0xC0 && *s <= 0xC3) { /* 2 bytes, 8 bits */
246 if (++s == end || *s < 0x80 || *s > 0xBF)
247 return -1; /* broken utf-8 encoding */
248 }
249 cnt++;
250 }
251
252 /* Allocate adequate space. */
253 d = malloc(cnt + 1);
254 if (d == NULL)
255 return -1;
256
257 *iso8859 = (char *)d;
258
259 /* convert to 8859-1 */
260 do {
261 for (s = src; s < end && *s < 0xC0; ++s)
262 *d++ = *s;
263 if (s + 1 >= end)
264 break;
265 *d++ = ((s[0] & 0x3) << 6) | (s[1] & 0x3f);
266 src = s + 2;
267 } while (src < end);
268
269 *d = '\0';
270 return 0;
271 }
272
273 /**
274 * @brief unquote a string by removing escapes.
275 * @param str string to unquote.
276 * @return NULL on failure
277 *
278 * NOTE: this allocates memory for its output and the caller is
279 * responsible for freeing it.
280 */
281 static char *
unq(const char * str)282 unq(const char *str)
283 {
284 const char *s;
285 char *unq_str, *d;
286 int escaped;
287
288 unq_str = malloc(strlen(str) + 1);
289 if (unq_str == NULL)
290 return NULL;
291
292 escaped = 0;
293 d = unq_str;
294 for (s = str; *s != '\0'; s++) {
295 switch (*s) {
296 case '\\':
297 if (escaped)
298 *d++ = *s;
299 escaped = !escaped;
300 break;
301 default:
302 *d++ = *s;
303 escaped = 0;
304 }
305 }
306 *d = '\0';
307
308 return unq_str;
309 }
310
311 /**
312 * @brief computing MD5(username:realm:password).
313 * @param ms mechanism session
314 * @param buf buffer for hash
315 * @return 0 on success, -1 on failure
316 */
317 static int
saslc__mech_digestmd5_userhash(saslc__mech_digestmd5_sess_t * ms,uint8_t * buf)318 saslc__mech_digestmd5_userhash(saslc__mech_digestmd5_sess_t *ms, uint8_t *buf)
319 {
320 char *tmp;
321 char *unq_username, *unq_realm;
322 ssize_t len;
323
324 if ((unq_username = unq(ms->rdata.authcid)) == NULL)
325 return -1;
326
327 /********************************************************/
328 /* RFC 2831 section 2.1.2 */
329 /* ... If the directive is missing, "realm-value" will */
330 /* set to the empty string when computing A1. */
331 /********************************************************/
332 if (ms->rdata.realm == NULL)
333 unq_realm = strdup("");
334 else
335 unq_realm = unq(ms->rdata.realm);
336
337 if (unq_realm == NULL) {
338 free(unq_username);
339 return -1;
340 }
341 len = asprintf(&tmp, "%s:%s:%s",
342 unq_username, unq_realm, ms->rdata.passwd);
343 free(unq_realm);
344 free(unq_username);
345
346 if (len == -1)
347 return -1;
348
349 saslc__crypto_md5_hash(tmp, (size_t)len, buf);
350 memset(tmp, 0, (size_t)len);
351 free(tmp);
352 return 0;
353 }
354
355 /**
356 * @brief setup the appropriate QOP keys as determined by the chosen
357 * QOP type (see RFC2831 sections 2.3 and 2.4).
358 * @param ms mechanism session
359 * @param a1hash MD5(a1)
360 * @return 0 on success, -1 on failure
361 */
362 static int
setup_qop_keys(saslc__mech_digestmd5_sess_t * ms,md5hash_t a1hash)363 setup_qop_keys(saslc__mech_digestmd5_sess_t *ms, md5hash_t a1hash)
364 {
365 #define KIC_MAGIC "Digest session key to client-to-server signing key magic constant"
366 #define KIS_MAGIC "Digest session key to server-to-client signing key magic constant"
367 #define KCC_MAGIC "Digest H(A1) to client-to-server sealing key magic constant"
368 #define KCS_MAGIC "Digest H(A1) to server-to-client sealing key magic constant"
369 #define KIC_MAGIC_LEN (sizeof(KIC_MAGIC) - 1)
370 #define KIS_MAGIC_LEN (sizeof(KIS_MAGIC) - 1)
371 #define KCC_MAGIC_LEN (sizeof(KCC_MAGIC) - 1)
372 #define KCS_MAGIC_LEN (sizeof(KCS_MAGIC) - 1)
373 #define MAX_MAGIC_LEN KIC_MAGIC_LEN
374
375 char buf[MD5_DIGEST_LENGTH + MAX_MAGIC_LEN];
376 size_t buflen;
377 size_t n;
378
379 switch (ms->mech_sess.qop) {
380 case QOP_NONE:
381 /* nothing to do */
382 break;
383
384 case QOP_CONF:
385 /*************************************************************************/
386 /* See RFC2831 section 2.4 (Confidentiality Protection) */
387 /* */
388 /* The key for confidentiality protecting messages from client to server */
389 /* is: */
390 /* */
391 /* Kcc = MD5({H(A1)[0..n], */
392 /* "Digest H(A1) to client-to-server sealing key magic constant"}) */
393 /* */
394 /* The key for confidentiality protecting messages from server to client */
395 /* is: */
396 /* */
397 /* Kcs = MD5({H(A1)[0..n], */
398 /* "Digest H(A1) to server-to-client sealing key magic constant"}) */
399 /* */
400 /* where MD5 is as specified in [RFC 1321]. For cipher "rc4-40" n is 5; */
401 /* for "rc4-56" n is 7; for the rest n is 16. */
402 /*************************************************************************/
403
404 switch (ms->rdata.cipher) {
405 case CIPHER_RC4_40: n = 5; break;
406 case CIPHER_RC4_56: n = 7; break;
407 default: n = MD5_DIGEST_LENGTH; break;
408 }
409 memcpy(buf, a1hash, n);
410
411 memcpy(buf + n, KCC_MAGIC, KCC_MAGIC_LEN);
412 buflen = n + KCC_MAGIC_LEN;
413 saslc__crypto_md5_hash(buf, buflen, ms->keys.kcc);
414
415 memcpy(buf + n, KCS_MAGIC, KCS_MAGIC_LEN);
416 buflen = n + KCS_MAGIC_LEN;
417 saslc__crypto_md5_hash(buf, buflen, ms->keys.kcs);
418
419 /*FALLTHROUGH*/
420
421 case QOP_INT:
422 /*************************************************************************/
423 /* See RFC2831 section 2.3 (Integrity Protection) */
424 /* The key for integrity protecting messages from client to server is: */
425 /* */
426 /* Kic = MD5({H(A1), */
427 /* "Digest session key to client-to-server signing key magic constant"}) */
428 /* */
429 /* The key for integrity protecting messages from server to client is: */
430 /* */
431 /* Kis = MD5({H(A1), */
432 /* "Digest session key to server-to-client signing key magic constant"}) */
433 /*************************************************************************/
434 memcpy(buf, a1hash, MD5_DIGEST_LENGTH);
435
436 memcpy(buf + MD5_DIGEST_LENGTH, KIC_MAGIC, KIC_MAGIC_LEN);
437 buflen = MD5_DIGEST_LENGTH + KIC_MAGIC_LEN;
438 saslc__crypto_md5_hash(buf, buflen, ms->keys.kic);
439
440 memcpy(buf + MD5_DIGEST_LENGTH, KIS_MAGIC, KIS_MAGIC_LEN);
441 buflen = MD5_DIGEST_LENGTH + KIS_MAGIC_LEN;
442 saslc__crypto_md5_hash(buf, buflen, ms->keys.kis);
443 break;
444 }
445 return 0;
446
447 #undef KIC_MAGIC
448 #undef KIS_MAGIC
449 #undef KCC_MAGIC
450 #undef KCS_MAGIC
451 #undef KIC_MAGIC_LEN
452 #undef KIS_MAGIC_LEN
453 #undef KCC_MAGIC_LEN
454 #undef KCS_MAGIC_LEN
455 #undef MAX_MAGIC_LEN
456 }
457
458 /**
459 * @brief computes A1 hash value (see: RFC2831)
460 * @param ms mechanism session
461 * @return hash in hex form
462 */
463 static char *
saslc__mech_digestmd5_a1(saslc__mech_digestmd5_sess_t * ms)464 saslc__mech_digestmd5_a1(saslc__mech_digestmd5_sess_t *ms)
465 {
466 char *tmp1, *tmp2, *r;
467 char *unq_authzid;
468 md5hash_t a1hash, userhash;
469 int plen;
470 size_t len;
471 /*****************************************************************************/
472 /* If authzid is specified, then A1 is */
473 /* */
474 /* A1 = { H({ unq(username-value), ":", unq(realm-value), ":", passwd }), */
475 /* ":", nonce-value, ":", cnonce-value, ":", unq(authzid-value) } */
476 /* */
477 /* If authzid is not specified, then A1 is */
478 /* */
479 /* A1 = { H({ unq(username-value), ":", unq(realm-value), ":", passwd }), */
480 /* ":", nonce-value, ":", cnonce-value } */
481 /*****************************************************************************/
482
483 if (saslc__mech_digestmd5_userhash(ms, userhash) == -1)
484 return NULL;
485
486 if (ms->rdata.authzid == NULL)
487 plen = asprintf(&tmp1, ":%s:%s",
488 ms->cdata.nonce, ms->rdata.cnonce);
489 else {
490 if ((unq_authzid = unq(ms->rdata.authzid)) == NULL)
491 return NULL;
492
493 plen = asprintf(&tmp1, ":%s:%s:%s",
494 ms->cdata.nonce, ms->rdata.cnonce, unq_authzid);
495 free(unq_authzid);
496 }
497 if (plen == -1)
498 return NULL;
499 len = plen;
500
501 tmp2 = malloc(MD5_DIGEST_LENGTH + len);
502 if (tmp2 == NULL) {
503 free(tmp1);
504 return NULL;
505 }
506 memcpy(tmp2, userhash, MD5_DIGEST_LENGTH);
507 memcpy(tmp2 + MD5_DIGEST_LENGTH, tmp1, len);
508 free(tmp1);
509
510 saslc__crypto_md5_hash(tmp2, MD5_DIGEST_LENGTH + len, a1hash);
511 free(tmp2);
512
513 r = saslc__crypto_hash_to_hex(a1hash);
514 setup_qop_keys(ms, a1hash);
515 return r;
516 }
517
518 /**
519 * @brief computes A2 hash value (see: RFC2831)
520 * @param ms mechanism session
521 * @param method string indicating method "AUTHENTICATE" or ""
522 * @return hash converted to ascii
523 */
524 static char *
saslc__mech_digestmd5_a2(saslc__mech_digestmd5_sess_t * ms,const char * method)525 saslc__mech_digestmd5_a2(saslc__mech_digestmd5_sess_t *ms,
526 const char *method)
527 {
528 char *tmp, *r;
529 int rval;
530 /*****************************************************************/
531 /* If the "qop" directive's value is "auth", then A2 is: */
532 /* */
533 /* A2 = { "AUTHENTICATE:", digest-uri-value } */
534 /* */
535 /* If the "qop" value is "auth-int" or "auth-conf" then A2 is: */
536 /* */
537 /* A2 = { "AUTHENTICATE:", digest-uri-value, */
538 /* ":00000000000000000000000000000000" } */
539 /*****************************************************************/
540
541 rval = -1;
542 switch(ms->mech_sess.qop) {
543 case QOP_NONE:
544 rval = asprintf(&tmp, "%s:%s", method,
545 ms->rdata.digesturi);
546 break;
547 case QOP_INT:
548 case QOP_CONF:
549 rval = asprintf(&tmp,
550 "%s:%s:00000000000000000000000000000000",
551 method, ms->rdata.digesturi);
552 break;
553 }
554 if (rval == -1)
555 return NULL;
556
557 r = saslc__crypto_md5_hex(tmp, strlen(tmp));
558 free(tmp);
559 return r;
560 }
561
562 /**
563 * @brief computes result hash.
564 * @param ms mechanism session
565 * @param a1 A1 hash value
566 * @param a2 A2 hash value
567 * @return hash converted to ascii, NULL on failure.
568 */
569 static char *
saslc__mech_digestmd5_rhash(saslc__mech_digestmd5_sess_t * ms,const char * a1,const char * a2)570 saslc__mech_digestmd5_rhash(saslc__mech_digestmd5_sess_t *ms,
571 const char *a1, const char *a2)
572 {
573 char *tmp, *r;
574 /******************************************************************/
575 /* response-value = */
576 /* HEX( KD ( HEX(H(A1)), */
577 /* { nonce-value, ":" nc-value, ":", */
578 /* cnonce-value, ":", qop-value, ":", HEX(H(A2)) })) */
579 /******************************************************************/
580
581 if (asprintf(&tmp, "%s:%s:%08x:%s:%s:%s", a1, ms->cdata.nonce,
582 ms->rdata.nonce_cnt, ms->rdata.cnonce,
583 saslc__mech_qop_name(ms->mech_sess.qop), a2)
584 == -1)
585 return NULL;
586
587 r = saslc__crypto_md5_hex(tmp, strlen(tmp));
588 free(tmp);
589 return r;
590 }
591
592 /**
593 * @brief building response string. Basing on
594 * session and mechanism properties.
595 * @param ms mechanism session
596 * @param method string indicating method: "AUTHENTICATE" or ""
597 * @return response string, NULL on failure.
598 */
599 static char *
saslc__mech_digestmd5_response(saslc__mech_digestmd5_sess_t * ms,const char * method)600 saslc__mech_digestmd5_response(saslc__mech_digestmd5_sess_t *ms,
601 const char *method)
602 {
603 char *r, *a1, *a2;
604
605 /******************************************************************/
606 /* charset = "charset" "=" "utf-8" */
607 /* */
608 /* This directive, if present, specifies that the client has used */
609 /* UTF-8 [UTF-8] encoding for the username, realm and */
610 /* password. If present, the username, realm and password are in */
611 /* Unicode, prepared using the "SASLPrep" profile [SASLPrep] of */
612 /* the "stringprep" algorithm [StringPrep] and than encoded as */
613 /* UTF-8 [UTF-8]. If not present, the username and password must */
614 /* be encoded in ISO 8859-1 [ISO-8859] (of which US-ASCII */
615 /* [USASCII] is a subset). The client should send this directive */
616 /* only if the server has indicated it supports UTF-8 */
617 /* [UTF-8]. The directive is needed for backwards compatibility */
618 /* with HTTP Digest, which only supports ISO 8859-1. */
619 /******************************************************************/
620 /*
621 * NOTE: We don't set charset in the response, so this is not
622 * an issue here. However, see the note in stringprep_realms()
623 * which is called when processing the challenge.
624 */
625 /******************************************************************/
626 /* response-value = */
627 /* HEX( KD ( HEX(H(A1)), */
628 /* { nonce-value, ":" nc-value, ":", */
629 /* cnonce-value, ":", qop-value, ":", HEX(H(A2)) })) */
630 /******************************************************************/
631
632 r = NULL;
633
634 a1 = saslc__mech_digestmd5_a1(ms);
635 if (a1 == NULL)
636 return NULL;
637
638 a2 = saslc__mech_digestmd5_a2(ms, method);
639 if (a2 != NULL) {
640 r = saslc__mech_digestmd5_rhash(ms, a1, a2);
641 free(a2);
642 }
643 free(a1);
644 return r;
645 }
646
647 /**
648 * @brief Choose a string from a user provided host qualified list,
649 * i.e., a comma delimited list with possible hostname qualifiers on
650 * the elements.
651 * @param hqlist a comma delimited list with entries of the form
652 * "[hostname:]string".
653 * @param hostname the hostname to use in the selection.
654 * @param rval pointer to location for returned string. Set to NULL
655 * if none found, otherwise set to strdup(3) of the string found.
656 * @return 0 on success, -1 on failure (no memory).
657 *
658 * NOTE: hqlist and rval must not be NULL.
659 * NOTE: this allocates memory for its output and the caller is
660 * responsible for freeing it.
661 */
662 static int
choose_from_hqlist(const char * hqlist,const char * hostname,char ** rval)663 choose_from_hqlist(const char *hqlist, const char *hostname, char **rval)
664 {
665 list_t *l, *list;
666 size_t len;
667 char *p;
668
669 if (saslc__list_parse(&list, hqlist) == -1)
670 return -1; /* no memory */
671
672 /*
673 * If the user provided a list and the caller provided a
674 * hostname, pick the first string from the list that
675 * corresponds to the hostname.
676 */
677 if (hostname != NULL) {
678 len = strlen(hostname);
679 for (l = list; l != NULL; l = l->next) {
680 p = l->value + len;
681 if (*p != ':' ||
682 strncasecmp(l->value, hostname, len) != 0)
683 continue;
684
685 if (*(++p) != '\0' && isalnum((unsigned char)*p)) {
686 if ((p = strdup(p)) == NULL)
687 goto nomem;
688 goto done;
689 }
690 }
691 }
692 /*
693 * If one couldn't be found, look for first string in the list
694 * without a hostname specifier.
695 */
696 p = NULL;
697 for (l = list; l != NULL; l = l->next) {
698 if (strchr(l->value, ':') == NULL) {
699 if ((p = strdup(l->value)) == NULL)
700 goto nomem;
701 goto done;
702 }
703 }
704 done:
705 saslc__list_free(list);
706 *rval = p;
707 return 0;
708 nomem:
709 saslc__list_free(list);
710 return -1;
711 }
712
713 /**
714 * @brief builds digesturi string
715 * @param serv_type type of service to use, e.g., "smtp"
716 * @param host fully-qualified canonical DNS name of host
717 * @param serv_name service name if it is replicated via DNS records; may
718 * be NULL.
719 * @return digesturi string, NULL on failure.
720 */
721 static char *
saslc__mech_digestmd5_digesturi(saslc_sess_t * sess,const char * serv_host)722 saslc__mech_digestmd5_digesturi(saslc_sess_t *sess, const char *serv_host)
723 {
724 const char *serv_list;
725 char *serv_name;
726 const char *serv_type;
727 char *r;
728 int rv;
729
730 serv_type = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVICE);
731 if (serv_type == NULL) {
732 saslc__error_set(ERR(sess), ERROR_MECH,
733 "service is required for an authentication");
734 return NULL;
735 }
736 serv_list = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVNAME);
737 if (serv_list == NULL)
738 serv_name = NULL;
739 else if (choose_from_hqlist(serv_list, serv_host, &serv_name) == -1)
740 goto nomem;
741
742 saslc__msg_dbg("%s: serv_name='%s'", __func__,
743 serv_name ? serv_name : "<null>");
744
745 /****************************************************************/
746 /* digest-uri = "digest-uri" "=" <"> digest-uri-value <"> */
747 /* digest-uri-value = serv-type "/" host [ "/" serv-name ] */
748 /* */
749 /* If the service is not replicated, or the serv-name is */
750 /* identical to the host, then the serv-name component MUST be */
751 /* omitted. The service is considered to be replicated if the */
752 /* client's service-location process involves resolution using */
753 /* standard DNS lookup operations, and if these operations */
754 /* involve DNS records (such as SRV, or MX) which resolve one */
755 /* DNS name into a set of other DNS names. */
756 /****************************************************************/
757
758 rv = serv_name == NULL || strcmp(serv_host, serv_name) == 0
759 ? asprintf(&r, "%s/%s", serv_type, serv_host)
760 : asprintf(&r, "%s/%s/%s", serv_type, serv_host, serv_name);
761 if (serv_name != NULL)
762 free(serv_name);
763 if (rv == -1)
764 goto nomem;
765
766 saslc__msg_dbg("%s: digest-uri='%s'", __func__, r);
767 return r;
768 nomem:
769 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
770 return NULL;
771 }
772
773 /**
774 * @brief creates client's nonce. (Basing on crypto.h)
775 * @param s length of nonce
776 * @return nonce string, NULL on failure.
777 */
778 static char *
saslc__mech_digestmd5_nonce(size_t s)779 saslc__mech_digestmd5_nonce(size_t s)
780 {
781 char *nonce;
782 char *r;
783
784 nonce = saslc__crypto_nonce(s);
785 if (nonce == NULL)
786 return NULL;
787
788 if (saslc__crypto_encode_base64(nonce, s, &r, NULL) == -1)
789 return NULL;
790 free(nonce);
791
792 return r;
793 }
794
795 /**
796 * @brief strip quotes from a string (modifies the string)
797 * @param str the string
798 * @return string without quotes.
799 */
800 static char *
strip_quotes(char * str)801 strip_quotes(char *str)
802 {
803 char *p;
804 size_t len;
805
806 if (*str != '"')
807 return str;
808
809 len = strlen(str);
810 p = str + len;
811 if (len < 2 || p[-1] != '"')
812 return str;
813
814 p[-1] = '\0';
815 return ++str;
816 }
817
818 /**
819 * @brief convert a list of realms from utf-8 to iso8859-q if necessary.
820 * @param is_utf8 the characterset of the realms (true if utf8)
821 * @param realms the realm list
822 */
823 static int
stringprep_realms(bool is_utf8,list_t * realms)824 stringprep_realms(bool is_utf8, list_t *realms)
825 {
826 list_t *l;
827 char *utf8, *iso8859;
828
829 /******************************************************************/
830 /* If at least one realm is present and the charset directive is */
831 /* also specified (which means that realm(s) are encoded as */
832 /* UTF-8), the client should prepare each instance of realm using */
833 /* the "SASLPrep" profile [SASLPrep] of the "stringprep" */
834 /* algorithm [StringPrep]. If preparation of a realm instance */
835 /* fails or results in an empty string, the client should abort */
836 /* the authentication exchange. */
837 /******************************************************************/
838 if (!is_utf8)
839 return 0;
840
841 for (l = realms; l != NULL; l = l->next) {
842 utf8 = l->value;
843 if (utf8_to_8859_1(utf8, &iso8859) == -1)
844 return -1;
845 free(utf8);
846 l->value = iso8859;
847 }
848 return 0;
849 }
850
851 /**
852 * @brief choose a realm from a list of possible realms provided by the server
853 * @param sess the session context
854 * @param realms the list of realms
855 * @return our choice of realm or NULL on failure. It is the user's
856 * responsibility to free the memory allocated for the return string.
857 */
858 static char *
choose_realm(saslc_sess_t * sess,const char * hostname,list_t * realms)859 choose_realm(saslc_sess_t *sess, const char *hostname, list_t *realms)
860 {
861 const char *user_realms;
862 list_t *l;
863 char *p = NULL;
864
865 /*****************************************************************/
866 /* The realm containing the user's account. This directive is */
867 /* required if the server provided any realms in the */
868 /* "digest-challenge", in which case it may appear exactly once */
869 /* and its value SHOULD be one of those realms. If the directive */
870 /* is missing, "realm-value" will set to the empty string when */
871 /* computing A1 (see below for details). */
872 /*****************************************************************/
873
874 user_realms = saslc_sess_getprop(sess, SASLC_DIGESTMD5_REALM);
875
876 /*
877 * If the challenge provided no realms, try to pick one from a
878 * user specified list, which may be keyed by the hostname.
879 * If one can't be found, return NULL;
880 */
881 if (realms == NULL) {
882 /*
883 * No realm was supplied in challenge. Figure out a
884 * plausable default.
885 */
886 if (user_realms == NULL) {
887 saslc__error_set(ERR(sess), ERROR_MECH,
888 "cannot determine the realm");
889 return NULL;
890 }
891 if (choose_from_hqlist(user_realms, hostname, &p) == -1)
892 goto nomem;
893
894 if (p == NULL)
895 saslc__error_set(ERR(sess), ERROR_MECH,
896 "cannot choose a realm");
897 return p;
898 }
899
900 /************************************************************/
901 /* Multiple realm directives are allowed, in which case the */
902 /* user or client must choose one as the realm for which to */
903 /* supply to username and password. */
904 /************************************************************/
905 /*
906 * If the user hasn't specified any realms, or we can't find
907 * one from the user provided list, just take the first realm
908 * from the challenge.
909 */
910 if (user_realms == NULL)
911 goto use_1st_realm;
912
913 if (choose_from_hqlist(user_realms, hostname, &p) == -1)
914 goto nomem;
915
916 if (p == NULL)
917 goto use_1st_realm;
918
919 /*
920 * If we found a matching user provide realm, make sure it is
921 * on the list of realms. If it isn't, just take the first
922 * realm in the challenge.
923 */
924 for (l = realms; l != NULL; l = l->next) {
925 if (strcasecmp(p, l->value) == 0)
926 return p;
927 }
928 use_1st_realm:
929 free(p);
930 if ((p = strdup(realms->value)) == NULL)
931 goto nomem;
932 return p;
933 nomem:
934 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
935 return NULL;
936 }
937
938 /**
939 * @brief destroy a cipher context
940 * @param ctx cipher context
941 * @return nothing
942 */
943 static void
cipher_context_destroy(cipher_context_t * ctx)944 cipher_context_destroy(cipher_context_t *ctx)
945 {
946
947 if (ctx != NULL) {
948 if (ctx->evp_ctx != NULL)
949 EVP_CIPHER_CTX_free(ctx->evp_ctx);
950 free(ctx);
951 }
952 }
953
954 /**
955 * @brief slide the bits from 7 bytes into the high 7 bits of 8 bites
956 * @param ikey input key
957 * @param okey output key
958 *
959 * This matches cyrus-sasl 2.1.23
960 */
961 static inline void
slidebits(uint8_t * ikey,uint8_t * okey)962 slidebits(uint8_t *ikey, uint8_t *okey)
963 {
964
965 okey[0] = ikey[0] << 0;
966 okey[1] = ikey[0] << 7 | (unsigned)ikey[1] >> 1;
967 okey[2] = ikey[1] << 6 | (unsigned)ikey[2] >> 2;
968 okey[3] = ikey[2] << 5 | (unsigned)ikey[3] >> 3;
969 okey[4] = ikey[3] << 4 | (unsigned)ikey[4] >> 4;
970 okey[5] = ikey[4] << 3 | (unsigned)ikey[5] >> 5;
971 okey[6] = ikey[5] << 2 | (unsigned)ikey[6] >> 6;
972 okey[7] = ikey[6] << 1;
973 }
974
975 /**
976 * @brief convert our key to a DES key
977 * @param key our key
978 * @param keylen our key length
979 * @param deskey the key in DES format
980 *
981 * NOTE: The openssl implementations of "des" and "3des" expect their
982 * keys to be in the high 7 bits of 8 bytes and 16 bytes,
983 * respectively. Thus, our key length will be 7 and 14 bytes,
984 * respectively.
985 */
986 static void
make_deskey(uint8_t * key,size_t keylen,uint8_t * deskey)987 make_deskey(uint8_t *key, size_t keylen, uint8_t *deskey)
988 {
989
990 assert(keylen == 7 || keylen == 14);
991
992 slidebits(deskey + 0, key + 0);
993 if (keylen == 14)
994 slidebits(deskey + 7, key + 7);
995 }
996
997 /**
998 * @brief create a cipher context, including EVP cipher initialization.
999 * @param sess session context
1000 * @param cipher cipher to use
1001 * @param do_enc encode context if set, decode context if 0
1002 * @param key crypt key to use
1003 * @return cipher context, or NULL on error
1004 */
1005 static cipher_context_t *
cipher_context_create(saslc_sess_t * sess,cipher_t cipher,int do_enc,uint8_t * key)1006 cipher_context_create(saslc_sess_t *sess, cipher_t cipher, int do_enc, uint8_t *key)
1007 {
1008 #define AES_IV_MAGIC "aes-128"
1009 #define AES_IV_MAGIC_LEN (sizeof(AES_IV_MAGIC) - 1)
1010 static const struct cipher_ctx_tbl_s {
1011 cipher_t eval; /* for error checking */
1012 const EVP_CIPHER *(*evp_type)(void);/* type of cipher */
1013 size_t keylen; /* key length */
1014 ssize_t blksize; /* block size for cipher */
1015 size_t ivlen; /* initial value length */
1016 } cipher_ctx_tbl[] = {
1017 /* NB: table indexed by cipher_t */
1018 /* eval evp_type keylen blksize ivlen */
1019 { CIPHER_DES, EVP_des_cbc, 7, 8, 8 },
1020 { CIPHER_3DES, EVP_des_ede_cbc, 14, 8, 8 },
1021 { CIPHER_RC4, EVP_rc4, 16, 1, 0 },
1022 { CIPHER_RC4_40, EVP_rc4, 5, 1, 0 },
1023 { CIPHER_RC4_56, EVP_rc4, 7, 1, 0 },
1024 { CIPHER_AES, EVP_aes_128_cbc, 16, 16, 16 }
1025 };
1026 const struct cipher_ctx_tbl_s *ctp;
1027 char buf[sizeof(md5hash_t) + AES_IV_MAGIC_LEN];
1028 uint8_t deskey[16];
1029 md5hash_t aes_iv; /* initial value buffer for aes */
1030 cipher_context_t *ctx; /* cipher context */
1031 uint8_t *ivp;
1032 const char *errmsg;
1033 int rv;
1034
1035 /*************************************************************************/
1036 /* See draft-ietf-sasl-rfc2831bis-02.txt section 2.4 (mentions "aes") */
1037 /* The key for the "rc4" and "aes" ciphers is all 16 bytes of Kcc or Kcs.*/
1038 /* The key for the "rc4-40" cipher is the first 5 bytes of Kcc or Kcs. */
1039 /* The key for the "rc4-56" is the first 7 bytes of Kcc or Kcs. */
1040 /* The key for "des" is the first 7 bytes of Kcc or Kcs. */
1041 /* The key for "3des" is the first 14 bytes of Kcc or Kcs. */
1042 /* */
1043 /* The IV used to send/receive the initial buffer of security encoded */
1044 /* data for "des" and "3des" is the last 8 bytes of Kcc or Kcs. For all */
1045 /* subsequent buffers the last 8 bytes of the ciphertext of the buffer */
1046 /* NNN is used as the IV for the buffer (NNN + 1). */
1047 /* */
1048 /* The IV for the "aes" cipher in CBC mode for messages going from the */
1049 /* client to the server (IVc) consists of 16 bytes calculated as */
1050 /* follows: IVc = MD5({Kcc, "aes-128"}) */
1051 /* */
1052 /* The IV for the "aes" cipher in CBC mode for messages going from the */
1053 /* server to the client (IVs) consists of 16 bytes calculated as */
1054 /* follows: IVs = MD5({Kcs, "aes-128"}) */
1055 /*************************************************************************/
1056
1057 assert(cipher < __arraycount(cipher_ctx_tbl));
1058 if (cipher >= __arraycount(cipher_ctx_tbl)) {
1059 saslc__error_set_errno(ERR(sess), ERROR_BADARG);
1060 return NULL;
1061 }
1062
1063 ctx = malloc(sizeof(*ctx));
1064 if (ctx == NULL) {
1065 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1066 return NULL;
1067 }
1068
1069 ctp = &cipher_ctx_tbl[cipher];
1070 assert(ctp->eval == cipher);
1071
1072 ctx->blksize = ctp->blksize;
1073
1074 ctx->evp_ctx = EVP_CIPHER_CTX_new();
1075 if (ctx->evp_ctx == NULL) {
1076 errmsg = "EVP_CIPHER_CTX_new failed";
1077 goto err;
1078 }
1079 if (EVP_CipherInit_ex(ctx->evp_ctx, ctp->evp_type(), NULL, NULL, NULL,
1080 do_enc) == 0) {
1081 errmsg = "EVP_CipherInit_ex failed";
1082 goto err;
1083 }
1084 if (EVP_CIPHER_CTX_set_padding(ctx->evp_ctx, 0) == 0) {
1085 errmsg = "EVP_CIPHER_CTX_set_padding failed";
1086 goto err;
1087 }
1088 ivp = NULL;
1089 switch (cipher) { /* prepare key and IV */
1090 case CIPHER_RC4:
1091 case CIPHER_RC4_40:
1092 case CIPHER_RC4_56:
1093 assert(ctp->ivlen == 0); /* no IV */
1094 rv = EVP_CIPHER_CTX_set_key_length(ctx->evp_ctx,
1095 (int)ctp->keylen);
1096 if (rv == 0) {
1097 errmsg = "EVP_CIPHER_CTX_set_key_length failed";
1098 goto err;
1099 }
1100 break;
1101 case CIPHER_DES:
1102 case CIPHER_3DES:
1103 assert(ctp->ivlen == 8);
1104 ivp = key + 8;
1105 make_deskey(key, ctp->keylen, deskey);
1106 key = deskey;
1107 break;
1108 case CIPHER_AES:
1109 assert(ctp->ivlen == 16);
1110 /* IVs = MD5({Kcs, "aes-128"}) */
1111 memcpy(buf, key, sizeof(md5hash_t));
1112 memcpy(buf + sizeof(md5hash_t), AES_IV_MAGIC, AES_IV_MAGIC_LEN);
1113 saslc__crypto_md5_hash(buf, sizeof(buf), aes_iv);
1114 ivp = aes_iv;
1115 break;
1116 }
1117 if (EVP_CipherInit_ex(ctx->evp_ctx, NULL, NULL, key, ivp, do_enc) == 0) {
1118 errmsg = "EVP_CipherInit_ex 2 failed";
1119 goto err;
1120 }
1121 return ctx;
1122 err:
1123 cipher_context_destroy(ctx);
1124 saslc__error_set(ERR(sess), ERROR_MECH, errmsg);
1125 return NULL;
1126
1127 #undef AES_IV_MAGIC_LEN
1128 #undef AES_IV_MAGIC
1129 }
1130
1131 /**
1132 * @brief compute the necessary padding length
1133 * @param ctx the cipher context
1134 * @param inlen the data length to put in the packet
1135 * @return the length of padding needed (zero if none needed)
1136 */
1137 static size_t
get_padlen(cipher_context_t * ctx,size_t inlen)1138 get_padlen(cipher_context_t *ctx, size_t inlen)
1139 {
1140 size_t blksize;
1141
1142 if (ctx == NULL)
1143 return 0;
1144
1145 blksize = ctx->blksize;
1146 if (blksize == 1)
1147 return 0;
1148
1149 return blksize - ((inlen + 10) % blksize);
1150 }
1151
1152 /**
1153 * @brief compute the packet integrity including the version and
1154 * sequence number
1155 * @param key the hmac_md5 hash key
1156 * @param seqnum the sequence number
1157 * @param in the input buffer
1158 * @param inlen the input buffer length
1159 * @return 0 on success, -1 on failure
1160 */
1161 static int
packet_integrity(md5hash_t key,uint32_t seqnum,void * in,size_t inlen,md5hash_t mac)1162 packet_integrity(md5hash_t key, uint32_t seqnum, void *in, size_t inlen,
1163 md5hash_t mac)
1164 {
1165
1166 be32enc(in, seqnum);
1167 if (saslc__crypto_hmac_md5_hash(key, MD5_DIGEST_LENGTH, in, inlen, mac)
1168 == -1)
1169 return -1;
1170
1171 /* we keep only the first 10 bytes of the hash */
1172 be16enc(mac + 10, 0x0001); /* add 2 byte version number */
1173 be32enc(mac + 12, seqnum); /* add 4 byte sequence number */
1174 return 0;
1175 }
1176
1177 /**
1178 * @brief encode or decode a buffer (in place)
1179 * @param ctx the cipher context
1180 * @param in the input buffer
1181 * @param inlen the buffer length
1182 * @return the length of the result left in the input buffer after
1183 * processing, or -1 on failure.
1184 */
1185 static ssize_t
cipher_update(cipher_context_t * ctx,void * in,size_t inlen)1186 cipher_update(cipher_context_t *ctx, void *in, size_t inlen)
1187 {
1188 int outl, rv;
1189 void *out;
1190
1191 out = in; /* XXX: this assumes we can encoded and decode in place */
1192 rv = EVP_CipherUpdate(ctx->evp_ctx, out, &outl, in, (int)inlen);
1193 if (rv == 0)
1194 return -1;
1195
1196 return outl;
1197 }
1198
1199 /**
1200 * @brief incapsulate a message with confidentiality (sign and encrypt)
1201 * @param ctx coder context
1202 * @param in pointer to message to encode
1203 * @param inlen length of message
1204 * @param out encoded output packet (including prefixed 4 byte length field)
1205 * @param outlen decoded output packet length
1206 * @returns 0 on success, -1 on failure
1207 *
1208 * NOTE: this allocates memory for its output and the caller is
1209 * responsible for freeing it.
1210 *
1211 * integrity (auth-int):
1212 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1213 *
1214 * confidentiality (auth-conf):
1215 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1216 */
1217 static ssize_t
encode_buffer(coder_context_t * ctx,const void * in,size_t inlen,void ** out,size_t * outlen)1218 encode_buffer(coder_context_t *ctx, const void *in, size_t inlen,
1219 void **out, size_t *outlen)
1220 {
1221 void *buf;
1222 uint8_t *mac, *p;
1223 ssize_t tmplen;
1224 size_t buflen;
1225 size_t padlen;
1226
1227 padlen = get_padlen(ctx->cph_ctx, inlen);
1228 buflen = 4 + inlen + padlen + sizeof(md5hash_t);
1229 buf = malloc(buflen);
1230 if (buf == NULL) {
1231 saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM);
1232 return -1;
1233 }
1234 p = buf;
1235 memcpy(p + 4, in, inlen);
1236 mac = p + 4 + inlen + padlen;
1237 if (packet_integrity(ctx->key, ctx->seqnum, buf, 4 + inlen, mac)
1238 == -1) {
1239 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed");
1240 free(buf);
1241 return -1;
1242 }
1243
1244 if (padlen)
1245 memset(p + 4 + inlen, (int)padlen, padlen);
1246
1247 if (ctx->cph_ctx != NULL) {
1248 if ((tmplen = cipher_update(ctx->cph_ctx, p + 4,
1249 inlen + padlen + 10)) == -1) {
1250 saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1251 "cipher error");
1252 free(buf);
1253 return -1;
1254 }
1255 assert((size_t)tmplen == inlen + padlen + 10);
1256 if ((size_t)tmplen != inlen + padlen + 10)
1257 return -1;
1258 }
1259
1260 be32enc(buf, (uint32_t)(buflen - 4));
1261
1262 *out = buf;
1263 *outlen = buflen;
1264 ctx->seqnum++; /* wraps at 2^32 */
1265 return 0;
1266 }
1267
1268 /**
1269 * @brief decode one complete confidentiality encoded packet
1270 * @param ctx coder context
1271 * @param in pointer to packet, including the beginning 4 byte length field.
1272 * @param inlen length of packet
1273 * @param out decoded output
1274 * @param outlen decoded output length
1275 * @returns 0 on success, -1 on failure
1276 *
1277 * NOTE: this modifies the intput buffer!
1278 * NOTE: this allocates memory for its output and the caller is
1279 * responsible for freeing it.
1280 *
1281 * integrity (auth-int):
1282 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1283 *
1284 * confidentiality (auth-conf):
1285 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1286 */
1287 static ssize_t
decode_buffer(coder_context_t * ctx,void * in,size_t inlen,void ** out,size_t * outlen)1288 decode_buffer(coder_context_t *ctx, void *in, size_t inlen,
1289 void **out, size_t *outlen)
1290 {
1291 md5hash_t mac;
1292 void *buf;
1293 uint8_t *p;
1294 size_t blksize, buflen, padlen;
1295 ssize_t tmplen;
1296 uint32_t len;
1297
1298 padlen = get_padlen(ctx->cph_ctx, 1);
1299 if (inlen < 4 + 1 + padlen + MD5_DIGEST_LENGTH) {
1300 saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1301 "zero payload packet");
1302 return -1;
1303 }
1304 len = be32dec(in);
1305 if (len + 4 != inlen) {
1306 saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1307 "bad packet length");
1308 return -1;
1309 }
1310
1311 if (ctx->cph_ctx != NULL) {
1312 p = in;
1313 if ((tmplen = cipher_update(ctx->cph_ctx, p + 4, len - 6)) == -1) {
1314 saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1315 "cipher error");
1316 return -1;
1317 }
1318 assert(tmplen == (ssize_t)len - 6);
1319 if (tmplen != (ssize_t)len - 6)
1320 return -1;
1321 }
1322
1323 blksize = ctx->cph_ctx ? ctx->cph_ctx->blksize : 0;
1324 if (blksize <= 1)
1325 padlen = 0;
1326 else{
1327 p = in;
1328 padlen = p[inlen - sizeof(md5hash_t) - 1];
1329 if (padlen > blksize || padlen == 0) {
1330 saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1331 "invalid padding length after decode");
1332 return -1;
1333 }
1334 }
1335 if (packet_integrity(ctx->key, ctx->seqnum, in,
1336 inlen - padlen - sizeof(mac), mac) == -1) {
1337 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed");
1338 return -1;
1339 }
1340
1341 p = in;
1342 p += 4 + len - MD5_DIGEST_LENGTH;
1343 if (memcmp(p, mac, MD5_DIGEST_LENGTH) != 0) {
1344 uint32_t seqnum;
1345
1346 p = in;
1347 seqnum = be32dec(p + inlen - 4);
1348 saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1349 seqnum != ctx->seqnum ? "invalid MAC (bad seqnum)" :
1350 "invalid MAC");
1351 return -1;
1352 }
1353
1354 buflen = len - padlen - MD5_DIGEST_LENGTH;
1355 buf = malloc(buflen);
1356 if (buf == NULL) {
1357 saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM);
1358 return -1;
1359 }
1360 p = in;
1361 p += 4;
1362 memcpy(buf, p, buflen);
1363
1364 *out = buf;
1365 *outlen = buflen;
1366 ctx->seqnum++;
1367 return 0;
1368 }
1369
1370 /**
1371 * @brief add integrity or confidentiality layer
1372 * @param sess session handle
1373 * @param in input buffer
1374 * @param inlen input buffer length
1375 * @param out pointer to output buffer
1376 * @param out pointer to output buffer length
1377 * @return number of bytes consumed on success, 0 if insufficient data
1378 * to process, -1 on failure
1379 */
1380 static ssize_t
saslc__mech_digestmd5_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)1381 saslc__mech_digestmd5_encode(saslc_sess_t *sess, const void *in, size_t inlen,
1382 void **out, size_t *outlen)
1383 {
1384 saslc__mech_digestmd5_sess_t *ms;
1385 uint8_t *buf;
1386 size_t buflen;
1387 ssize_t rval;
1388
1389 ms = sess->mech_sess;
1390 assert(ms->mech_sess.qop != QOP_NONE);
1391 if (ms->mech_sess.qop == QOP_NONE)
1392 return -1;
1393
1394 rval = saslc__buffer_fetch(ms->enc_ctx.buf_ctx, in, inlen, &buf, &buflen);
1395 if (rval == -1)
1396 return -1;
1397 if (buflen == 0) {
1398 *out = NULL;
1399 *outlen = 0;
1400 return rval;
1401 }
1402 if (encode_buffer(&ms->enc_ctx, buf, buflen, out, outlen) == -1)
1403 return -1;
1404
1405 return rval;
1406 }
1407
1408 /**
1409 * @brief remove integrity or confidentiality layer
1410 * @param sess session handle
1411 * @param in input buffer
1412 * @param inlen input buffer length
1413 * @param out pointer to output buffer
1414 * @param out pointer to output buffer length
1415 * @return number of bytes consumed on success, 0 if insufficient data
1416 * to process, -1 on failure
1417 *
1418 * integrity (auth-int):
1419 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1420 *
1421 * confidentiality (auth-conf):
1422 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1423 */
1424 static ssize_t
saslc__mech_digestmd5_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)1425 saslc__mech_digestmd5_decode(saslc_sess_t *sess, const void *in, size_t inlen,
1426 void **out, size_t *outlen)
1427 {
1428 saslc__mech_digestmd5_sess_t *ms;
1429 uint8_t *buf;
1430 size_t buflen;
1431 ssize_t rval;
1432
1433 ms = sess->mech_sess;
1434 assert(ms->mech_sess.qop != QOP_NONE);
1435 if (ms->mech_sess.qop == QOP_NONE)
1436 return -1;
1437
1438 rval = saslc__buffer32_fetch(ms->dec_ctx.buf_ctx, in, inlen, &buf, &buflen);
1439 if (rval == -1)
1440 return -1;
1441
1442 if (buflen == 0) {
1443 *out = NULL;
1444 *outlen = 0;
1445 return rval;
1446 }
1447 if (decode_buffer(&ms->dec_ctx, buf, buflen, out, outlen) == -1)
1448 return -1;
1449
1450 return rval;
1451 }
1452
1453 /************************************************************************
1454 * XXX: Share with mech_gssapi.c? They are almost identical.
1455 */
1456 /**
1457 * @brief choose the best qop based on what was provided by the
1458 * challenge and a possible user mask.
1459 * @param sess the session context
1460 * @param qop_flags the qop flags parsed from the challenge string
1461 * @return the selected saslc__mech_sess_qop_t or -1 if no match
1462 */
1463 static int
choose_qop(saslc_sess_t * sess,uint32_t qop_flags)1464 choose_qop(saslc_sess_t *sess, uint32_t qop_flags)
1465 {
1466 list_t *list;
1467 const char *user_qop;
1468
1469 if (qop_flags == 0) /* no qop spec in challenge (it's optional) */
1470 return QOP_NONE;
1471
1472 qop_flags &= DEFAULT_QOP_MASK;
1473 user_qop = saslc_sess_getprop(sess, SASLC_DIGESTMD5_QOPMASK);
1474 if (user_qop != NULL) {
1475 if (saslc__list_parse(&list, user_qop) == -1) {
1476 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1477 return -1;
1478 }
1479 qop_flags &= saslc__mech_qop_list_flags(list);
1480 saslc__list_free(list);
1481 }
1482
1483 /*
1484 * Select the most secure supported qop.
1485 */
1486 if ((qop_flags & F_QOP_CONF) != 0)
1487 return QOP_CONF;
1488 if ((qop_flags & F_QOP_INT) != 0)
1489 return QOP_INT;
1490 if ((qop_flags & F_QOP_NONE) != 0)
1491 return QOP_NONE;
1492
1493 saslc__error_set(ERR(sess), ERROR_MECH,
1494 "cannot choose an acceptable qop");
1495 return -1;
1496 }
1497 /************************************************************************/
1498
1499 /**
1500 * @brief choose the best cipher based on what was provided by the
1501 * challenge and a possible user mask.
1502 * @param sess the session context
1503 * @param cipher_flags the cipher flags parsed from the challenge
1504 * string
1505 * @return the selected cipher_t
1506 */
1507 static int
choose_cipher(saslc_sess_t * sess,unsigned int cipher_flags)1508 choose_cipher(saslc_sess_t *sess, unsigned int cipher_flags)
1509 {
1510 list_t *list;
1511 unsigned int cipher_mask;
1512 const char *user_cipher;
1513
1514 if (cipher_flags == 0) {
1515 saslc__error_set(ERR(sess), ERROR_MECH,
1516 "no cipher spec in challenge");
1517 return -1;
1518 }
1519 cipher_mask = DEFAULT_CIPHER_MASK;
1520 user_cipher = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CIPHERMASK);
1521 if (user_cipher != NULL) {
1522 if (saslc__list_parse(&list, user_cipher) == -1) {
1523 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1524 return -1;
1525 }
1526 cipher_mask = cipher_list_flags(list);
1527 saslc__list_free(list);
1528 }
1529 cipher_flags &= cipher_mask;
1530
1531 /*
1532 * Select the most secure cipher supported.
1533 * XXX: Is the order here right?
1534 */
1535 if ((cipher_flags & F_CIPHER_AES) != 0)
1536 return CIPHER_AES;
1537 if ((cipher_flags & F_CIPHER_3DES) != 0)
1538 return CIPHER_3DES;
1539 if ((cipher_flags & F_CIPHER_DES) != 0)
1540 return CIPHER_DES;
1541 if ((cipher_flags & F_CIPHER_RC4) != 0)
1542 return CIPHER_RC4;
1543 if ((cipher_flags & F_CIPHER_RC4_56) != 0)
1544 return CIPHER_RC4_56;
1545 if ((cipher_flags & F_CIPHER_RC4_40) != 0)
1546 return CIPHER_RC4_40;
1547
1548 saslc__error_set(ERR(sess), ERROR_MECH,
1549 "qop \"auth-conf\" requires a cipher");
1550 return -1;
1551 }
1552
1553 /**
1554 * @brief get the challenge_t value corresponding to a challenge key
1555 * string.
1556 * @param key challenge key string
1557 * @return the challenge_t value including CHALLENGE_IGNORE (-1) if
1558 * the key is not recognized
1559 */
1560 static challenge_t
get_challenge_t(const char * key)1561 get_challenge_t(const char *key)
1562 {
1563 static const struct {
1564 const char *key;
1565 challenge_t value;
1566 } challenge_keys[] = {
1567 { "realm", CHALLENGE_REALM },
1568 { "nonce", CHALLENGE_NONCE },
1569 { "qop", CHALLENGE_QOP },
1570 { "stale", CHALLENGE_STALE },
1571 { "maxbuf", CHALLENGE_MAXBUF },
1572 { "charset", CHALLENGE_CHARSET },
1573 { "algorithm", CHALLENGE_ALGORITHM },
1574 { "cipher", CHALLENGE_CIPHER }
1575 };
1576 size_t i;
1577
1578 for (i = 0; i < __arraycount(challenge_keys); i++) {
1579 if (strcasecmp(key, challenge_keys[i].key) == 0)
1580 return challenge_keys[i].value;
1581 }
1582 return CHALLENGE_IGNORE;
1583 }
1584
1585 /**
1586 * @brief parses challenge and store result in mech_sess.
1587 * @param mech_sess session where parsed data will be stored
1588 * @param challenge challenge
1589 * @return 0 on success, -1 on failure.
1590 */
1591 static int
saslc__mech_digestmd5_parse_challenge(saslc_sess_t * sess,const char * challenge)1592 saslc__mech_digestmd5_parse_challenge(saslc_sess_t *sess, const char *challenge)
1593 {
1594 saslc__mech_digestmd5_sess_t *ms;
1595 list_t *list, *n;
1596 list_t *tmp_list;
1597 cdata_t *cdata;
1598 size_t maxbuf;
1599 uint32_t tmp_flags;
1600 int rv;
1601
1602 /******************************************************************/
1603 /* digest-challenge = */
1604 /* 1#( realm | nonce | qop-options | stale | server_maxbuf | */
1605 /* charset | algorithm | cipher-opts | auth-param ) */
1606 /******************************************************************/
1607
1608 saslc__msg_dbg("challenge: '%s'\n", challenge);
1609
1610 ms = sess->mech_sess;
1611 cdata = &ms->cdata;
1612
1613 rv = -1;
1614 memset(cdata, 0, sizeof(*cdata));
1615 if (saslc__list_parse(&list, challenge) == -1) {
1616 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1617 return -1;
1618 }
1619 saslc__list_log(list, "parse list:\n");
1620 for (n = list; n != NULL; n = n->next) {
1621 char *key;
1622 char *val;
1623
1624 /* Split string into key and val */
1625 key = n->value;
1626 val = strchr(key, '=');
1627 if (val == NULL)
1628 goto no_mem;
1629 *val = '\0';
1630 val = strip_quotes(val + 1);
1631
1632 saslc__msg_dbg("key='%s' val='%s'\n", key, val);
1633 switch (get_challenge_t(key)) {
1634 case CHALLENGE_REALM:
1635 /**************************************************/
1636 /* realm = "realm" "=" <"> realm-value <"> */
1637 /* realm-value = qdstr-val */
1638 /* */
1639 /* This directive is optional; if not present, */
1640 /* the client SHOULD solicit it from the user or */
1641 /* be able to compute a default; a plausible */
1642 /* default might be the realm supplied by the */
1643 /* user when they logged in to the client system. */
1644 /* Multiple realm directives are allowed, in */
1645 /* which case the user or client must choose one */
1646 /* as the realm for which to supply to username */
1647 /* and password. */
1648 /**************************************************/
1649 if (saslc__list_append(&cdata->realm, val) == -1)
1650 goto no_mem;
1651 break;
1652 case CHALLENGE_NONCE:
1653 /**************************************************/
1654 /* nonce = "nonce" "=" <"> nonce-value <"> */
1655 /* nonce-value = *qdtext */
1656 /* */
1657 /* This directive is required and MUST appear */
1658 /* exactly once; if not present, or if multiple */
1659 /* instances are present, the client should abort */
1660 /* the authentication exchange. */
1661 /**************************************************/
1662 if (cdata->nonce != NULL) {
1663 saslc__error_set(ERR(sess), ERROR_MECH,
1664 "multiple nonce in challenge");
1665 goto out;
1666 }
1667 cdata->nonce = strdup(val);
1668 if (cdata->nonce == NULL)
1669 goto no_mem;
1670 break;
1671 case CHALLENGE_QOP:
1672 /**************************************************/
1673 /* qop-options = "qop" "=" <"> qop-list <"> */
1674 /* qop-list = 1#qop-value */
1675 /* qop-value = "auth" | "auth-int" | */
1676 /* "auth-conf" | token */
1677 /* */
1678 /* This directive is optional; if not present it */
1679 /* defaults to "auth". The client MUST ignore */
1680 /* unrecognized options; if the client recognizes */
1681 /* no option, it should abort the authentication */
1682 /* exchange. */
1683 /**************************************************/
1684 if (saslc__list_parse(&tmp_list, val) == -1)
1685 goto no_mem;
1686 saslc__list_log(tmp_list, "qop list:\n");
1687 tmp_flags = saslc__mech_qop_list_flags(tmp_list);
1688 saslc__list_free(tmp_list);
1689 if (tmp_flags == 0) {
1690 saslc__error_set(ERR(sess), ERROR_MECH,
1691 "qop required in challenge");
1692 goto out;
1693 }
1694 cdata->qop_flags |= tmp_flags;
1695 break;
1696 case CHALLENGE_STALE:
1697 /**************************************************/
1698 /* stale = "stale" "=" "true" */
1699 /* */
1700 /* This directive may appear at most once; if */
1701 /* multiple instances are present, the client */
1702 /* should abort the authentication exchange. */
1703 /**************************************************/
1704 if (cdata->stale) {
1705 saslc__error_set(ERR(sess), ERROR_MECH,
1706 "multiple stale in challenge");
1707 goto out;
1708 }
1709 if (strcasecmp(val, "true") != 0) {
1710 saslc__error_set(ERR(sess), ERROR_MECH,
1711 "stale must be true");
1712 goto out;
1713 }
1714 cdata->stale = true;
1715 break;
1716 case CHALLENGE_MAXBUF:
1717 /**************************************************/
1718 /* maxbuf-value = 1*DIGIT */
1719 /* */
1720 /* The value MUST be bigger than 16 and smaller */
1721 /* or equal to 16777215 (i.e. 2**24-1). If this */
1722 /* directive is missing, the default value is */
1723 /* 65536. This directive may appear at most once; */
1724 /* if multiple instances are present, the client */
1725 /* MUST abort the authentication exchange. */
1726 /**************************************************/
1727 if (cdata->maxbuf != 0) {
1728 saslc__error_set(ERR(sess), ERROR_MECH,
1729 "multiple maxbuf in challenge");
1730 goto out;
1731 }
1732 maxbuf = (size_t)strtoul(val, NULL, 10);
1733 if (INVALID_MAXBUF(maxbuf)) {
1734 saslc__error_set(ERR(sess), ERROR_MECH,
1735 "invalid maxbuf in challenge");
1736 goto out;
1737 }
1738 cdata->maxbuf = maxbuf;
1739 break;
1740 case CHALLENGE_CHARSET:
1741 /**************************************************/
1742 /* charset = "charset" "=" "utf-8" */
1743 /* */
1744 /* This directive may appear at most once; if */
1745 /* multiple instances are present, the client */
1746 /* should abort the authentication exchange. */
1747 /**************************************************/
1748 if (cdata->utf8) {
1749 saslc__error_set(ERR(sess), ERROR_MECH,
1750 "multiple charset in challenge");
1751 goto out;
1752 }
1753 if (strcasecmp(val, "utf-8") != 0) {
1754 saslc__error_set(ERR(sess), ERROR_MECH,
1755 "charset != \"utf-8\" in challenge");
1756 goto out;
1757 }
1758 cdata->utf8 = true;
1759 break;
1760 case CHALLENGE_ALGORITHM:
1761 /**************************************************/
1762 /* algorithm = "algorithm" "=" "md5-sess" */
1763 /* */
1764 /* This directive is required and MUST appear */
1765 /* exactly once; if not present, or if multiple */
1766 /* instances are present, the client should abort */
1767 /* the authentication exchange. */
1768 /**************************************************/
1769 if (cdata->algorithm) {
1770 saslc__error_set(ERR(sess), ERROR_MECH,
1771 "multiple algorithm in challenge");
1772 goto out;
1773 }
1774 if (strcasecmp(val, "md5-sess") != 0) {
1775 saslc__error_set(ERR(sess), ERROR_MECH,
1776 "algorithm != \"md5-sess\" in challenge");
1777 goto out;
1778 }
1779 cdata->algorithm = true;
1780 break;
1781 case CHALLENGE_CIPHER:
1782 /**************************************************/
1783 /* cipher-opts = "cipher" "=" <"> 1#cipher-val <">*/
1784 /* cipher-val = "3des" | "des" | "rc4-40" | */
1785 /* "rc4" |"rc4-56" | "aes" | */
1786 /* token */
1787 /* */
1788 /* This directive must be present exactly once if */
1789 /* "auth-conf" is offered in the "qop-options" */
1790 /* directive, in which case the "3des" cipher is */
1791 /* mandatory-to-implement. The client MUST ignore */
1792 /* unrecognized options; if the client recognizes */
1793 /* no option, it should abort the authentication */
1794 /* exchange. */
1795 /**************************************************/
1796 if (saslc__list_parse(&tmp_list, val) == -1)
1797 goto no_mem;
1798 saslc__list_log(tmp_list, "cipher list:\n");
1799 tmp_flags = cipher_list_flags(tmp_list);
1800 saslc__list_free(tmp_list);
1801 if (tmp_flags == 0) {
1802 saslc__error_set(ERR(sess), ERROR_MECH,
1803 "unknown cipher");
1804 goto out;
1805 }
1806 cdata->cipher_flags |= tmp_flags;
1807 break;
1808 case CHALLENGE_IGNORE:
1809 /**************************************************/
1810 /* auth-param = token "=" ( token | */
1811 /* quoted-string ) */
1812 /* */
1813 /* The client MUST ignore any unrecognized */
1814 /* directives. */
1815 /**************************************************/
1816 break;
1817 }
1818 }
1819
1820 /*
1821 * make sure realms are in iso8859-1
1822 */
1823 if (stringprep_realms(cdata->utf8, cdata->realm) == -1) {
1824 saslc__error_set(ERR(sess), ERROR_MECH,
1825 "unable to convert realms in challenge from "
1826 "\"utf-8\" to iso8859-1");
1827 goto out;
1828 }
1829
1830 /*
1831 * test for required options
1832 */
1833 if (cdata->nonce == NULL) {
1834 saslc__error_set(ERR(sess), ERROR_MECH,
1835 "nonce required in challenge");
1836 goto out;
1837 }
1838
1839 if (!cdata->algorithm) {
1840 saslc__error_set(ERR(sess), ERROR_MECH,
1841 "algorithm required in challenge");
1842 goto out;
1843 }
1844
1845 /*
1846 * set the default maxbuf value if it was missing from the
1847 * challenge.
1848 */
1849 if (cdata->maxbuf == 0)
1850 cdata->maxbuf = DEFAULT_MAXBUF;
1851
1852 saslc__msg_dbg("qop_flags=0x%04x\n", cdata->qop_flags);
1853 saslc__msg_dbg("cipher_flags=0x%04x\n", cdata->cipher_flags);
1854
1855 rv = 0;
1856 out:
1857 saslc__list_free(list);
1858 return rv;
1859 no_mem:
1860 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1861 goto out;
1862 }
1863
1864 /**
1865 * @brief creates digestmd5 mechanism session.
1866 * Function initializes also default options for the session.
1867 * @param sess sasl session
1868 * @return 0 on success, -1 on failure.
1869 */
1870 static int
saslc__mech_digestmd5_create(saslc_sess_t * sess)1871 saslc__mech_digestmd5_create(saslc_sess_t *sess)
1872 {
1873 saslc__mech_digestmd5_sess_t *c;
1874
1875 if ((c = calloc(1, sizeof(*c))) == NULL) {
1876 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1877 return -1;
1878 }
1879 c->rdata.nonce_cnt = 1;
1880 sess->mech_sess = c;
1881
1882 return 0;
1883 }
1884
1885 static void
free_cdata(cdata_t * cdata)1886 free_cdata(cdata_t *cdata)
1887 {
1888
1889 free(cdata->nonce);
1890 saslc__list_free(cdata->realm);
1891 }
1892
1893 static void
free_rdata(rdata_t * rdata)1894 free_rdata(rdata_t *rdata)
1895 {
1896
1897 free(rdata->authcid);
1898 free(rdata->authzid);
1899 free(rdata->cnonce);
1900 free(rdata->digesturi);
1901 if (rdata->passwd != NULL) {
1902 memset(rdata->passwd, 0, strlen(rdata->passwd));
1903 free(rdata->passwd);
1904 }
1905 free(rdata->realm);
1906 }
1907
1908 /**
1909 * @brief destroys digestmd5 mechanism session.
1910 * Function also is freeing assigned resources to the session.
1911 * @param sess sasl session
1912 * @return Functions always returns 0.
1913 */
1914 static int
saslc__mech_digestmd5_destroy(saslc_sess_t * sess)1915 saslc__mech_digestmd5_destroy(saslc_sess_t *sess)
1916 {
1917 saslc__mech_digestmd5_sess_t *ms;
1918
1919 ms = sess->mech_sess;
1920
1921 free_cdata(&ms->cdata);
1922 free_rdata(&ms->rdata);
1923
1924 saslc__buffer32_destroy(ms->dec_ctx.buf_ctx);
1925 saslc__buffer_destroy(ms->enc_ctx.buf_ctx);
1926
1927 cipher_context_destroy(ms->dec_ctx.cph_ctx);
1928 cipher_context_destroy(ms->enc_ctx.cph_ctx);
1929
1930 free(sess->mech_sess);
1931 sess->mech_sess = NULL;
1932
1933 return 0;
1934 }
1935
1936 /**
1937 * @brief collect the response data necessary to build the reply.
1938 * @param sess the session context
1939 * @return 0 on success, -1 on failure
1940 *
1941 * NOTE:
1942 * The input info is from the challenge (previously saved in cdata of
1943 * saslc__mech_digestmd5_sess_t) or from the property dictionaries.
1944 *
1945 * The output info is saved in (mostly) in rdata of the
1946 * saslc__mech_digestmd5_sess_t structure. The qop is special in that
1947 * it is exposed to the saslc__mech_sess_t layer.
1948 */
1949 static int
saslc__mech_digestmd5_response_data(saslc_sess_t * sess)1950 saslc__mech_digestmd5_response_data(saslc_sess_t *sess)
1951 {
1952 saslc__mech_digestmd5_sess_t *ms;
1953 cdata_t *cdata;
1954 rdata_t *rdata;
1955 const char *authcid;
1956 const char *authzid;
1957 const char *hostname;
1958 const char *maxbuf;
1959 const char *passwd;
1960 int rv;
1961
1962 ms = sess->mech_sess;
1963 cdata = &ms->cdata;
1964 rdata = &ms->rdata;
1965
1966 if ((rv = choose_qop(sess, cdata->qop_flags)) == -1)
1967 return -1; /* error message already set */
1968 ms->mech_sess.qop = rv;
1969
1970 if (ms->mech_sess.qop == QOP_CONF) {
1971 if ((rv = choose_cipher(sess, cdata->cipher_flags)) == -1)
1972 return -1; /* error message already set */
1973 rdata->cipher = rv;
1974 }
1975
1976 hostname = saslc_sess_getprop(sess, SASLC_DIGESTMD5_HOSTNAME);
1977 if (hostname == NULL) {
1978 saslc__error_set(ERR(sess), ERROR_MECH,
1979 "hostname is required for authentication");
1980 return -1;
1981 }
1982
1983 rdata->realm = choose_realm(sess, hostname, cdata->realm);
1984 if (rdata->realm == NULL)
1985 return -1; /* error message already set */
1986
1987 rdata->digesturi = saslc__mech_digestmd5_digesturi(sess, hostname);
1988 if (rdata->digesturi == NULL)
1989 return -1; /* error message already set */
1990
1991 authcid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHCID);
1992 if (authcid == NULL) {
1993 saslc__error_set(ERR(sess), ERROR_MECH,
1994 "authcid is required for an authentication");
1995 return -1;
1996 }
1997 rdata->authcid = strdup(authcid);
1998 if (rdata->authcid == NULL)
1999 goto no_mem;
2000
2001 authzid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHZID);
2002 if (authzid != NULL) {
2003 rdata->authzid = strdup(authzid);
2004 if (rdata->authzid == NULL)
2005 goto no_mem;
2006 }
2007
2008 passwd = saslc_sess_getprop(sess, SASLC_DIGESTMD5_PASSWD);
2009 if (passwd == NULL) {
2010 saslc__error_set(ERR(sess), ERROR_MECH,
2011 "password is required for an authentication");
2012 return -1;
2013 }
2014 rdata->passwd = strdup(passwd);
2015 if (rdata->passwd == NULL)
2016 goto no_mem;
2017
2018 rdata->cnonce = saslc__mech_digestmd5_nonce(NONCE_LEN);
2019 if (rdata->cnonce == NULL) {
2020 saslc__error_set(ERR(sess), ERROR_MECH,
2021 "failed to create cnonce");
2022 return -1;
2023 }
2024 #ifdef SASLC_DIGESTMD5_CNONCE /* XXX: for debugging! */
2025 {
2026 const char *cnonce;
2027 cnonce = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CNONCE);
2028 if (cnonce != NULL) {
2029 rdata->cnonce = strdup(cnonce);
2030 if (rdata->cnonce == NULL)
2031 goto no_mem;
2032 }
2033 }
2034 #endif
2035 if (ms->mech_sess.qop != QOP_NONE) {
2036 maxbuf = saslc_sess_getprop(sess, SASLC_DIGESTMD5_MAXBUF);
2037 if (maxbuf != NULL)
2038 rdata->maxbuf = (size_t)strtoul(maxbuf, NULL, 10);
2039 if (rdata->maxbuf == 0)
2040 rdata->maxbuf = cdata->maxbuf;
2041 if (INVALID_MAXBUF(rdata->maxbuf)) {
2042 saslc__error_set(ERR(sess), ERROR_MECH,
2043 "maxbuf out of range");
2044 return -1;
2045 }
2046 }
2047 return 0;
2048
2049 no_mem:
2050 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2051 return -1;
2052 }
2053
2054 /**
2055 * @brief compute the maximum payload that can go into an integrity or
2056 * confidentiality packet.
2057 * @param maxbuf the server's maxbuf size.
2058 * @param blksize the ciphers block size. 0 or 1 if there is no blocking.
2059 * @return the payload size
2060 *
2061 * The packet (not including the leading uint32_t packet length field)
2062 * has this structure:
2063 *
2064 * struct {
2065 * uint8_t payload[]; // packet payload
2066 * uint8_t padding[]; // padding to block size
2067 * uint8_t hmac_0_9[10]; // the first 10 bytes of the hmac
2068 * uint8_t version[2]; // version number (1) in BE format
2069 * uint8_t seqnum[4]; // sequence number in BE format
2070 * } __packed
2071 *
2072 * NOTE: if the block size is > 1, then padding is required to make
2073 * the {payload[], padding[], and hmac_0_9[]} a multiple of the block
2074 * size. Furthermore there must be at least one byte of padding! The
2075 * padding bytes are all set to the padding length and one byte of
2076 * padding is necessary to recover the padding length.
2077 */
2078 static size_t
maxpayload(size_t maxbuf,size_t blksize)2079 maxpayload(size_t maxbuf, size_t blksize)
2080 {
2081 size_t l;
2082
2083 if (blksize <= 1) { /* no padding used */
2084 if (maxbuf <= sizeof(md5hash_t))
2085 return 0;
2086
2087 return maxbuf - sizeof(md5hash_t);
2088 }
2089 if (maxbuf < 2 * blksize + 6)
2090 return 0;
2091
2092 l = rounddown(maxbuf - 6, blksize);
2093 if (l <= 10 + 1) /* we need at least one byte of padding */
2094 return 0;
2095
2096 return l - 10 - 1;
2097 }
2098
2099 /**
2100 * @brief initialize the encode and decode coder contexts for the session
2101 * @param sess the current session
2102 * @return 0 on success, -1 on failure.
2103 */
2104 static int
init_coder_context(saslc_sess_t * sess)2105 init_coder_context(saslc_sess_t *sess)
2106 {
2107 saslc__mech_digestmd5_sess_t *ms;
2108 size_t blksize;
2109 #ifdef SASLC_DIGESTMD5_SELFTEST
2110 int selftest; /* XXX: allow for testing against ourselves */
2111 #endif
2112
2113 ms = sess->mech_sess;
2114 #ifdef SASLC_DIGESTMD5_SELFTEST
2115 selftest = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SELFTEST) != NULL;
2116 #endif
2117 blksize = 0;
2118 switch (ms->mech_sess.qop) {
2119 case QOP_NONE:
2120 return 0;
2121 case QOP_INT:
2122 #ifdef SASLC_DIGESTMD5_SELFTEST
2123 ms->dec_ctx.key = selftest ? ms->keys.kic : ms->keys.kis;
2124 #else
2125 ms->dec_ctx.key = ms->keys.kis;
2126 #endif
2127 ms->enc_ctx.key = ms->keys.kic;
2128 ms->dec_ctx.cph_ctx = NULL;
2129 ms->enc_ctx.cph_ctx = NULL;
2130 break;
2131 case QOP_CONF:
2132 #ifdef SASLC_DIGESTMD5_SELFTEST
2133 ms->dec_ctx.key = selftest ? ms->keys.kcc : ms->keys.kcs;
2134 #else
2135 ms->dec_ctx.key = ms->keys.kcs;
2136 #endif
2137 ms->enc_ctx.key = ms->keys.kcc;
2138 ms->dec_ctx.cph_ctx = cipher_context_create(sess,
2139 ms->rdata.cipher, 0, ms->dec_ctx.key);
2140 if (ms->dec_ctx.cph_ctx == NULL)
2141 return -1;
2142
2143 ms->enc_ctx.cph_ctx = cipher_context_create(sess,
2144 ms->rdata.cipher, 1, ms->enc_ctx.key);
2145 if (ms->enc_ctx.cph_ctx == NULL)
2146 return -1;
2147
2148 blksize = ms->enc_ctx.cph_ctx->blksize;
2149 break;
2150 }
2151 ms->dec_ctx.sess = sess;
2152 ms->enc_ctx.sess = sess;
2153 ms->dec_ctx.buf_ctx = saslc__buffer32_create(sess, ms->rdata.maxbuf);
2154 if (ms->cdata.maxbuf < 2 * blksize + 6) {
2155 saslc__error_set(ERR(sess), ERROR_MECH,
2156 "server buffer too small for packet");
2157 return -1;
2158 }
2159 ms->enc_ctx.buf_ctx = saslc__buffer_create(sess,
2160 maxpayload(ms->cdata.maxbuf, blksize));
2161
2162 if (ms->dec_ctx.buf_ctx == NULL || ms->enc_ctx.buf_ctx == NULL) {
2163 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2164 return -1;
2165 }
2166 return 0;
2167 }
2168
2169 /**
2170 * @brief construct the reply string.
2171 * @param sess session context
2172 * @param response string
2173 * @return reply string or NULL on failure.
2174 */
2175 static char *
saslc__mech_digestmd5_reply(saslc_sess_t * sess,char * response)2176 saslc__mech_digestmd5_reply(saslc_sess_t *sess, char *response)
2177 {
2178 saslc__mech_digestmd5_sess_t *ms;
2179 char *out;
2180 char *cipher, *maxbuf, *realm;
2181
2182 ms = sess->mech_sess;
2183
2184 out = NULL;
2185 cipher = __UNCONST("");
2186 maxbuf = __UNCONST("");
2187 realm = __UNCONST("");
2188
2189 switch (ms->mech_sess.qop) {
2190 case QOP_CONF:
2191 if (asprintf(&cipher, "cipher=\"%s\",",
2192 cipher_name(ms->rdata.cipher)) == -1) {
2193 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2194 goto done;
2195 }
2196 /*FALLTHROUGH*/
2197 case QOP_INT:
2198 if (asprintf(&maxbuf, "maxbuf=%zu,", ms->rdata.maxbuf) == -1) {
2199 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2200 goto done;
2201 }
2202 break;
2203 case QOP_NONE:
2204 break;
2205 default:
2206 assert(/*CONSTCOND*/0);
2207 return NULL;
2208 }
2209 if (ms->rdata.realm != NULL &&
2210 asprintf(&realm, "realm=\"%s\",", ms->rdata.realm) == -1) {
2211 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2212 goto done;
2213 }
2214
2215 if (asprintf(&out,
2216 "username=\"%s\","
2217 "%s" /* realm= */
2218 "nonce=\"%s\","
2219 "cnonce=\"%s\","
2220 "nc=%08d,"
2221 "qop=%s,"
2222 "%s" /* cipher= */
2223 "%s" /* maxbuf= */
2224 "digest-uri=\"%s\","
2225 "response=%s",
2226 ms->rdata.authcid,
2227 realm,
2228 ms->cdata.nonce,
2229 ms->rdata.cnonce,
2230 ms->rdata.nonce_cnt,
2231 saslc__mech_qop_name(ms->mech_sess.qop),
2232 cipher,
2233 maxbuf,
2234 ms->rdata.digesturi,
2235 response
2236 ) == -1) {
2237 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2238 out = NULL;
2239 }
2240 done:
2241 if (realm[0] != '\0')
2242 free(realm);
2243 if (maxbuf[0] != '\0')
2244 free(maxbuf);
2245 if (cipher[0] != '\0')
2246 free(cipher);
2247
2248 return out;
2249 }
2250
2251 /**
2252 * @brief do one step of the sasl authentication
2253 * @param sess sasl session
2254 * @param in input data
2255 * @param inlen input data length
2256 * @param out place to store output data
2257 * @param outlen output data length
2258 * @return MECH_OK - authentication successful,
2259 * MECH_STEP - more steps are needed,
2260 * MECH_ERROR - error
2261 */
2262 static int
saslc__mech_digestmd5_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)2263 saslc__mech_digestmd5_cont(saslc_sess_t *sess, const void *in, size_t inlen,
2264 void **out, size_t *outlen)
2265 {
2266 saslc__mech_digestmd5_sess_t *ms;
2267 char *response;
2268 const char *p;
2269
2270 ms = sess->mech_sess;
2271
2272 switch(ms->mech_sess.step) {
2273 case 0:
2274 /* in case we are called before getting data from server */
2275 if (inlen == 0) {
2276 *out = NULL;
2277 *outlen = 0;
2278 return MECH_STEP;
2279 }
2280 /* if input data was provided, then doing the first step */
2281 ms->mech_sess.step++;
2282 /*FALLTHROUGH*/
2283 case 1:
2284 if (saslc__mech_digestmd5_parse_challenge(sess, in) == -1)
2285 return MECH_ERROR;
2286
2287 if (saslc__mech_digestmd5_response_data(sess) == -1)
2288 return MECH_ERROR;
2289
2290 if ((response = saslc__mech_digestmd5_response(ms,
2291 "AUTHENTICATE")) == NULL) {
2292 saslc__error_set(ERR(sess), ERROR_MECH,
2293 "unable to construct response");
2294 return MECH_ERROR;
2295 }
2296 *out = saslc__mech_digestmd5_reply(sess, response);
2297 free(response);
2298 if (*out == NULL)
2299 return MECH_ERROR;
2300
2301 *outlen = strlen(*out);
2302 return MECH_STEP;
2303 case 2:
2304 if ((response = saslc__mech_digestmd5_response(ms, ""))
2305 == NULL) {
2306 saslc__error_set(ERR(sess), ERROR_MECH,
2307 "unable to construct rspauth");
2308 return MECH_ERROR;
2309 }
2310 p = in;
2311 if (strncmp(p, "rspauth=", 8) != 0 ||
2312 strcmp(response, p + 8) != 0) {
2313 saslc__msg_dbg("rspauth='%s'\n", response);
2314 saslc__error_set(ERR(sess), ERROR_MECH,
2315 "failed to validate rspauth response");
2316 free(response);
2317 return MECH_ERROR;
2318 }
2319 free(response);
2320 if (init_coder_context(sess) == -1)
2321 return MECH_ERROR;
2322 *out = NULL;
2323 *outlen = 0;
2324 return MECH_OK;
2325 default:
2326 assert(/*CONSTCOND*/0); /* impossible */
2327 return MECH_ERROR;
2328 }
2329 }
2330
2331 /* mechanism definition */
2332 const saslc__mech_t saslc__mech_digestmd5 = {
2333 .name = "DIGEST-MD5",
2334 .flags = FLAG_MUTUAL | FLAG_DICTIONARY,
2335 .create = saslc__mech_digestmd5_create,
2336 .cont = saslc__mech_digestmd5_cont,
2337 .encode = saslc__mech_digestmd5_encode,
2338 .decode = saslc__mech_digestmd5_decode,
2339 .destroy = saslc__mech_digestmd5_destroy
2340 };
2341