xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/mech_digestmd5.c (revision bfb29ad1d2daebbeca2228a53b3042e83ab19362)
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