1 /* $NetBSD: mech_digestmd5.c,v 1.11 2013/06/28 15:04:35 joerg 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.11 2013/06/28 15:04:35 joerg 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;
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 	if ((p = strdup(realms->value)) == NULL)
930 		goto nomem;
931 	return p;
932  nomem:
933 	saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
934 	return NULL;
935 }
936 
937 /**
938  * @brief destroy a cipher context
939  * @param ctx cipher context
940  * @return nothing
941  */
942 static void
cipher_context_destroy(cipher_context_t * ctx)943 cipher_context_destroy(cipher_context_t *ctx)
944 {
945 
946 	if (ctx != NULL) {
947 		if (ctx->evp_ctx != NULL)
948 			EVP_CIPHER_CTX_free(ctx->evp_ctx);
949 		free(ctx);
950 	}
951 }
952 
953 /**
954  * @brief slide the bits from 7 bytes into the high 7 bits of 8 bites
955  * @param ikey input key
956  * @param okey output key
957  *
958  * This matches cyrus-sasl 2.1.23
959  */
960 static inline void
slidebits(uint8_t * ikey,uint8_t * okey)961 slidebits(uint8_t *ikey, uint8_t *okey)
962 {
963 
964 	okey[0] = ikey[0] << 0;
965 	okey[1] = ikey[0] << 7 | (unsigned)ikey[1] >> 1;
966 	okey[2] = ikey[1] << 6 | (unsigned)ikey[2] >> 2;
967 	okey[3] = ikey[2] << 5 | (unsigned)ikey[3] >> 3;
968 	okey[4] = ikey[3] << 4 | (unsigned)ikey[4] >> 4;
969 	okey[5] = ikey[4] << 3 | (unsigned)ikey[5] >> 5;
970 	okey[6] = ikey[5] << 2 | (unsigned)ikey[6] >> 6;
971 	okey[7] = ikey[6] << 1;
972 }
973 
974 /**
975  * @brief convert our key to a DES key
976  * @param key our key
977  * @param keylen our key length
978  * @param deskey the key in DES format
979  *
980  * NOTE: The openssl implementations of "des" and "3des" expect their
981  * keys to be in the high 7 bits of 8 bytes and 16 bytes,
982  * respectively.  Thus, our key length will be 7 and 14 bytes,
983  * respectively.
984  */
985 static void
make_deskey(uint8_t * key,size_t keylen,uint8_t * deskey)986 make_deskey(uint8_t *key, size_t keylen, uint8_t *deskey)
987 {
988 
989 	assert(keylen == 7 || keylen == 14);
990 
991 	slidebits(deskey + 0, key + 0);
992 	if (keylen == 14)
993 		slidebits(deskey + 7, key + 7);
994 }
995 
996 /**
997  * @brief create a cipher context, including EVP cipher initialization.
998  * @param sess session context
999  * @param cipher cipher to use
1000  * @param do_enc encode context if set, decode context if 0
1001  * @param key crypt key to use
1002  * @return cipher context, or NULL on error
1003  */
1004 static cipher_context_t *
cipher_context_create(saslc_sess_t * sess,cipher_t cipher,int do_enc,uint8_t * key)1005 cipher_context_create(saslc_sess_t *sess, cipher_t cipher, int do_enc, uint8_t *key)
1006 {
1007 #define AES_IV_MAGIC		"aes-128"
1008 #define AES_IV_MAGIC_LEN	(sizeof(AES_IV_MAGIC) - 1)
1009 	static const struct cipher_ctx_tbl_s {
1010 		cipher_t eval;			/* for error checking */
1011 		const EVP_CIPHER *(*evp_type)(void);/* type of cipher */
1012 		size_t keylen;			/* key length */
1013 		ssize_t blksize;		/* block size for cipher */
1014 		size_t ivlen;			/* initial value length */
1015 	} cipher_ctx_tbl[] = {
1016 		/* NB: table indexed by cipher_t */
1017 		/* eval		 evp_type	 keylen  blksize  ivlen */
1018 		{ CIPHER_DES,    EVP_des_cbc,       7,       8,      8 },
1019 		{ CIPHER_3DES,   EVP_des_ede_cbc,  14,       8,      8 },
1020 		{ CIPHER_RC4,    EVP_rc4,          16,       1,      0 },
1021 		{ CIPHER_RC4_40, EVP_rc4,           5,       1,      0 },
1022 		{ CIPHER_RC4_56, EVP_rc4,           7,       1,      0 },
1023 		{ CIPHER_AES,    EVP_aes_128_cbc,  16,      16,     16 }
1024 	};
1025 	const struct cipher_ctx_tbl_s *ctp;
1026 	char buf[sizeof(md5hash_t) + AES_IV_MAGIC_LEN];
1027 	uint8_t deskey[16];
1028 	md5hash_t aes_iv;		/* initial value buffer for aes */
1029 	cipher_context_t *ctx;		/* cipher context */
1030 	uint8_t *ivp;
1031 	const char *errmsg;
1032 	int rv;
1033 
1034 	/*************************************************************************/
1035 	/* See draft-ietf-sasl-rfc2831bis-02.txt section 2.4 (mentions "aes")    */
1036 	/* The key for the "rc4" and "aes" ciphers is all 16 bytes of Kcc or Kcs.*/
1037 	/* The key for the "rc4-40" cipher is the first 5 bytes of Kcc or Kcs.   */
1038 	/* The key for the "rc4-56" is the first 7 bytes of Kcc or Kcs.          */
1039 	/* The key for "des" is the first 7 bytes of Kcc or Kcs.                 */
1040 	/* The key for "3des" is the first 14 bytes of Kcc or Kcs.               */
1041 	/*                                                                       */
1042 	/* The IV used to send/receive the initial buffer of security encoded    */
1043 	/* data for "des" and "3des" is the last 8 bytes of Kcc or Kcs. For all  */
1044 	/* subsequent buffers the last 8 bytes of the ciphertext of the buffer   */
1045 	/* NNN is used as the IV for the buffer (NNN + 1).                       */
1046 	/*                                                                       */
1047 	/* The IV for the "aes" cipher in CBC mode for messages going from the   */
1048 	/* client to the server (IVc) consists of 16 bytes calculated as         */
1049 	/* follows: IVc = MD5({Kcc, "aes-128"})                                  */
1050 	/*                                                                       */
1051 	/* The IV for the "aes" cipher in CBC mode for messages going from the   */
1052 	/* server to the client (IVs) consists of 16 bytes calculated as         */
1053 	/* follows: IVs = MD5({Kcs, "aes-128"})                                  */
1054 	/*************************************************************************/
1055 
1056 	assert(cipher < __arraycount(cipher_ctx_tbl));
1057 	if (cipher >= __arraycount(cipher_ctx_tbl)) {
1058 		saslc__error_set_errno(ERR(sess), ERROR_BADARG);
1059 		return NULL;
1060 	}
1061 
1062 	ctx = malloc(sizeof(*ctx));
1063 	if (ctx == NULL) {
1064 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1065 		return NULL;
1066 	}
1067 
1068 	ctp = &cipher_ctx_tbl[cipher];
1069 	assert(ctp->eval == cipher);
1070 
1071 	ctx->blksize = ctp->blksize;
1072 
1073 	ctx->evp_ctx = EVP_CIPHER_CTX_new();
1074 	if (ctx->evp_ctx == NULL) {
1075 		errmsg = "EVP_CIPHER_CTX_new failed";
1076 		goto err;
1077 	}
1078 	if (EVP_CipherInit_ex(ctx->evp_ctx, ctp->evp_type(), NULL, NULL, NULL,
1079 								do_enc) == 0) {
1080 		errmsg = "EVP_CipherInit_ex failed";
1081 		goto err;
1082 	}
1083 	if (EVP_CIPHER_CTX_set_padding(ctx->evp_ctx, 0) == 0) {
1084 		errmsg = "EVP_CIPHER_CTX_set_padding failed";
1085 		goto err;
1086 	}
1087 	ivp = NULL;
1088 	switch (cipher) {	/* prepare key and IV */
1089 	case CIPHER_RC4:
1090 	case CIPHER_RC4_40:
1091 	case CIPHER_RC4_56:
1092 		assert(ctp->ivlen == 0);	/* no IV */
1093 		rv = EVP_CIPHER_CTX_set_key_length(ctx->evp_ctx,
1094 		    (int)ctp->keylen);
1095 		if (rv == 0) {
1096 			errmsg = "EVP_CIPHER_CTX_set_key_length failed";
1097 			goto err;
1098 		}
1099 		break;
1100 	case CIPHER_DES:
1101 	case CIPHER_3DES:
1102 		assert(ctp->ivlen == 8);
1103 		ivp = key + 8;
1104 		make_deskey(key, ctp->keylen, deskey);
1105 		key = deskey;
1106 		break;
1107 	case CIPHER_AES:
1108 		assert(ctp->ivlen == 16);
1109 		/* IVs = MD5({Kcs, "aes-128"}) */
1110 		memcpy(buf, key, sizeof(md5hash_t));
1111 		memcpy(buf + sizeof(md5hash_t), AES_IV_MAGIC, AES_IV_MAGIC_LEN);
1112 		saslc__crypto_md5_hash(buf, sizeof(buf), aes_iv);
1113 		ivp = aes_iv;
1114 		break;
1115 	}
1116 	if (EVP_CipherInit_ex(ctx->evp_ctx, NULL, NULL, key, ivp, do_enc) == 0) {
1117 		errmsg = "EVP_CipherInit_ex 2 failed";
1118 		goto err;
1119 	}
1120 	return ctx;
1121  err:
1122 	cipher_context_destroy(ctx);
1123 	saslc__error_set(ERR(sess), ERROR_MECH, errmsg);
1124 	return NULL;
1125 
1126 #undef AES_IV_MAGIC_LEN
1127 #undef AES_IV_MAGIC
1128 }
1129 
1130 /**
1131  * @brief compute the necessary padding length
1132  * @param ctx the cipher context
1133  * @param inlen the data length to put in the packet
1134  * @return the length of padding needed (zero if none needed)
1135  */
1136 static size_t
get_padlen(cipher_context_t * ctx,size_t inlen)1137 get_padlen(cipher_context_t *ctx, size_t inlen)
1138 {
1139 	size_t blksize;
1140 
1141 	if (ctx == NULL)
1142 		return 0;
1143 
1144 	blksize = ctx->blksize;
1145 	if (blksize == 1)
1146 		return 0;
1147 
1148 	return blksize - ((inlen + 10) % blksize);
1149 }
1150 
1151 /**
1152  * @brief compute the packet integrity including the version and
1153  * sequence number
1154  * @param key the hmac_md5 hash key
1155  * @param seqnum the sequence number
1156  * @param in the input buffer
1157  * @param inlen the input buffer length
1158  * @return 0 on success, -1 on failure
1159  */
1160 static int
packet_integrity(md5hash_t key,uint32_t seqnum,void * in,size_t inlen,md5hash_t mac)1161 packet_integrity(md5hash_t key, uint32_t seqnum, void *in, size_t inlen,
1162     md5hash_t mac)
1163 {
1164 
1165 	be32enc(in, seqnum);
1166 	if (saslc__crypto_hmac_md5_hash(key, MD5_DIGEST_LENGTH, in, inlen, mac)
1167 	    == -1)
1168 		return -1;
1169 
1170 	/* we keep only the first 10 bytes of the hash */
1171 	be16enc(mac + 10, 0x0001);	/* add 2 byte version number */
1172 	be32enc(mac + 12, seqnum);	/* add 4 byte sequence number */
1173 	return 0;
1174 }
1175 
1176 /**
1177  * @brief encode or decode a buffer (in place)
1178  * @param ctx the cipher context
1179  * @param in the input buffer
1180  * @param inlen the buffer length
1181  * @return the length of the result left in the input buffer after
1182  * processing, or -1 on failure.
1183  */
1184 static ssize_t
cipher_update(cipher_context_t * ctx,void * in,size_t inlen)1185 cipher_update(cipher_context_t *ctx, void *in, size_t inlen)
1186 {
1187 	int outl, rv;
1188 	void *out;
1189 
1190 	out = in; /* XXX: this assumes we can encoded and decode in place */
1191 	rv = EVP_CipherUpdate(ctx->evp_ctx, out, &outl, in, (int)inlen);
1192 	if (rv == 0)
1193 		return -1;
1194 
1195 	return outl;
1196 }
1197 
1198 /**
1199  * @brief incapsulate a message with confidentiality (sign and encrypt)
1200  * @param ctx coder context
1201  * @param in pointer to message to encode
1202  * @param inlen length of message
1203  * @param out encoded output packet (including prefixed 4 byte length field)
1204  * @param outlen decoded output packet length
1205  * @returns 0 on success, -1 on failure
1206  *
1207  * NOTE: this allocates memory for its output and the caller is
1208  * responsible for freeing it.
1209  *
1210  * integrity (auth-int):
1211  * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1212  *
1213  * confidentiality (auth-conf):
1214  * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1215  */
1216 static ssize_t
encode_buffer(coder_context_t * ctx,const void * in,size_t inlen,void ** out,size_t * outlen)1217 encode_buffer(coder_context_t *ctx, const void *in, size_t inlen,
1218     void **out, size_t *outlen)
1219 {
1220 	void *buf;
1221 	uint8_t *mac, *p;
1222 	ssize_t tmplen;
1223 	size_t buflen;
1224 	size_t padlen;
1225 
1226 	padlen = get_padlen(ctx->cph_ctx, inlen);
1227 	buflen = 4 + inlen + padlen + sizeof(md5hash_t);
1228 	buf = malloc(buflen);
1229 	if (buf == NULL) {
1230 		saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM);
1231 		return -1;
1232 	}
1233 	p = buf;
1234 	memcpy(p + 4, in, inlen);
1235 	mac = p + 4 + inlen + padlen;
1236 	if (packet_integrity(ctx->key, ctx->seqnum, buf, 4 + inlen, mac)
1237 	    == -1) {
1238 		saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed");
1239 		free(buf);
1240 		return -1;
1241 	}
1242 
1243 	if (padlen)
1244 		memset(p + 4 + inlen, (int)padlen, padlen);
1245 
1246 	if (ctx->cph_ctx != NULL) {
1247 		if ((tmplen = cipher_update(ctx->cph_ctx, p + 4,
1248 			 inlen + padlen + 10)) == -1) {
1249 			saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1250 			    "cipher error");
1251 			free(buf);
1252 			return -1;
1253 		}
1254 		assert((size_t)tmplen == inlen + padlen + 10);
1255 		if ((size_t)tmplen != inlen + padlen + 10)
1256 			return -1;
1257 	}
1258 
1259 	be32enc(buf, (uint32_t)(buflen - 4));
1260 
1261 	*out = buf;
1262 	*outlen = buflen;
1263 	ctx->seqnum++;		/* wraps at 2^32 */
1264 	return 0;
1265 }
1266 
1267 /**
1268  * @brief decode one complete confidentiality encoded packet
1269  * @param ctx coder context
1270  * @param in pointer to packet, including the beginning 4 byte length field.
1271  * @param inlen length of packet
1272  * @param out decoded output
1273  * @param outlen decoded output length
1274  * @returns 0 on success, -1 on failure
1275  *
1276  * NOTE: this modifies the intput buffer!
1277  * NOTE: this allocates memory for its output and the caller is
1278  * responsible for freeing it.
1279  *
1280  * integrity (auth-int):
1281  * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1282  *
1283  * confidentiality (auth-conf):
1284  * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1285  */
1286 static ssize_t
decode_buffer(coder_context_t * ctx,void * in,size_t inlen,void ** out,size_t * outlen)1287 decode_buffer(coder_context_t *ctx, void *in, size_t inlen,
1288     void **out, size_t *outlen)
1289 {
1290 	md5hash_t mac;
1291 	void *buf;
1292 	uint8_t *p;
1293 	size_t blksize, buflen, padlen;
1294 	ssize_t tmplen;
1295 	uint32_t len;
1296 
1297 	padlen = get_padlen(ctx->cph_ctx, 1);
1298 	if (inlen < 4 + 1 + padlen + MD5_DIGEST_LENGTH) {
1299 		saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1300 		    "zero payload packet");
1301 		return -1;
1302 	}
1303 	len = be32dec(in);
1304 	if (len + 4 != inlen) {
1305 		saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1306 		    "bad packet length");
1307 		return -1;
1308 	}
1309 
1310 	if (ctx->cph_ctx != NULL) {
1311 		p = in;
1312 		if ((tmplen = cipher_update(ctx->cph_ctx, p + 4, len - 6)) == -1) {
1313 			saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1314 			    "cipher error");
1315 			return -1;
1316 		}
1317 		assert(tmplen == (ssize_t)len - 6);
1318 		if (tmplen != (ssize_t)len - 6)
1319 			return -1;
1320 	}
1321 
1322 	blksize = ctx->cph_ctx ? ctx->cph_ctx->blksize : 0;
1323 	if (blksize <= 1)
1324 		padlen = 0;
1325 	else{
1326 		p = in;
1327 		padlen = p[inlen - sizeof(md5hash_t) - 1];
1328 		if (padlen > blksize || padlen == 0) {
1329 			saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1330 			    "invalid padding length after decode");
1331 			return -1;
1332 		}
1333 	}
1334 	if (packet_integrity(ctx->key, ctx->seqnum, in,
1335 				inlen - padlen - sizeof(mac), mac) == -1) {
1336 		saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed");
1337 		return -1;
1338 	}
1339 
1340 	p = in;
1341 	p += 4 + len - MD5_DIGEST_LENGTH;
1342 	if (memcmp(p, mac, MD5_DIGEST_LENGTH) != 0) {
1343 		uint32_t seqnum;
1344 
1345 		p = in;
1346 		seqnum = be32dec(p + inlen - 4);
1347 		saslc__error_set(ERR(ctx->sess), ERROR_MECH,
1348 		    seqnum != ctx->seqnum ? "invalid MAC (bad seqnum)" :
1349 		    "invalid MAC");
1350 		return -1;
1351 	}
1352 
1353 	buflen = len - padlen - MD5_DIGEST_LENGTH;
1354 	buf = malloc(buflen);
1355 	if (buf == NULL) {
1356 		saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM);
1357 		return -1;
1358 	}
1359 	p = in;
1360 	p += 4;
1361 	memcpy(buf, p, buflen);
1362 
1363 	*out = buf;
1364 	*outlen = buflen;
1365 	ctx->seqnum++;
1366 	return 0;
1367 }
1368 
1369 /**
1370  * @brief add integrity or confidentiality layer
1371  * @param sess session handle
1372  * @param in input buffer
1373  * @param inlen input buffer length
1374  * @param out pointer to output buffer
1375  * @param out pointer to output buffer length
1376  * @return number of bytes consumed on success, 0 if insufficient data
1377  * to process, -1 on failure
1378  */
1379 static ssize_t
saslc__mech_digestmd5_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)1380 saslc__mech_digestmd5_encode(saslc_sess_t *sess, const void *in, size_t inlen,
1381     void **out, size_t *outlen)
1382 {
1383 	saslc__mech_digestmd5_sess_t *ms;
1384 	uint8_t *buf;
1385 	size_t buflen;
1386 	ssize_t rval;
1387 
1388 	ms = sess->mech_sess;
1389 	assert(ms->mech_sess.qop != QOP_NONE);
1390 	if (ms->mech_sess.qop == QOP_NONE)
1391 		return -1;
1392 
1393 	rval = saslc__buffer_fetch(ms->enc_ctx.buf_ctx, in, inlen, &buf, &buflen);
1394 	if (rval == -1)
1395 		return -1;
1396 	if (buflen == 0) {
1397 		*out = NULL;
1398 		*outlen = 0;
1399 		return rval;
1400 	}
1401 	if (encode_buffer(&ms->enc_ctx, buf, buflen, out, outlen) == -1)
1402 		return -1;
1403 
1404 	return rval;
1405 }
1406 
1407 /**
1408  * @brief remove integrity or confidentiality layer
1409  * @param sess session handle
1410  * @param in input buffer
1411  * @param inlen input buffer length
1412  * @param out pointer to output buffer
1413  * @param out pointer to output buffer length
1414  * @return number of bytes consumed on success, 0 if insufficient data
1415  * to process, -1 on failure
1416  *
1417  * integrity (auth-int):
1418  * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1419  *
1420  * confidentiality (auth-conf):
1421  * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1422  */
1423 static ssize_t
saslc__mech_digestmd5_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)1424 saslc__mech_digestmd5_decode(saslc_sess_t *sess, const void *in, size_t inlen,
1425     void **out, size_t *outlen)
1426 {
1427 	saslc__mech_digestmd5_sess_t *ms;
1428 	uint8_t *buf;
1429 	size_t buflen;
1430 	ssize_t rval;
1431 
1432 	ms = sess->mech_sess;
1433 	assert(ms->mech_sess.qop != QOP_NONE);
1434 	if (ms->mech_sess.qop == QOP_NONE)
1435 		return -1;
1436 
1437 	rval = saslc__buffer32_fetch(ms->dec_ctx.buf_ctx, in, inlen, &buf, &buflen);
1438 	if (rval == -1)
1439 		return -1;
1440 
1441 	if (buflen == 0) {
1442 		*out = NULL;
1443 		*outlen = 0;
1444 		return rval;
1445 	}
1446 	if (decode_buffer(&ms->dec_ctx, buf, buflen, out, outlen) == -1)
1447 		return -1;
1448 
1449 	return rval;
1450 }
1451 
1452 /************************************************************************
1453  * XXX: Share with mech_gssapi.c?  They are almost identical.
1454  */
1455 /**
1456  * @brief choose the best qop based on what was provided by the
1457  * challenge and a possible user mask.
1458  * @param sess the session context
1459  * @param qop_flags the qop flags parsed from the challenge string
1460  * @return the selected saslc__mech_sess_qop_t or -1 if no match
1461  */
1462 static int
choose_qop(saslc_sess_t * sess,uint32_t qop_flags)1463 choose_qop(saslc_sess_t *sess, uint32_t qop_flags)
1464 {
1465 	list_t *list;
1466 	const char *user_qop;
1467 
1468 	if (qop_flags == 0)	/* no qop spec in challenge (it's optional) */
1469 		return QOP_NONE;
1470 
1471 	qop_flags &= DEFAULT_QOP_MASK;
1472 	user_qop = saslc_sess_getprop(sess, SASLC_DIGESTMD5_QOPMASK);
1473 	if (user_qop != NULL) {
1474 		if (saslc__list_parse(&list, user_qop) == -1) {
1475 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1476 			return -1;
1477 		}
1478 		qop_flags &= saslc__mech_qop_list_flags(list);
1479 		saslc__list_free(list);
1480 	}
1481 
1482 	/*
1483 	 * Select the most secure supported qop.
1484 	 */
1485 	if ((qop_flags & F_QOP_CONF) != 0)
1486 		return QOP_CONF;
1487 	if ((qop_flags & F_QOP_INT) != 0)
1488 		return QOP_INT;
1489 	if ((qop_flags & F_QOP_NONE) != 0)
1490 		return QOP_NONE;
1491 
1492 	saslc__error_set(ERR(sess), ERROR_MECH,
1493 	    "cannot choose an acceptable qop");
1494 	return -1;
1495 }
1496 /************************************************************************/
1497 
1498 /**
1499  * @brief choose the best cipher based on what was provided by the
1500  * challenge and a possible user mask.
1501  * @param sess the session context
1502  * @param cipher_flags the cipher flags parsed from the challenge
1503  * string
1504  * @return the selected cipher_t
1505  */
1506 static int
choose_cipher(saslc_sess_t * sess,unsigned int cipher_flags)1507 choose_cipher(saslc_sess_t *sess, unsigned int cipher_flags)
1508 {
1509 	list_t *list;
1510 	unsigned int cipher_mask;
1511 	const char *user_cipher;
1512 
1513 	if (cipher_flags == 0) {
1514 		saslc__error_set(ERR(sess), ERROR_MECH,
1515 		    "no cipher spec in challenge");
1516 		return -1;
1517 	}
1518 	cipher_mask = DEFAULT_CIPHER_MASK;
1519 	user_cipher = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CIPHERMASK);
1520 	if (user_cipher != NULL) {
1521 		if (saslc__list_parse(&list, user_cipher) == -1) {
1522 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1523 			return -1;
1524 		}
1525 		cipher_mask = cipher_list_flags(list);
1526 		saslc__list_free(list);
1527 	}
1528 	cipher_flags &= cipher_mask;
1529 
1530 	/*
1531 	 * Select the most secure cipher supported.
1532 	 * XXX: Is the order here right?
1533 	 */
1534 	if ((cipher_flags & F_CIPHER_AES) != 0)
1535 		return CIPHER_AES;
1536 	if ((cipher_flags & F_CIPHER_3DES) != 0)
1537 		return CIPHER_3DES;
1538 	if ((cipher_flags & F_CIPHER_DES) != 0)
1539 		return CIPHER_DES;
1540 	if ((cipher_flags & F_CIPHER_RC4) != 0)
1541 		return CIPHER_RC4;
1542 	if ((cipher_flags & F_CIPHER_RC4_56) != 0)
1543 		return CIPHER_RC4_56;
1544 	if ((cipher_flags & F_CIPHER_RC4_40) != 0)
1545 		return CIPHER_RC4_40;
1546 
1547 	saslc__error_set(ERR(sess), ERROR_MECH,
1548 	    "qop \"auth-conf\" requires a cipher");
1549 	return -1;
1550 }
1551 
1552 /**
1553  * @brief get the challenge_t value corresponding to a challenge key
1554  * string.
1555  * @param key challenge key string
1556  * @return the challenge_t value including CHALLENGE_IGNORE (-1) if
1557  * the key is not recognized
1558  */
1559 static challenge_t
get_challenge_t(const char * key)1560 get_challenge_t(const char *key)
1561 {
1562 	static const struct {
1563 		const char *key;
1564 		challenge_t value;
1565 	} challenge_keys[] = {
1566 		{ "realm",	CHALLENGE_REALM     },
1567 		{ "nonce",	CHALLENGE_NONCE     },
1568 		{ "qop",	CHALLENGE_QOP       },
1569 		{ "stale",	CHALLENGE_STALE     },
1570 		{ "maxbuf",	CHALLENGE_MAXBUF    },
1571 		{ "charset",	CHALLENGE_CHARSET   },
1572 		{ "algorithm",	CHALLENGE_ALGORITHM },
1573 		{ "cipher",	CHALLENGE_CIPHER    }
1574 	};
1575 	size_t i;
1576 
1577 	for (i = 0; i < __arraycount(challenge_keys); i++) {
1578 		if (strcasecmp(key, challenge_keys[i].key) == 0)
1579 			return challenge_keys[i].value;
1580 	}
1581 	return CHALLENGE_IGNORE;
1582 }
1583 
1584 /**
1585  * @brief parses challenge and store result in mech_sess.
1586  * @param mech_sess session where parsed data will be stored
1587  * @param challenge challenge
1588  * @return 0 on success, -1 on failure.
1589  */
1590 static int
saslc__mech_digestmd5_parse_challenge(saslc_sess_t * sess,const char * challenge)1591 saslc__mech_digestmd5_parse_challenge(saslc_sess_t *sess, const char *challenge)
1592 {
1593 	saslc__mech_digestmd5_sess_t *ms;
1594 	list_t *list, *n;
1595 	list_t *tmp_list;
1596 	cdata_t *cdata;
1597 	size_t maxbuf;
1598 	uint32_t tmp_flags;
1599 	int rv;
1600 
1601 	/******************************************************************/
1602 	/* digest-challenge  =                                            */
1603 	/*     1#( realm | nonce | qop-options | stale | server_maxbuf |  */
1604 	/*      charset | algorithm | cipher-opts | auth-param )          */
1605 	/******************************************************************/
1606 
1607 	saslc__msg_dbg("challenge: '%s'\n", challenge);
1608 
1609 	ms = sess->mech_sess;
1610 	cdata = &ms->cdata;
1611 
1612 	rv = -1;
1613 	memset(cdata, 0, sizeof(*cdata));
1614 	if (saslc__list_parse(&list, challenge) == -1) {
1615 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1616 		return -1;
1617 	}
1618 	saslc__list_log(list, "parse list:\n");
1619 	for (n = list; n != NULL; n = n->next) {
1620 		char *key;
1621 		char *val;
1622 
1623 		/* Split string into key and val */
1624 		key = n->value;
1625 		val = strchr(key, '=');
1626 		if (val == NULL)
1627 			goto no_mem;
1628 		*val = '\0';
1629 		val = strip_quotes(val + 1);
1630 
1631 		saslc__msg_dbg("key='%s' val='%s'\n", key, val);
1632 		switch (get_challenge_t(key)) {
1633 		case CHALLENGE_REALM:
1634 			/**************************************************/
1635 			/* realm       = "realm" "=" <"> realm-value <">  */
1636 			/* realm-value = qdstr-val                        */
1637 			/*                                                */
1638 			/* This directive is optional; if not present,    */
1639 			/* the client SHOULD solicit it from the user or  */
1640 			/* be able to compute a default; a plausible      */
1641 			/* default might be the realm supplied by the     */
1642 			/* user when they logged in to the client system. */
1643 			/* Multiple realm directives are allowed, in      */
1644 			/* which case the user or client must choose one  */
1645 			/* as the realm for which to supply to username   */
1646 			/* and password.                                  */
1647 			/**************************************************/
1648 			if (saslc__list_append(&cdata->realm, val) == -1)
1649 				goto no_mem;
1650 			break;
1651 		case CHALLENGE_NONCE:
1652 			/**************************************************/
1653 			/* nonce       = "nonce" "=" <"> nonce-value <">  */
1654 			/* nonce-value = *qdtext                          */
1655 			/*                                                */
1656 			/* This directive is required and MUST appear     */
1657 			/* exactly once; if not present, or if multiple   */
1658 			/* instances are present, the client should abort */
1659 			/* the authentication exchange.                   */
1660 			/**************************************************/
1661 			if (cdata->nonce != NULL) {
1662 				saslc__error_set(ERR(sess), ERROR_MECH,
1663 				    "multiple nonce in challenge");
1664 				goto out;
1665 			}
1666 			cdata->nonce = strdup(val);
1667 			if (cdata->nonce == NULL)
1668 				goto no_mem;
1669 			break;
1670 		case CHALLENGE_QOP:
1671 			/**************************************************/
1672 			/* qop-options = "qop" "=" <"> qop-list <">       */
1673 			/* qop-list    = 1#qop-value                      */
1674 			/* qop-value   = "auth" | "auth-int" |            */
1675 			/*               "auth-conf" | token              */
1676 			/*                                                */
1677 			/* This directive is optional; if not present it  */
1678 			/* defaults to "auth".  The client MUST ignore    */
1679 			/* unrecognized options; if the client recognizes */
1680 			/* no option, it should abort the authentication  */
1681 			/* exchange.                                      */
1682 			/**************************************************/
1683 			if (saslc__list_parse(&tmp_list, val) == -1)
1684 				goto no_mem;
1685 			saslc__list_log(tmp_list, "qop list:\n");
1686 			tmp_flags = saslc__mech_qop_list_flags(tmp_list);
1687 			saslc__list_free(tmp_list);
1688 			if (tmp_flags == 0) {
1689 				saslc__error_set(ERR(sess), ERROR_MECH,
1690 				    "qop required in challenge");
1691 				goto out;
1692 			}
1693 			cdata->qop_flags |= tmp_flags;
1694 			break;
1695 		case CHALLENGE_STALE:
1696 			/**************************************************/
1697 			/* stale = "stale" "=" "true"                     */
1698 			/*                                                */
1699 			/* This directive may appear at most once; if     */
1700 			/* multiple instances are present, the client     */
1701 			/* should abort the authentication exchange.      */
1702 			/**************************************************/
1703 			if (cdata->stale) {
1704 				saslc__error_set(ERR(sess), ERROR_MECH,
1705 				    "multiple stale in challenge");
1706 				goto out;
1707 			}
1708 			if (strcasecmp(val, "true") != 0) {
1709 				saslc__error_set(ERR(sess), ERROR_MECH,
1710 				    "stale must be true");
1711 				goto out;
1712 			}
1713 			cdata->stale = true;
1714 			break;
1715 		case CHALLENGE_MAXBUF:
1716 			/**************************************************/
1717 			/* maxbuf-value = 1*DIGIT                         */
1718 			/*                                                */
1719 			/* The value MUST be bigger than 16 and smaller   */
1720 			/* or equal to 16777215 (i.e.  2**24-1). If this  */
1721 			/* directive is missing, the default value is     */
1722 			/* 65536. This directive may appear at most once; */
1723 			/* if multiple instances are present, the client  */
1724 			/* MUST abort the authentication exchange.        */
1725 			/**************************************************/
1726 			if (cdata->maxbuf != 0) {
1727 				saslc__error_set(ERR(sess), ERROR_MECH,
1728 				    "multiple maxbuf in challenge");
1729 				goto out;
1730 			}
1731 			maxbuf = (size_t)strtoul(val, NULL, 10);
1732 			if (INVALID_MAXBUF(maxbuf)) {
1733 				saslc__error_set(ERR(sess), ERROR_MECH,
1734 				    "invalid maxbuf in challenge");
1735 				goto out;
1736 			}
1737 			cdata->maxbuf = maxbuf;
1738 			break;
1739 		case CHALLENGE_CHARSET:
1740 			/**************************************************/
1741 			/* charset = "charset" "=" "utf-8"                */
1742 			/*                                                */
1743 			/* This directive may appear at most once; if     */
1744 			/* multiple instances are present, the client     */
1745 			/* should abort the authentication exchange.      */
1746 			/**************************************************/
1747 			if (cdata->utf8) {
1748 				saslc__error_set(ERR(sess), ERROR_MECH,
1749 				    "multiple charset in challenge");
1750 				goto out;
1751 			}
1752 			if (strcasecmp(val, "utf-8") != 0) {
1753 				saslc__error_set(ERR(sess), ERROR_MECH,
1754 				    "charset != \"utf-8\" in challenge");
1755 				goto out;
1756 			}
1757 			cdata->utf8 = true;
1758 			break;
1759 		case CHALLENGE_ALGORITHM:
1760 			/**************************************************/
1761 			/* algorithm = "algorithm" "=" "md5-sess"         */
1762 			/*                                                */
1763 			/* This directive is required and MUST appear     */
1764 			/* exactly once; if not present, or if multiple   */
1765 			/* instances are present, the client should abort */
1766 			/* the authentication exchange.                   */
1767 			/**************************************************/
1768 			if (cdata->algorithm) {
1769 				saslc__error_set(ERR(sess), ERROR_MECH,
1770 				    "multiple algorithm in challenge");
1771 				goto out;
1772 			}
1773 			if (strcasecmp(val, "md5-sess") != 0) {
1774 				saslc__error_set(ERR(sess), ERROR_MECH,
1775 				    "algorithm != \"md5-sess\" in challenge");
1776 				goto out;
1777 			}
1778 			cdata->algorithm = true;
1779 			break;
1780 		case CHALLENGE_CIPHER:
1781 			/**************************************************/
1782 			/* cipher-opts = "cipher" "=" <"> 1#cipher-val <">*/
1783 			/* cipher-val  = "3des" | "des" | "rc4-40" |      */
1784 			/*               "rc4" |"rc4-56" | "aes" |        */
1785 			/*               token                            */
1786 			/*                                                */
1787 			/* This directive must be present exactly once if */
1788 			/* "auth-conf" is offered in the "qop-options"    */
1789 			/* directive, in which case the "3des" cipher is  */
1790 			/* mandatory-to-implement. The client MUST ignore */
1791 			/* unrecognized options; if the client recognizes */
1792 			/* no option, it should abort the authentication  */
1793 			/* exchange.                                      */
1794 			/**************************************************/
1795 			if (saslc__list_parse(&tmp_list, val) == -1)
1796 				goto no_mem;
1797 			saslc__list_log(tmp_list, "cipher list:\n");
1798 			tmp_flags = cipher_list_flags(tmp_list);
1799 			saslc__list_free(tmp_list);
1800 			if (tmp_flags == 0) {
1801 				saslc__error_set(ERR(sess), ERROR_MECH,
1802 				    "unknown cipher");
1803 				goto out;
1804 			}
1805 			cdata->cipher_flags |= tmp_flags;
1806 			break;
1807 		case CHALLENGE_IGNORE:
1808 			/**************************************************/
1809 			/* auth-param = token "=" ( token |               */
1810 			/*                          quoted-string )       */
1811 			/*                                                */
1812 			/* The client MUST ignore any unrecognized        */
1813 			/* directives.                                    */
1814 			/**************************************************/
1815 			break;
1816 		}
1817 	}
1818 
1819 	/*
1820 	 * make sure realms are in iso8859-1
1821 	 */
1822 	if (stringprep_realms(cdata->utf8, cdata->realm) == -1) {
1823 		saslc__error_set(ERR(sess), ERROR_MECH,
1824 		    "unable to convert realms in challenge from "
1825 		    "\"utf-8\" to iso8859-1");
1826 		goto out;
1827 	}
1828 
1829 	/*
1830 	 * test for required options
1831 	 */
1832 	if (cdata->nonce == NULL) {
1833 		saslc__error_set(ERR(sess), ERROR_MECH,
1834 		    "nonce required in challenge");
1835 		goto out;
1836 	}
1837 
1838 	if (!cdata->algorithm) {
1839 		saslc__error_set(ERR(sess), ERROR_MECH,
1840 		    "algorithm required in challenge");
1841 		goto out;
1842 	}
1843 
1844 	/*
1845 	 * set the default maxbuf value if it was missing from the
1846 	 * challenge.
1847 	 */
1848 	if (cdata->maxbuf == 0)
1849 		cdata->maxbuf = DEFAULT_MAXBUF;
1850 
1851 	saslc__msg_dbg("qop_flags=0x%04x\n",    cdata->qop_flags);
1852 	saslc__msg_dbg("cipher_flags=0x%04x\n", cdata->cipher_flags);
1853 
1854 	rv = 0;
1855  out:
1856 	saslc__list_free(list);
1857 	return rv;
1858  no_mem:
1859 	saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1860 	goto out;
1861 }
1862 
1863 /**
1864  * @brief creates digestmd5 mechanism session.
1865  * Function initializes also default options for the session.
1866  * @param sess sasl session
1867  * @return 0 on success, -1 on failure.
1868  */
1869 static int
saslc__mech_digestmd5_create(saslc_sess_t * sess)1870 saslc__mech_digestmd5_create(saslc_sess_t *sess)
1871 {
1872 	saslc__mech_digestmd5_sess_t *c;
1873 
1874 	if ((c = calloc(1, sizeof(*c))) == NULL) {
1875 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
1876 		return -1;
1877 	}
1878 	c->rdata.nonce_cnt = 1;
1879 	sess->mech_sess = c;
1880 
1881 	return 0;
1882 }
1883 
1884 static void
free_cdata(cdata_t * cdata)1885 free_cdata(cdata_t *cdata)
1886 {
1887 
1888 	free(cdata->nonce);
1889 	saslc__list_free(cdata->realm);
1890 }
1891 
1892 static void
free_rdata(rdata_t * rdata)1893 free_rdata(rdata_t *rdata)
1894 {
1895 
1896 	free(rdata->authcid);
1897 	free(rdata->authzid);
1898 	free(rdata->cnonce);
1899 	free(rdata->digesturi);
1900 	if (rdata->passwd != NULL) {
1901 		memset(rdata->passwd, 0, strlen(rdata->passwd));
1902 		free(rdata->passwd);
1903 	}
1904 	free(rdata->realm);
1905 }
1906 
1907 /**
1908  * @brief destroys digestmd5 mechanism session.
1909  * Function also is freeing assigned resources to the session.
1910  * @param sess sasl session
1911  * @return Functions always returns 0.
1912  */
1913 static int
saslc__mech_digestmd5_destroy(saslc_sess_t * sess)1914 saslc__mech_digestmd5_destroy(saslc_sess_t *sess)
1915 {
1916 	saslc__mech_digestmd5_sess_t *ms;
1917 
1918 	ms = sess->mech_sess;
1919 
1920 	free_cdata(&ms->cdata);
1921 	free_rdata(&ms->rdata);
1922 
1923 	saslc__buffer32_destroy(ms->dec_ctx.buf_ctx);
1924 	saslc__buffer_destroy(ms->enc_ctx.buf_ctx);
1925 
1926 	cipher_context_destroy(ms->dec_ctx.cph_ctx);
1927 	cipher_context_destroy(ms->enc_ctx.cph_ctx);
1928 
1929 	free(sess->mech_sess);
1930 	sess->mech_sess = NULL;
1931 
1932 	return 0;
1933 }
1934 
1935 /**
1936  * @brief collect the response data necessary to build the reply.
1937  * @param sess the session context
1938  * @return 0 on success, -1 on failure
1939  *
1940  * NOTE:
1941  * The input info is from the challenge (previously saved in cdata of
1942  * saslc__mech_digestmd5_sess_t) or from the property dictionaries.
1943  *
1944  * The output info is saved in (mostly) in rdata of the
1945  * saslc__mech_digestmd5_sess_t structure.  The qop is special in that
1946  * it is exposed to the saslc__mech_sess_t layer.
1947  */
1948 static int
saslc__mech_digestmd5_response_data(saslc_sess_t * sess)1949 saslc__mech_digestmd5_response_data(saslc_sess_t *sess)
1950 {
1951 	saslc__mech_digestmd5_sess_t *ms;
1952 	cdata_t *cdata;
1953 	rdata_t *rdata;
1954 	const char *authcid;
1955 	const char *authzid;
1956 	const char *hostname;
1957 	const char *maxbuf;
1958 	const char *passwd;
1959 	int rv;
1960 
1961 	ms = sess->mech_sess;
1962 	cdata = &ms->cdata;
1963 	rdata = &ms->rdata;
1964 
1965 	if ((rv = choose_qop(sess, cdata->qop_flags)) == -1)
1966 		return -1;	/* error message already set */
1967 	ms->mech_sess.qop = rv;
1968 
1969 	if (ms->mech_sess.qop == QOP_CONF) {
1970 		if ((rv = choose_cipher(sess, cdata->cipher_flags)) == -1)
1971 			return -1;	/* error message already set */
1972 		rdata->cipher = rv;
1973 	}
1974 
1975 	hostname = saslc_sess_getprop(sess, SASLC_DIGESTMD5_HOSTNAME);
1976 	if (hostname == NULL) {
1977 		saslc__error_set(ERR(sess), ERROR_MECH,
1978 		    "hostname is required for authentication");
1979 		return -1;
1980 	}
1981 
1982 	rdata->realm = choose_realm(sess, hostname, cdata->realm);
1983 	if (rdata->realm == NULL)
1984 		return -1;	/* error message already set */
1985 
1986 	rdata->digesturi = saslc__mech_digestmd5_digesturi(sess, hostname);
1987 	if (rdata->digesturi == NULL)
1988 		return -1;	/* error message already set */
1989 
1990 	authcid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHCID);
1991 	if (authcid == NULL) {
1992 		saslc__error_set(ERR(sess), ERROR_MECH,
1993 		    "authcid is required for an authentication");
1994 		return -1;
1995 	}
1996 	rdata->authcid = strdup(authcid);
1997 	if (rdata->authcid == NULL)
1998 		goto no_mem;
1999 
2000 	authzid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHZID);
2001 	if (authzid != NULL) {
2002 		rdata->authzid = strdup(authzid);
2003 		if (rdata->authzid == NULL)
2004 			goto no_mem;
2005 	}
2006 
2007 	passwd = saslc_sess_getprop(sess, SASLC_DIGESTMD5_PASSWD);
2008 	if (passwd == NULL) {
2009 		saslc__error_set(ERR(sess), ERROR_MECH,
2010 		    "password is required for an authentication");
2011 		return -1;
2012 	}
2013 	rdata->passwd = strdup(passwd);
2014 	if (rdata->passwd == NULL)
2015 		goto no_mem;
2016 
2017 	rdata->cnonce = saslc__mech_digestmd5_nonce(NONCE_LEN);
2018 	if (rdata->cnonce == NULL) {
2019 		saslc__error_set(ERR(sess), ERROR_MECH,
2020 		    "failed to create cnonce");
2021 		return -1;
2022 	}
2023 #ifdef SASLC_DIGESTMD5_CNONCE	/* XXX: for debugging! */
2024 	{
2025 		const char *cnonce;
2026 		cnonce = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CNONCE);
2027 		if (cnonce != NULL) {
2028 			rdata->cnonce = strdup(cnonce);
2029 			if (rdata->cnonce == NULL)
2030 				goto no_mem;
2031 		}
2032 	}
2033 #endif
2034 	if (ms->mech_sess.qop != QOP_NONE) {
2035 		maxbuf = saslc_sess_getprop(sess, SASLC_DIGESTMD5_MAXBUF);
2036 		if (maxbuf != NULL)
2037 			rdata->maxbuf = (size_t)strtoul(maxbuf, NULL, 10);
2038 		if (rdata->maxbuf == 0)
2039 			rdata->maxbuf = cdata->maxbuf;
2040 		if (INVALID_MAXBUF(rdata->maxbuf)) {
2041 			saslc__error_set(ERR(sess), ERROR_MECH,
2042 			    "maxbuf out of range");
2043 			return -1;
2044 		}
2045 	}
2046 	return 0;
2047 
2048  no_mem:
2049 	saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2050 	return -1;
2051 }
2052 
2053 /**
2054  * @brief compute the maximum payload that can go into an integrity or
2055  * confidentiality packet.
2056  * @param maxbuf the server's maxbuf size.
2057  * @param blksize the ciphers block size.  0 or 1 if there is no blocking.
2058  * @return the payload size
2059  *
2060  * The packet (not including the leading uint32_t packet length field)
2061  * has this structure:
2062  *
2063  * struct {
2064  *    uint8_t payload[];	// packet payload
2065  *    uint8_t padding[];	// padding to block size
2066  *    uint8_t hmac_0_9[10];	// the first 10 bytes of the hmac
2067  *    uint8_t version[2];	// version number (1) in BE format
2068  *    uint8_t seqnum[4];	// sequence number in BE format
2069  * } __packed
2070  *
2071  * NOTE: if the block size is > 1, then padding is required to make
2072  * the {payload[], padding[], and hmac_0_9[]} a multiple of the block
2073  * size.  Furthermore there must be at least one byte of padding!  The
2074  * padding bytes are all set to the padding length and one byte of
2075  * padding is necessary to recover the padding length.
2076  */
2077 static size_t
maxpayload(size_t maxbuf,size_t blksize)2078 maxpayload(size_t maxbuf, size_t blksize)
2079 {
2080 	size_t l;
2081 
2082 	if (blksize <= 1) {	/* no padding used */
2083 		if (maxbuf <= sizeof(md5hash_t))
2084 			return 0;
2085 
2086 		return maxbuf - sizeof(md5hash_t);
2087 	}
2088 	if (maxbuf < 2 * blksize + 6)
2089 		return 0;
2090 
2091 	l = rounddown(maxbuf - 6, blksize);
2092 	if (l <= 10 + 1)	/* we need at least one byte of padding */
2093 		return 0;
2094 
2095 	return l - 10 - 1;
2096 }
2097 
2098 /**
2099  * @brief initialize the encode and decode coder contexts for the session
2100  * @param sess the current session
2101  * @return 0 on success, -1 on failure.
2102  */
2103 static int
init_coder_context(saslc_sess_t * sess)2104 init_coder_context(saslc_sess_t *sess)
2105 {
2106 	saslc__mech_digestmd5_sess_t *ms;
2107 	size_t blksize;
2108 #ifdef SASLC_DIGESTMD5_SELFTEST
2109 	int selftest;		/* XXX: allow for testing against ourselves */
2110 #endif
2111 
2112 	ms = sess->mech_sess;
2113 #ifdef SASLC_DIGESTMD5_SELFTEST
2114 	selftest = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SELFTEST) != NULL;
2115 #endif
2116 	blksize = 0;
2117 	switch (ms->mech_sess.qop) {
2118 	case QOP_NONE:
2119 		return 0;
2120 	case QOP_INT:
2121 #ifdef	SASLC_DIGESTMD5_SELFTEST
2122 		ms->dec_ctx.key = selftest ? ms->keys.kic : ms->keys.kis;
2123 #else
2124 		ms->dec_ctx.key = ms->keys.kis;
2125 #endif
2126 		ms->enc_ctx.key = ms->keys.kic;
2127 		ms->dec_ctx.cph_ctx = NULL;
2128 		ms->enc_ctx.cph_ctx = NULL;
2129 		break;
2130 	case QOP_CONF:
2131 #ifdef	SASLC_DIGESTMD5_SELFTEST
2132 		ms->dec_ctx.key = selftest ? ms->keys.kcc : ms->keys.kcs;
2133 #else
2134 		ms->dec_ctx.key = ms->keys.kcs;
2135 #endif
2136 		ms->enc_ctx.key = ms->keys.kcc;
2137 		ms->dec_ctx.cph_ctx = cipher_context_create(sess,
2138 		    ms->rdata.cipher, 0, ms->dec_ctx.key);
2139 		if (ms->dec_ctx.cph_ctx == NULL)
2140 			return -1;
2141 
2142 		ms->enc_ctx.cph_ctx = cipher_context_create(sess,
2143 		    ms->rdata.cipher, 1, ms->enc_ctx.key);
2144 		if (ms->enc_ctx.cph_ctx == NULL)
2145 			return -1;
2146 
2147 		blksize = ms->enc_ctx.cph_ctx->blksize;
2148 		break;
2149 	}
2150 	ms->dec_ctx.sess = sess;
2151 	ms->enc_ctx.sess = sess;
2152 	ms->dec_ctx.buf_ctx = saslc__buffer32_create(sess, ms->rdata.maxbuf);
2153 	if (ms->cdata.maxbuf < 2 * blksize + 6) {
2154 		saslc__error_set(ERR(sess), ERROR_MECH,
2155 		    "server buffer too small for packet");
2156 		return -1;
2157 	}
2158 	ms->enc_ctx.buf_ctx = saslc__buffer_create(sess,
2159 	    maxpayload(ms->cdata.maxbuf, blksize));
2160 
2161 	if (ms->dec_ctx.buf_ctx == NULL || ms->enc_ctx.buf_ctx == NULL) {
2162 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2163 		return -1;
2164 	}
2165 	return 0;
2166 }
2167 
2168 /**
2169  * @brief construct the reply string.
2170  * @param sess session context
2171  * @param response string
2172  * @return reply string or NULL on failure.
2173  */
2174 static char *
saslc__mech_digestmd5_reply(saslc_sess_t * sess,char * response)2175 saslc__mech_digestmd5_reply(saslc_sess_t *sess, char *response)
2176 {
2177 	saslc__mech_digestmd5_sess_t *ms;
2178 	char *out;
2179 	char *cipher, *maxbuf, *realm;
2180 
2181 	ms = sess->mech_sess;
2182 
2183 	out = NULL;
2184 	cipher = __UNCONST("");
2185 	maxbuf = __UNCONST("");
2186 	realm = __UNCONST("");
2187 
2188 	switch (ms->mech_sess.qop) {
2189 	case QOP_CONF:
2190 		if (asprintf(&cipher, "cipher=\"%s\",",
2191 				cipher_name(ms->rdata.cipher)) == -1) {
2192 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2193 			goto done;
2194 		}
2195 		/*FALLTHROUGH*/
2196 	case QOP_INT:
2197 		if (asprintf(&maxbuf, "maxbuf=%zu,", ms->rdata.maxbuf) == -1) {
2198 			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2199 			goto done;
2200 		}
2201 		break;
2202 	case QOP_NONE:
2203 		break;
2204 	default:
2205 		assert(/*CONSTCOND*/0);
2206 		return NULL;
2207 	}
2208 	if (ms->rdata.realm != NULL &&
2209 	    asprintf(&realm, "realm=\"%s\",", ms->rdata.realm) == -1) {
2210 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2211 		goto done;
2212 	}
2213 
2214 	if (asprintf(&out,
2215 		"username=\"%s\","
2216 		"%s"			/* realm= */
2217 		"nonce=\"%s\","
2218 		"cnonce=\"%s\","
2219 		"nc=%08d,"
2220 		"qop=%s,"
2221 		"%s"			/* cipher= */
2222 		"%s"			/* maxbuf= */
2223 		"digest-uri=\"%s\","
2224 		"response=%s",
2225 		ms->rdata.authcid,
2226 		realm,
2227 		ms->cdata.nonce,
2228 		ms->rdata.cnonce,
2229 		ms->rdata.nonce_cnt,
2230 		saslc__mech_qop_name(ms->mech_sess.qop),
2231 		cipher,
2232 		maxbuf,
2233 		ms->rdata.digesturi,
2234 		response
2235 		    ) == -1) {
2236 		saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
2237 		out = NULL;
2238 	}
2239  done:
2240 	if (realm[0] != '\0')
2241 		free(realm);
2242 	if (maxbuf[0] != '\0')
2243 		free(maxbuf);
2244 	if (cipher[0] != '\0')
2245 		free(cipher);
2246 
2247 	return out;
2248 }
2249 
2250 /**
2251  * @brief do one step of the sasl authentication
2252  * @param sess sasl session
2253  * @param in input data
2254  * @param inlen input data length
2255  * @param out place to store output data
2256  * @param outlen output data length
2257  * @return MECH_OK - authentication successful,
2258  * MECH_STEP - more steps are needed,
2259  * MECH_ERROR - error
2260  */
2261 static int
saslc__mech_digestmd5_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)2262 saslc__mech_digestmd5_cont(saslc_sess_t *sess, const void *in, size_t inlen,
2263     void **out, size_t *outlen)
2264 {
2265 	saslc__mech_digestmd5_sess_t *ms;
2266 	char *response;
2267 	const char *p;
2268 
2269 	ms = sess->mech_sess;
2270 
2271 	switch(ms->mech_sess.step) {
2272 	case 0:
2273 		/* in case we are called before getting data from server */
2274 		if (inlen == 0) {
2275 			*out = NULL;
2276 			*outlen = 0;
2277 			return MECH_STEP;
2278 		}
2279 		/* if input data was provided, then doing the first step */
2280 		ms->mech_sess.step++;
2281 		/*FALLTHROUGH*/
2282 	case 1:
2283 		if (saslc__mech_digestmd5_parse_challenge(sess, in) == -1)
2284 			return MECH_ERROR;
2285 
2286 		if (saslc__mech_digestmd5_response_data(sess) == -1)
2287 			return MECH_ERROR;
2288 
2289 		if ((response = saslc__mech_digestmd5_response(ms,
2290 			 "AUTHENTICATE")) == NULL) {
2291 			saslc__error_set(ERR(sess), ERROR_MECH,
2292 			    "unable to construct response");
2293 			return MECH_ERROR;
2294 		}
2295 		*out = saslc__mech_digestmd5_reply(sess, response);
2296 		free(response);
2297 		if (out == NULL)
2298 			return MECH_ERROR;
2299 
2300 		*outlen = strlen(*out);
2301 		return MECH_STEP;
2302 	case 2:
2303 		if ((response = saslc__mech_digestmd5_response(ms, ""))
2304 		    == NULL) {
2305 			saslc__error_set(ERR(sess), ERROR_MECH,
2306 			    "unable to construct rspauth");
2307 			return MECH_ERROR;
2308 		}
2309 		p = in;
2310 		if (strncmp(p, "rspauth=", 8) != 0 ||
2311 		    strcmp(response, p + 8) != 0) {
2312 			saslc__msg_dbg("rspauth='%s'\n", response);
2313 			saslc__error_set(ERR(sess), ERROR_MECH,
2314 			    "failed to validate rspauth response");
2315 			free(response);
2316 			return MECH_ERROR;
2317 		}
2318 		free(response);
2319 		if (init_coder_context(sess) == -1)
2320 			return MECH_ERROR;
2321 		*out = NULL;
2322 		*outlen = 0;
2323 		return MECH_OK;
2324 	default:
2325 		assert(/*CONSTCOND*/0); /* impossible */
2326 		return MECH_ERROR;
2327 	}
2328 }
2329 
2330 /* mechanism definition */
2331 const saslc__mech_t saslc__mech_digestmd5 = {
2332 	.name	 = "DIGEST-MD5",
2333 	.flags	 = FLAG_MUTUAL | FLAG_DICTIONARY,
2334 	.create	 = saslc__mech_digestmd5_create,
2335 	.cont	 = saslc__mech_digestmd5_cont,
2336 	.encode	 = saslc__mech_digestmd5_encode,
2337 	.decode	 = saslc__mech_digestmd5_decode,
2338 	.destroy = saslc__mech_digestmd5_destroy
2339 };
2340