1 /* DIGEST-MD5 SASL plugin
2  * Ken Murchison
3  * Rob Siemborski
4  * Tim Martin
5  * Alexey Melnikov
6  */
7 /*
8  * Copyright (c) 1998-2016 Carnegie Mellon University.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in
19  *    the documentation and/or other materials provided with the
20  *    distribution.
21  *
22  * 3. The name "Carnegie Mellon University" must not be used to
23  *    endorse or promote products derived from this software without
24  *    prior written permission. For permission or any other legal
25  *    details, please contact
26  *      Carnegie Mellon University
27  *      Center for Technology Transfer and Enterprise Creation
28  *      4615 Forbes Avenue
29  *      Suite 302
30  *      Pittsburgh, PA  15213
31  *      (412) 268-7393, fax: (412) 268-7395
32  *      innovation@andrew.cmu.edu
33  *
34  * 4. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by Computing Services
37  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
38  *
39  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
40  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
41  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
42  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
44  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
45  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 #include <config.h>
49 
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #ifndef macintosh
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #endif
57 #include <fcntl.h>
58 #include <ctype.h>
59 
60 /* DES support */
61 #ifdef WITH_DES
62 # ifdef WITH_SSL_DES
63 #  include <openssl/des.h>
64 #  include <openssl/opensslv.h>
65 #  if (OPENSSL_VERSION_NUMBER >= 0x0090700f) && \
66       !defined(OPENSSL_ENABLE_OLD_DES_SUPPORT)
67 #   define des_cblock DES_cblock
68 #   define des_key_schedule DES_key_schedule
69 #   define des_key_sched(k,ks) \
70            DES_key_sched((k),&(ks))
71 #   define des_cbc_encrypt(i,o,l,k,iv,e) \
72            DES_cbc_encrypt((i),(o),(l),&(k),(iv),(e))
73 #   define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \
74            DES_ede2_cbc_encrypt((i),(o),(l),&(k1),&(k2),(iv),(e))
75 #  endif /* OpenSSL 0.9.7+ w/o old DES support */
76 # else /* system DES library */
77 #ifdef HAVE_DES_H
78 #  include <des.h>
79 #endif
80 # endif
81 #endif /* WITH_DES */
82 
83 #ifdef WIN32
84 # include <winsock2.h>
85 #else /* Unix */
86 # include <netinet/in.h>
87 #endif /* WIN32 */
88 
89 #include <sasl.h>
90 #include <saslplug.h>
91 
92 #include "plugin_common.h"
93 
94 #ifndef WIN32
95 extern int strcasecmp(const char *s1, const char *s2);
96 #endif /* end WIN32 */
97 
98 #ifdef macintosh
99 #include <sasl_md5_plugin_decl.h>
100 #endif
101 
102 /* external definitions */
103 
104 #define bool int
105 
106 #ifndef TRUE
107 #define TRUE  (1)
108 #define FALSE (0)
109 #endif
110 
111 /* MAX_UIN32_DIV_10 * 10 + MAX_UIN32_MOD_10 == 2^32-1 == 4294967295 */
112 #define MAX_UIN32_DIV_10    429496729
113 #define MAX_UIN32_MOD_10    5
114 
115 #define DEFAULT_BUFSIZE	    0xFFFF
116 #define MAX_SASL_BUFSIZE    0xFFFFFF
117 
118 /*****************************  Common Section  *****************************/
119 
120 /* Definitions */
121 #define NONCE_SIZE (32)		/* arbitrary */
122 
123 /* Layer Flags */
124 #define DIGEST_NOLAYER    (1)
125 #define DIGEST_INTEGRITY  (2)
126 #define DIGEST_PRIVACY    (4)
127 
128 /* defines */
129 #define HASHLEN 16
130 typedef unsigned char HASH[HASHLEN + 1];
131 #define HASHHEXLEN 32
132 typedef unsigned char HASHHEX[HASHHEXLEN + 1];
133 
134 #define MAC_SIZE 10
135 #define MAC_OFFS 2
136 
137 const char *SEALING_CLIENT_SERVER="Digest H(A1) to client-to-server sealing key magic constant";
138 const char *SEALING_SERVER_CLIENT="Digest H(A1) to server-to-client sealing key magic constant";
139 
140 const char *SIGNING_CLIENT_SERVER="Digest session key to client-to-server signing key magic constant";
141 const char *SIGNING_SERVER_CLIENT="Digest session key to server-to-client signing key magic constant";
142 
143 #define HT	(9)
144 #define CR	(13)
145 #define LF	(10)
146 #define SP	(32)
147 #define DEL	(127)
148 
149 #define NEED_ESCAPING	"\"\\"
150 
151 #define REALM_CHAL_PREFIX	"Available realms:"
152 
153 static char *quote (char *str);
154 
155 struct context;
156 
157 /* function definitions for cipher encode/decode */
158 typedef int cipher_function_t(struct context *,
159 			      const char *,
160 			      unsigned,
161 			      unsigned char[],
162 			      char *,
163 			      unsigned *);
164 
165 typedef int cipher_init_t(struct context *, unsigned char [16],
166                                             unsigned char [16]);
167 typedef void cipher_free_t(struct context *);
168 
169 enum Context_type { SERVER = 0, CLIENT = 1 };
170 
171 typedef struct cipher_context cipher_context_t;
172 
173 /* cached auth info used for fast reauth */
174 typedef struct reauth_entry {
175     char *authid;
176     char *realm;
177     unsigned char *nonce;
178     unsigned int nonce_count;
179     unsigned char *cnonce;
180 
181     union {
182 	struct {
183 	    time_t timestamp;
184 	} s; /* server stuff */
185 
186 	struct {
187 	    char *serverFQDN;
188 	    int protection;
189 	    struct digest_cipher *cipher;
190 	    unsigned long server_maxbuf;
191 
192 	    /* for HTTP mode (RFC 2617) only */
193 	    char *algorithm;
194 	    unsigned char *opaque;
195 	} c; /* client stuff */
196     } u;
197 } reauth_entry_t;
198 
199 typedef struct reauth_cache {
200     /* static stuff */
201     enum Context_type i_am;	/* are we the client or server? */
202     time_t timeout;
203     void *mutex;
204     unsigned size;
205 
206     reauth_entry_t *e;		/* fixed-size hash table of entries */
207 } reauth_cache_t;
208 
209 /* global context for reauth use */
210 typedef struct digest_glob_context {
211    reauth_cache_t *reauth;
212 } digest_glob_context_t;
213 
214 /* context that stores info */
215 typedef struct context {
216     int state;			/* state in the authentication we are in */
217     enum Context_type i_am;	/* are we the client or server? */
218     int http_mode;    		/* use RFC 2617 compatible protocol? */
219 
220     reauth_cache_t *reauth;
221 
222     char *authid;
223     char *realm;
224     unsigned char *nonce;
225     unsigned int nonce_count;
226     unsigned char *cnonce;
227 
228     /* only used by the client */
229     char ** realms;
230     int realm_cnt;
231 
232     char *response_value;
233 
234     unsigned int seqnum;
235     unsigned int rec_seqnum;	/* for checking integrity */
236 
237     HASH Ki_send;
238     HASH Ki_receive;
239 
240     HASH HA1;			/* Kcc or Kcs */
241 
242     /* copy of utils from the params structures */
243     const sasl_utils_t *utils;
244 
245     /* For general use */
246     char *out_buf;
247     unsigned out_buf_len;
248 
249     /* for encoding/decoding */
250     buffer_info_t *enc_in_buf;
251     char *encode_buf, *decode_buf, *decode_packet_buf;
252     unsigned encode_buf_len, decode_buf_len, decode_packet_buf_len;
253 
254     decode_context_t decode_context;
255 
256     /* if privacy mode is used use these functions for encode and decode */
257     cipher_function_t *cipher_enc;
258     cipher_function_t *cipher_dec;
259     cipher_init_t *cipher_init;
260     cipher_free_t *cipher_free;
261     struct cipher_context *cipher_enc_context;
262     struct cipher_context *cipher_dec_context;
263 } context_t;
264 
265 struct digest_cipher {
266     char *name;
267     sasl_ssf_t ssf;
268     int n; /* bits to make privacy key */
269     int flag; /* a bitmask to make things easier for us */
270 
271     cipher_function_t *cipher_enc;
272     cipher_function_t *cipher_dec;
273     cipher_init_t *cipher_init;
274     cipher_free_t *cipher_free;
275 };
276 #if 0
277 static const unsigned char *COLON = ":";
278 #else
279 static const unsigned char COLON[] = { ':', '\0' };
280 #endif
281 /* Hashes a string to produce an unsigned short */
hash(const char * str)282 static unsigned hash(const char *str)
283 {
284     unsigned val = 0;
285     int i;
286 
287     while (str && *str) {
288 	i = (int) *str;
289 	val ^= i;
290 	val <<= 1;
291 	str++;
292     }
293 
294     return val;
295 }
296 
CvtHex(HASH Bin,HASHHEX Hex)297 static void CvtHex(HASH Bin, HASHHEX Hex)
298 {
299     unsigned short  i;
300     unsigned char   j;
301 
302     for (i = 0; i < HASHLEN; i++) {
303 	j = (Bin[i] >> 4) & 0xf;
304 	if (j <= 9)
305 	    Hex[i * 2] = (j + '0');
306 	else
307 	    Hex[i * 2] = (j + 'a' - 10);
308 	j = Bin[i] & 0xf;
309 	if (j <= 9)
310 	    Hex[i * 2 + 1] = (j + '0');
311 	else
312 	    Hex[i * 2 + 1] = (j + 'a' - 10);
313     }
314     Hex[HASHHEXLEN] = '\0';
315 }
316 
317 /*
318  * calculate request-digest/response-digest as per HTTP Digest spec
319  */
320 void
DigestCalcResponse(const sasl_utils_t * utils,HASHHEX HA1,unsigned char * pszNonce,unsigned int pszNonceCount,unsigned char * pszCNonce,unsigned char * pszQop,unsigned char * pszDigestUri,unsigned char * pszMethod,HASHHEX HEntity,HASHHEX Response)321 DigestCalcResponse(const sasl_utils_t * utils,
322 		   HASHHEX HA1,	/* HEX(H(A1)) */
323 		   unsigned char *pszNonce,	/* nonce from server */
324 		   unsigned int pszNonceCount,	/* 8 hex digits */
325 		   unsigned char *pszCNonce,	/* client nonce */
326 		   unsigned char *pszQop,	/* qop-value: "", "auth",
327 						 * "auth-int" */
328 		   unsigned char *pszDigestUri,	/* requested URL */
329 		   unsigned char *pszMethod,
330 		   HASHHEX HEntity,	/* H(entity body) if qop="auth-int" */
331 		   HASHHEX Response	/* request-digest or response-digest */
332     )
333 {
334     MD5_CTX         Md5Ctx;
335     HASH            HA2;
336     HASH            RespHash;
337     HASHHEX         HA2Hex;
338     unsigned char ncvalue[10];
339 
340     /* calculate H(A2) */
341     utils->MD5Init(&Md5Ctx);
342 
343     if (pszMethod != NULL) {
344 	utils->MD5Update(&Md5Ctx, pszMethod, (unsigned) strlen((char *) pszMethod));
345     }
346     utils->MD5Update(&Md5Ctx, (unsigned char *) COLON, 1);
347 
348     /* utils->MD5Update(&Md5Ctx, (unsigned char *) "AUTHENTICATE:", 13); */
349     utils->MD5Update(&Md5Ctx, pszDigestUri, (unsigned) strlen((char *) pszDigestUri));
350     if (strcasecmp((char *) pszQop, "auth") != 0) {
351 	/* append ":00000000000000000000000000000000" */
352 	utils->MD5Update(&Md5Ctx, COLON, 1);
353 	utils->MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
354     }
355     utils->MD5Final(HA2, &Md5Ctx);
356     CvtHex(HA2, HA2Hex);
357 
358     /* calculate response */
359     utils->MD5Init(&Md5Ctx);
360     utils->MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
361     utils->MD5Update(&Md5Ctx, COLON, 1);
362     utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce));
363     utils->MD5Update(&Md5Ctx, COLON, 1);
364     if (*pszQop) {
365 	sprintf((char *)ncvalue, "%08x", pszNonceCount);
366 	utils->MD5Update(&Md5Ctx, ncvalue, (unsigned) strlen((char *)ncvalue));
367 	utils->MD5Update(&Md5Ctx, COLON, 1);
368 	utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce));
369 	utils->MD5Update(&Md5Ctx, COLON, 1);
370 	utils->MD5Update(&Md5Ctx, pszQop, (unsigned) strlen((char *) pszQop));
371 	utils->MD5Update(&Md5Ctx, COLON, 1);
372     }
373     utils->MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
374     utils->MD5Final(RespHash, &Md5Ctx);
375     CvtHex(RespHash, Response);
376 }
377 
UTF8_In_8859_1(const unsigned char * base,size_t len)378 static bool UTF8_In_8859_1(const unsigned char *base, size_t len)
379 {
380     const unsigned char *scan, *end;
381 
382     end = base + len;
383     for (scan = base; scan < end; ++scan) {
384 	if (*scan > 0xC3)
385 	    break;			/* abort if outside 8859-1 */
386 	if (*scan >= 0xC0 && *scan <= 0xC3) {
387 	    if (++scan == end || *scan < 0x80 || *scan > 0xBF)
388 		break;
389 	}
390     }
391 
392     /* if scan >= end, then this is a 8859-1 string. */
393     return (scan >= end);
394 }
395 
396 /*
397  * if the string is entirely in the 8859-1 subset of UTF-8, then translate to
398  * 8859-1 prior to MD5
399  */
MD5_UTF8_8859_1(const sasl_utils_t * utils,MD5_CTX * ctx,bool In_ISO_8859_1,const unsigned char * base,int len)400 static void MD5_UTF8_8859_1(const sasl_utils_t * utils,
401 			    MD5_CTX * ctx,
402 			    bool In_ISO_8859_1,
403 			    const unsigned char *base,
404 			    int len)
405 {
406     const unsigned char *scan, *end;
407     unsigned char   cbuf;
408 
409     end = base + len;
410 
411     /* if we found a character outside 8859-1, don't alter string */
412     if (!In_ISO_8859_1) {
413 	utils->MD5Update(ctx, base, len);
414 	return;
415     }
416     /* convert to 8859-1 prior to applying hash */
417     do {
418 	for (scan = base; scan < end && *scan < 0xC0; ++scan);
419 	if (scan != base)
420 	    utils->MD5Update(ctx, base, (unsigned) (scan - base));
421 	if (scan + 1 >= end)
422 	    break;
423 	cbuf = ((scan[0] & 0x3) << 6) | (scan[1] & 0x3f);
424 	utils->MD5Update(ctx, &cbuf, 1);
425 	base = scan + 2;
426     }
427     while (base < end);
428 }
429 
430 /**
431  * Returns true if it mangled the username.
432  */
DigestCalcSecret(const sasl_utils_t * utils,unsigned char * pszUserName,unsigned char * pszRealm,unsigned char * Password,int PasswordLen,bool Ignore_8859,HASH HA1)433 static bool DigestCalcSecret(const sasl_utils_t * utils,
434 		            unsigned char *pszUserName,
435 		            unsigned char *pszRealm,
436 		            unsigned char *Password,
437 		            int PasswordLen,
438 		            bool Ignore_8859,
439 		            HASH HA1)
440 {
441     bool            In_8859_1;
442     bool            Any_8859_1 = FALSE;
443     MD5_CTX         Md5Ctx;
444 
445     /* Chris Newman clarified that the following text in DIGEST-MD5 spec
446        is bogus: "if name and password are both in ISO 8859-1 charset"
447        We shoud use code example instead */
448 
449     utils->MD5Init(&Md5Ctx);
450 
451     /* We have to convert UTF-8 to ISO-8859-1 if possible */
452     if (Ignore_8859 == FALSE) {
453 	In_8859_1 = UTF8_In_8859_1(pszUserName, strlen((char *) pszUserName));
454 	MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1,
455 			pszUserName, (unsigned) strlen((char *) pszUserName));
456 	Any_8859_1 |= In_8859_1;
457     } else {
458 	utils->MD5Update(&Md5Ctx, pszUserName, (unsigned) strlen((char *) pszUserName));
459     }
460 
461     utils->MD5Update(&Md5Ctx, COLON, 1);
462 
463     /* a NULL realm is equivalent to the empty string */
464     if (pszRealm != NULL && pszRealm[0] != '\0') {
465 	if (Ignore_8859 == FALSE) {
466 	    /* We have to convert UTF-8 to ISO-8859-1 if possible */
467 	    In_8859_1 = UTF8_In_8859_1(pszRealm, strlen((char *) pszRealm));
468 	    MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1,
469 			    pszRealm, (unsigned) strlen((char *) pszRealm));
470 	    Any_8859_1 |= In_8859_1;
471 	} else {
472 	   utils->MD5Update(&Md5Ctx, pszRealm, (unsigned) strlen((char *) pszRealm));
473 	}
474     }
475 
476     utils->MD5Update(&Md5Ctx, COLON, 1);
477 
478     if (Ignore_8859 == FALSE) {
479 	/* We have to convert UTF-8 to ISO-8859-1 if possible */
480 	In_8859_1 = UTF8_In_8859_1(Password, PasswordLen);
481 	MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1,
482 			Password, PasswordLen);
483 	Any_8859_1 |= In_8859_1;
484     } else {
485 	utils->MD5Update(&Md5Ctx, Password, PasswordLen);
486     }
487     utils->MD5Final(HA1, &Md5Ctx);
488 
489     return Any_8859_1;
490 }
491 
create_nonce(const sasl_utils_t * utils)492 static unsigned char *create_nonce(const sasl_utils_t * utils)
493 {
494     unsigned char  *base64buf;
495     int             base64len;
496 
497     char           *ret = (char *) utils->malloc(NONCE_SIZE);
498     if (ret == NULL)
499 	return NULL;
500 
501     utils->rand(utils->rpool, (char *) ret, NONCE_SIZE);
502 
503     /* base 64 encode it so it has valid chars */
504     base64len = (NONCE_SIZE * 4 / 3) + (NONCE_SIZE % 3 ? 4 : 0);
505 
506     base64buf = (unsigned char *) utils->malloc(base64len + 1);
507     if (base64buf == NULL) {
508 	utils->seterror(utils->conn, 0, "Unable to allocate final buffer");
509 	return NULL;
510     }
511 
512     /*
513      * Returns SASL_OK on success, SASL_BUFOVER if result won't fit
514      */
515     if (utils->encode64(ret, NONCE_SIZE,
516 			(char *) base64buf, base64len, NULL) != SASL_OK) {
517 	utils->free(ret);
518 	return NULL;
519     }
520     utils->free(ret);
521 
522     return base64buf;
523 }
524 
add_to_challenge(const sasl_utils_t * utils,char ** str,unsigned * buflen,unsigned * curlen,char * name,unsigned char * value,bool need_quotes)525 static int add_to_challenge(const sasl_utils_t *utils,
526 			    char **str, unsigned *buflen, unsigned *curlen,
527 			    char *name,
528 			    unsigned char *value,
529 			    bool need_quotes)
530 {
531     size_t          namesize = strlen(name);
532     size_t          valuesize = strlen((char *) value);
533     unsigned        newlen;
534     int             ret;
535 
536     newlen = (unsigned) (*curlen + 1 + namesize + 2 + valuesize + 2);
537     ret = _plug_buf_alloc(utils, str, buflen, newlen);
538     if(ret != SASL_OK) return ret;
539 
540     if (*curlen > 0) {
541 	strcat(*str, ",");
542 	strcat(*str, name);
543     } else {
544 	strcpy(*str, name);
545     }
546 
547     if (need_quotes) {
548 	strcat(*str, "=\"");
549 
550 	/* Check if the value needs quoting */
551 	if (strpbrk ((char *)value, NEED_ESCAPING) != NULL) {
552 	    char * quoted = quote ((char *) value);
553 	    if (quoted == NULL)
554 		MEMERROR(utils);
555 	    valuesize = strlen(quoted);
556 	    /* As the quoted string is bigger, make sure we have enough
557 	       space now */
558 	    ret = _plug_buf_alloc(utils, str, buflen, newlen);
559 	    if (ret == SASL_OK) {
560 		strcat(*str, quoted);
561 		free (quoted);
562 	    } else {
563 		free (quoted);
564 		return ret;
565 	    }
566 	} else {
567 	    strcat(*str, (char *) value);
568 	}
569 	strcat(*str, "\"");
570     } else {
571 	strcat(*str, "=");
572 	strcat(*str, (char *) value);
573     }
574 
575     *curlen = newlen;
576     return SASL_OK;
577 }
578 
is_lws_char(char c)579 static int is_lws_char (char c)
580 {
581     return (c == ' ' || c == HT || c == CR || c == LF);
582 }
583 
skip_lws(char * s)584 static char *skip_lws (char *s)
585 {
586     if (!s) return NULL;
587 
588     /* skipping spaces: */
589     while (is_lws_char(s[0])) {
590 	if (s[0] == '\0') break;
591 	s++;
592     }
593 
594     return s;
595 }
596 
597 /* Same as skip_lws, but do this right to left */
598 /* skip LWSP at the end of the value (if any), skip_r_lws returns pointer to
599    the first LWSP character, NUL (if there were none) or NULL if the value
600    is entirely from LWSP characters */
skip_r_lws(char * s)601 static char *skip_r_lws (char *s)
602 {
603     char *end;
604     size_t len;
605 
606     if (!s) return NULL;
607 
608     len = strlen(s);
609     if (len == 0) return NULL;
610 
611     /* the last character before terminating NUL */
612     end = s + len - 1;
613 
614     /* skipping spaces: */
615     while (end > s && (end[0] == ' ' || end[0] == HT || end[0] == CR || end[0] == LF)) {
616 	end--;
617     }
618 
619     /* If all string from spaces, return NULL */
620     if (end == s && (end[0] == ' ' || end[0] == HT || end[0] == CR || end[0] == LF)) {
621 	return NULL;
622     } else {
623 	return (end + 1);
624     }
625 }
626 
skip_token(char * s,int caseinsensitive)627 static char *skip_token (char *s, int caseinsensitive)
628 {
629     if(!s) return NULL;
630 
631     while (s[0]>SP) {
632 	if (s[0]==DEL || s[0]=='(' || s[0]==')' || s[0]=='<' || s[0]=='>' ||
633 	    s[0]=='@' || s[0]==',' || s[0]==';' || s[0]==':' || s[0]=='\\' ||
634 	    s[0]=='\'' || s[0]=='/' || s[0]=='[' || s[0]==']' || s[0]== '?' ||
635 	    s[0]=='=' || s[0]== '{' || s[0]== '}') {
636 	    if (caseinsensitive == 1) {
637 		if (!isupper((unsigned char) s[0]))
638 		    break;
639 	    } else {
640 		break;
641 	    }
642 	}
643 	s++;
644     }
645     return s;
646 }
647 
648 /* Convert a string to 32 bit unsigned integer.
649    Any number of trailing spaces is allowed, but not a string
650    entirely comprised of spaces */
str2ul32(char * str,unsigned long * value)651 static bool str2ul32 (char *str, unsigned long * value)
652 {
653     unsigned int n;
654     char c;
655 
656     if (str == NULL) {
657 	return (FALSE);
658     }
659 
660     *value = 0;
661 
662     str = skip_lws (str);
663     if (str[0] == '\0') {
664 	return (FALSE);
665     }
666 
667     n = 0;
668     while (str[0] != '\0') {
669 	c = str[0];
670 	if (!isdigit((int)c)) {
671 	    return (FALSE);
672 	}
673 
674 /* Will overflow after adding additional digit */
675 	if (n > MAX_UIN32_DIV_10) {
676 	    return (FALSE);
677 	} else if (n == MAX_UIN32_DIV_10 && ((unsigned) (c - '0') > MAX_UIN32_MOD_10)) {
678 	    return (FALSE);
679 	}
680 
681 	n = n * 10 + (unsigned) (c - '0');
682 	str++;
683     }
684 
685     *value = n;
686     return (TRUE);
687 }
688 
689 /* NULL - error (unbalanced quotes),
690    otherwise pointer to the first character after the value.
691    The function performs work in place. */
unquote(char * qstr)692 static char *unquote (char *qstr)
693 {
694     char *endvalue;
695     int   escaped = 0;
696     char *outptr;
697 
698     if(!qstr) return NULL;
699 
700     if (qstr[0] == '"') {
701 	qstr++;
702 	outptr = qstr;
703 
704 	for (endvalue = qstr; endvalue[0] != '\0'; endvalue++, outptr++) {
705 	    if (escaped) {
706 		outptr[0] = endvalue[0];
707 		escaped = 0;
708 	    }
709 	    else if (endvalue[0] == '\\') {
710 		escaped = 1;
711 		outptr--; /* Will be incremented at the end of the loop */
712 	    }
713 	    else if (endvalue[0] == '"') {
714 		break;
715 	    }
716 	    else {
717 		outptr[0] = endvalue[0];
718 	    }
719 	}
720 
721 	if (endvalue[0] != '"') {
722 	    return NULL;
723 	}
724 
725 	while (outptr <= endvalue) {
726 	    outptr[0] = '\0';
727 	    outptr++;
728 	}
729 	endvalue++;
730     }
731     else { /* not qouted value (token) */
732 	/* qstr already contains output */
733 	endvalue = skip_token(qstr,0);
734     };
735 
736     return endvalue;
737 }
738 
739 /* Unlike unquote, this function returns an allocated quoted copy */
quote(char * str)740 static char *quote (char *str)
741 {
742     char *p;
743     char *outp;
744     char *result;
745     int num_to_escape;		/* How many characters need escaping */
746 
747     if (!str) return NULL;
748 
749     num_to_escape = 0;
750     p = strpbrk (str, NEED_ESCAPING);
751     while (p != NULL) {
752 	num_to_escape++;
753 	p = strpbrk (p + 1, NEED_ESCAPING);
754     }
755 
756     if (num_to_escape == 0) {
757 	return (strdup (str));
758     }
759 
760     result = malloc (strlen(str) + num_to_escape + 1);
761     if (result == NULL) {
762         return NULL;
763     }
764     for (p = str, outp = result; *p; p++) {
765 	if (*p == '"' || *p == '\\') {
766 	    *outp = '\\';
767 	    outp++;
768 	}
769 	*outp = *p;
770 	outp++;
771     }
772 
773     *outp = '\0';
774 
775     return (result);
776 }
777 
get_pair(char ** in,char ** name,char ** value)778 static void get_pair(char **in, char **name, char **value)
779 {
780     char  *endpair;
781     char  *curp = *in;
782     *name = NULL;
783     *value = NULL;
784 
785     if (curp == NULL) return;
786 
787     while (curp[0] != '\0') {
788 	/* skipping spaces: */
789 	curp = skip_lws(curp);
790 
791 	/* 'LWS "," LWS "," ...' is allowed by the DIGEST-MD5 ABNF */
792 	if (curp[0] == ',') {
793 	    curp++;
794 	} else {
795 	    break;
796 	}
797     }
798 
799     if (curp[0] == '\0') {
800 	/* End of the string is not an error */
801 	*name = "";
802 	return;
803     }
804 
805     *name = curp;
806 
807     curp = skip_token(curp,1);
808 
809     /* strip wierd chars */
810     if (curp[0] != '=' && curp[0] != '\0') {
811 	*curp++ = '\0';
812     };
813 
814     curp = skip_lws(curp);
815 
816     if (curp[0] != '=') { /* No '=' sign */
817 	*name = NULL;
818 	return;
819     }
820 
821     curp[0] = '\0';
822     curp++;
823 
824     curp = skip_lws(curp);
825 
826     *value = (curp[0] == '"') ? curp+1 : curp;
827 
828     endpair = unquote (curp);
829     if (endpair == NULL) { /* Unbalanced quotes */
830 	*name = NULL;
831 	*value = NULL;
832 	return;
833     }
834 
835     /* An optional LWS is allowed after the value. Skip it. */
836     if (is_lws_char (endpair[0])) {
837 	/* Remove the trailing LWS from the value */
838 	*endpair++ = '\0';
839 	endpair = skip_lws(endpair);
840     }
841 
842     /* syntax check: MUST be '\0' or ',' */
843     if (endpair[0] == ',') {
844 	endpair[0] = '\0';
845 	endpair++; /* skipping <,> */
846     } else if (endpair[0] != '\0') {
847 	*name = NULL;
848 	*value = NULL;
849 	return;
850     }
851 
852     *in = endpair;
853 }
854 
855 #ifdef WITH_DES
856 struct des_context_s {
857     des_key_schedule keysched;  /* key schedule for des initialization */
858     des_cblock ivec;            /* initial vector for encoding */
859     des_key_schedule keysched2; /* key schedule for 3des initialization */
860 };
861 
862 typedef struct des_context_s des_context_t;
863 
864 /* slide the first 7 bytes of 'inbuf' into the high seven bits of the
865    first 8 bytes of 'keybuf'. 'keybuf' better be 8 bytes long or longer. */
slidebits(unsigned char * keybuf,unsigned char * inbuf)866 static void slidebits(unsigned char *keybuf, unsigned char *inbuf)
867 {
868     keybuf[0] = inbuf[0];
869     keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1);
870     keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2);
871     keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3);
872     keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4);
873     keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5);
874     keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6);
875     keybuf[7] = (inbuf[6]<<1);
876 }
877 
878 /******************************
879  *
880  * 3DES functions
881  *
882  *****************************/
883 
dec_3des(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)884 static int dec_3des(context_t *text,
885 		    const char *input,
886 		    unsigned inputlen,
887 		    unsigned char digest[16] __attribute__((unused)),
888 		    char *output,
889 		    unsigned *outputlen)
890 {
891     des_context_t *c = (des_context_t *) text->cipher_dec_context;
892     int padding, p;
893 
894     des_ede2_cbc_encrypt((void *) input,
895 			 (void *) output,
896 			 inputlen,
897 			 c->keysched,
898 			 c->keysched2,
899 			 &c->ivec,
900 			 DES_DECRYPT);
901 
902     /* now chop off the padding */
903     padding = output[inputlen - 11];
904     if (padding < 1 || padding > 8) {
905 	/* invalid padding length */
906 	return SASL_FAIL;
907     }
908     /* verify all padding is correct */
909     for (p = 1; p <= padding; p++) {
910 	if (output[inputlen - 10 - p] != padding) {
911 	    return SASL_FAIL;
912 	}
913     }
914 
915     /* chop off the padding */
916     *outputlen = inputlen - padding - 10;
917 
918     return SASL_OK;
919 }
920 
enc_3des(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)921 static int enc_3des(context_t *text,
922 		    const char *input,
923 		    unsigned inputlen,
924 		    unsigned char digest[16],
925 		    char *output,
926 		    unsigned *outputlen)
927 {
928     des_context_t *c = (des_context_t *) text->cipher_enc_context;
929     int len;
930     int paddinglen;
931 
932     /* determine padding length */
933     paddinglen = 8 - ((inputlen + 10) % 8);
934 
935     /* now construct the full stuff to be ciphered */
936     memcpy(output, input, inputlen);                /* text */
937     memset(output+inputlen, paddinglen, paddinglen);/* pad  */
938     memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */
939 
940     len=inputlen+paddinglen+10;
941 
942     des_ede2_cbc_encrypt((void *) output,
943 			 (void *) output,
944 			 len,
945 			 c->keysched,
946 			 c->keysched2,
947 			 &c->ivec,
948 			 DES_ENCRYPT);
949 
950     *outputlen=len;
951 
952     return SASL_OK;
953 }
954 
init_3des(context_t * text,unsigned char enckey[16],unsigned char deckey[16])955 static int init_3des(context_t *text,
956 		     unsigned char enckey[16],
957 		     unsigned char deckey[16])
958 {
959     des_context_t *c;
960     unsigned char keybuf[8];
961 
962     /* allocate enc & dec context */
963     c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t));
964     if (c == NULL) return SASL_NOMEM;
965 
966     /* setup enc context */
967     slidebits(keybuf, enckey);
968     if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0)
969 	return SASL_FAIL;
970 
971     slidebits(keybuf, enckey + 7);
972     if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0)
973 	return SASL_FAIL;
974     memcpy(c->ivec, ((char *) enckey) + 8, 8);
975 
976     text->cipher_enc_context = (cipher_context_t *) c;
977 
978     /* setup dec context */
979     c++;
980     slidebits(keybuf, deckey);
981     if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0)
982 	return SASL_FAIL;
983 
984     slidebits(keybuf, deckey + 7);
985     if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0)
986 	return SASL_FAIL;
987 
988     memcpy(c->ivec, ((char *) deckey) + 8, 8);
989 
990     text->cipher_dec_context = (cipher_context_t *) c;
991 
992     return SASL_OK;
993 }
994 
995 
996 /******************************
997  *
998  * DES functions
999  *
1000  *****************************/
1001 
dec_des(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)1002 static int dec_des(context_t *text,
1003 		   const char *input,
1004 		   unsigned inputlen,
1005 		   unsigned char digest[16] __attribute__((unused)),
1006 		   char *output,
1007 		   unsigned *outputlen)
1008 {
1009     des_context_t *c = (des_context_t *) text->cipher_dec_context;
1010     int p, padding = 0;
1011 
1012     des_cbc_encrypt((void *) input,
1013 		    (void *) output,
1014 		    inputlen,
1015 		    c->keysched,
1016 		    &c->ivec,
1017 		    DES_DECRYPT);
1018 
1019     /* Update the ivec (des_cbc_encrypt implementations tend to be broken in
1020        this way) */
1021     memcpy(c->ivec, input + (inputlen - 8), 8);
1022 
1023     /* now chop off the padding */
1024     padding = output[inputlen - 11];
1025     if (padding < 1 || padding > 8) {
1026 	/* invalid padding length */
1027 	return SASL_FAIL;
1028     }
1029     /* verify all padding is correct */
1030     for (p = 1; p <= padding; p++) {
1031 	if (output[inputlen - 10 - p] != padding) {
1032 	    return SASL_FAIL;
1033 	}
1034     }
1035 
1036     /* chop off the padding */
1037     *outputlen = inputlen - padding - 10;
1038 
1039     return SASL_OK;
1040 }
1041 
enc_des(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)1042 static int enc_des(context_t *text,
1043 		   const char *input,
1044 		   unsigned inputlen,
1045 		   unsigned char digest[16],
1046 		   char *output,
1047 		   unsigned *outputlen)
1048 {
1049     des_context_t *c = (des_context_t *) text->cipher_enc_context;
1050     int len;
1051     int paddinglen;
1052 
1053     /* determine padding length */
1054     paddinglen = 8 - ((inputlen+10) % 8);
1055 
1056     /* now construct the full stuff to be ciphered */
1057     memcpy(output, input, inputlen);                /* text */
1058     memset(output+inputlen, paddinglen, paddinglen);/* pad  */
1059     memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */
1060 
1061     len = inputlen + paddinglen + 10;
1062 
1063     des_cbc_encrypt((void *) output,
1064                     (void *) output,
1065                     len,
1066                     c->keysched,
1067                     &c->ivec,
1068                     DES_ENCRYPT);
1069 
1070     /* Update the ivec (des_cbc_encrypt implementations tend to be broken in
1071        this way) */
1072     memcpy(c->ivec, output + (len - 8), 8);
1073 
1074     *outputlen = len;
1075 
1076     return SASL_OK;
1077 }
1078 
init_des(context_t * text,unsigned char enckey[16],unsigned char deckey[16])1079 static int init_des(context_t *text,
1080 		    unsigned char enckey[16],
1081 		    unsigned char deckey[16])
1082 {
1083     des_context_t *c;
1084     unsigned char keybuf[8];
1085 
1086     /* allocate enc context */
1087     c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t));
1088     if (c == NULL) return SASL_NOMEM;
1089 
1090     /* setup enc context */
1091     slidebits(keybuf, enckey);
1092     des_key_sched((des_cblock *) keybuf, c->keysched);
1093 
1094     memcpy(c->ivec, ((char *) enckey) + 8, 8);
1095 
1096     text->cipher_enc_context = (cipher_context_t *) c;
1097 
1098     /* setup dec context */
1099     c++;
1100     slidebits(keybuf, deckey);
1101     des_key_sched((des_cblock *) keybuf, c->keysched);
1102 
1103     memcpy(c->ivec, ((char *) deckey) + 8, 8);
1104 
1105     text->cipher_dec_context = (cipher_context_t *) c;
1106 
1107     return SASL_OK;
1108 }
1109 
free_des(context_t * text)1110 static void free_des(context_t *text)
1111 {
1112     /* free des contextss. only cipher_enc_context needs to be free'd,
1113        since cipher_dec_context was allocated at the same time. */
1114     if (text->cipher_enc_context) text->utils->free(text->cipher_enc_context);
1115 }
1116 
1117 #endif /* WITH_DES */
1118 
1119 #ifdef WITH_RC4
1120 #ifdef HAVE_OPENSSL
1121 #include <openssl/evp.h>
1122 
free_rc4(context_t * text)1123 static void free_rc4(context_t *text)
1124 {
1125     if (text->cipher_enc_context) {
1126         EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->cipher_enc_context);
1127         text->cipher_enc_context = NULL;
1128     }
1129     if (text->cipher_dec_context) {
1130         EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *)text->cipher_dec_context);
1131         text->cipher_dec_context = NULL;
1132     }
1133 }
1134 
init_rc4(context_t * text,unsigned char enckey[16],unsigned char deckey[16])1135 static int init_rc4(context_t *text,
1136 		    unsigned char enckey[16],
1137 		    unsigned char deckey[16])
1138 {
1139     EVP_CIPHER_CTX *ctx;
1140     int rc;
1141 
1142     ctx = EVP_CIPHER_CTX_new();
1143     if (ctx == NULL) return SASL_NOMEM;
1144 
1145     rc = EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, enckey, NULL);
1146     if (rc != 1) return SASL_FAIL;
1147 
1148     text->cipher_enc_context = (void *)ctx;
1149 
1150     ctx = EVP_CIPHER_CTX_new();
1151     if (ctx == NULL) return SASL_NOMEM;
1152 
1153     rc = EVP_DecryptInit_ex(ctx, EVP_rc4(), NULL, deckey, NULL);
1154     if (rc != 1) return SASL_FAIL;
1155 
1156     text->cipher_dec_context = (void *)ctx;
1157 
1158     return SASL_OK;
1159 }
1160 
dec_rc4(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)1161 static int dec_rc4(context_t *text,
1162 		   const char *input,
1163 		   unsigned inputlen,
1164 		   unsigned char digest[16] __attribute__((unused)),
1165 		   char *output,
1166 		   unsigned *outputlen)
1167 {
1168     int len;
1169     int rc;
1170 
1171     /* decrypt the text part & HMAC */
1172     rc = EVP_DecryptUpdate((EVP_CIPHER_CTX *)text->cipher_dec_context,
1173                            (unsigned char *)output, &len,
1174                            (const unsigned char *)input, inputlen);
1175     if (rc != 1) return SASL_FAIL;
1176 
1177     *outputlen = len;
1178 
1179     rc = EVP_DecryptFinal_ex((EVP_CIPHER_CTX *)text->cipher_dec_context,
1180                              (unsigned char *)output + len, &len);
1181     if (rc != 1) return SASL_FAIL;
1182 
1183     *outputlen += len;
1184 
1185     /* subtract the HMAC to get the text length */
1186     *outputlen -= 10;
1187 
1188     return SASL_OK;
1189 }
1190 
enc_rc4(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)1191 static int enc_rc4(context_t *text,
1192 		   const char *input,
1193 		   unsigned inputlen,
1194 		   unsigned char digest[16],
1195 		   char *output,
1196 		   unsigned *outputlen)
1197 {
1198     int len;
1199     int rc;
1200     /* encrypt the text part */
1201     rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context,
1202                            (unsigned char *)output, &len,
1203                            (const unsigned char *)input, inputlen);
1204     if (rc != 1) return SASL_FAIL;
1205 
1206     *outputlen = len;
1207 
1208     /* encrypt the `MAC part */
1209     rc = EVP_EncryptUpdate((EVP_CIPHER_CTX *)text->cipher_enc_context,
1210                            (unsigned char *)output + *outputlen, &len,
1211                            digest, 10);
1212     if (rc != 1) return SASL_FAIL;
1213 
1214     *outputlen += len;
1215 
1216     rc = EVP_EncryptFinal_ex((EVP_CIPHER_CTX *)text->cipher_enc_context,
1217                              (unsigned char *)output + *outputlen, &len);
1218     if (rc != 1) return SASL_FAIL;
1219 
1220     *outputlen += len;
1221 
1222     return SASL_OK;
1223 }
1224 #else
1225 /* quick generic implementation of RC4 */
1226 struct rc4_context_s {
1227     unsigned char sbox[256];
1228     int i, j;
1229 };
1230 
1231 typedef struct rc4_context_s rc4_context_t;
1232 
rc4_init(rc4_context_t * text,const unsigned char * key,unsigned keylen)1233 static void rc4_init(rc4_context_t *text,
1234 		     const unsigned char *key,
1235 		     unsigned keylen)
1236 {
1237     int i, j;
1238 
1239     /* fill in linearly s0=0 s1=1... */
1240     for (i=0;i<256;i++)
1241 	text->sbox[i]=i;
1242 
1243     j=0;
1244     for (i = 0; i < 256; i++) {
1245 	unsigned char tmp;
1246 	/* j = (j + Si + Ki) mod 256 */
1247 	j = (j + text->sbox[i] + key[i % keylen]) % 256;
1248 
1249 	/* swap Si and Sj */
1250 	tmp = text->sbox[i];
1251 	text->sbox[i] = text->sbox[j];
1252 	text->sbox[j] = tmp;
1253     }
1254 
1255     /* counters initialized to 0 */
1256     text->i = 0;
1257     text->j = 0;
1258 }
1259 
rc4_encrypt(rc4_context_t * text,const char * input,char * output,unsigned len)1260 static void rc4_encrypt(rc4_context_t *text,
1261 			const char *input,
1262 			char *output,
1263 			unsigned len)
1264 {
1265     int tmp;
1266     int i = text->i;
1267     int j = text->j;
1268     int t;
1269     int K;
1270     const char *input_end = input + len;
1271 
1272     while (input < input_end) {
1273 	i = (i + 1) % 256;
1274 
1275 	j = (j + text->sbox[i]) % 256;
1276 
1277 	/* swap Si and Sj */
1278 	tmp = text->sbox[i];
1279 	text->sbox[i] = text->sbox[j];
1280 	text->sbox[j] = tmp;
1281 
1282 	t = (text->sbox[i] + text->sbox[j]) % 256;
1283 
1284 	K = text->sbox[t];
1285 
1286 	/* byte K is Xor'ed with plaintext */
1287 	*output++ = *input++ ^ K;
1288     }
1289 
1290     text->i = i;
1291     text->j = j;
1292 }
1293 
rc4_decrypt(rc4_context_t * text,const char * input,char * output,unsigned len)1294 static void rc4_decrypt(rc4_context_t *text,
1295 			const char *input,
1296 			char *output,
1297 			unsigned len)
1298 {
1299     int tmp;
1300     int i = text->i;
1301     int j = text->j;
1302     int t;
1303     int K;
1304     const char *input_end = input + len;
1305 
1306     while (input < input_end) {
1307 	i = (i + 1) % 256;
1308 
1309 	j = (j + text->sbox[i]) % 256;
1310 
1311 	/* swap Si and Sj */
1312 	tmp = text->sbox[i];
1313 	text->sbox[i] = text->sbox[j];
1314 	text->sbox[j] = tmp;
1315 
1316 	t = (text->sbox[i] + text->sbox[j]) % 256;
1317 
1318 	K = text->sbox[t];
1319 
1320 	/* byte K is Xor'ed with plaintext */
1321 	*output++ = *input++ ^ K;
1322     }
1323 
1324     text->i = i;
1325     text->j = j;
1326 }
1327 
free_rc4(context_t * text)1328 static void free_rc4(context_t *text)
1329 {
1330     /* free rc4 context structures */
1331 
1332     if (text->cipher_enc_context) {
1333         text->utils->free(text->cipher_enc_context);
1334         text->cipher_enc_context = NULL;
1335     }
1336     if (text->cipher_dec_context) {
1337         text->utils->free(text->cipher_dec_context);
1338         text->cipher_dec_context = NULL;
1339     }
1340 }
1341 
init_rc4(context_t * text,unsigned char enckey[16],unsigned char deckey[16])1342 static int init_rc4(context_t *text,
1343 		    unsigned char enckey[16],
1344 		    unsigned char deckey[16])
1345 {
1346     /* allocate rc4 context structures */
1347     text->cipher_enc_context=
1348 	(cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t));
1349     if (text->cipher_enc_context == NULL) return SASL_NOMEM;
1350 
1351     text->cipher_dec_context=
1352 	(cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t));
1353     if (text->cipher_dec_context == NULL) return SASL_NOMEM;
1354 
1355     /* initialize them */
1356     rc4_init((rc4_context_t *) text->cipher_enc_context,
1357              (const unsigned char *) enckey, 16);
1358     rc4_init((rc4_context_t *) text->cipher_dec_context,
1359              (const unsigned char *) deckey, 16);
1360 
1361     return SASL_OK;
1362 }
1363 
dec_rc4(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)1364 static int dec_rc4(context_t *text,
1365 		   const char *input,
1366 		   unsigned inputlen,
1367 		   unsigned char digest[16] __attribute__((unused)),
1368 		   char *output,
1369 		   unsigned *outputlen)
1370 {
1371     /* decrypt the text part & HMAC */
1372     rc4_decrypt((rc4_context_t *) text->cipher_dec_context,
1373                 input, output, inputlen);
1374 
1375     /* no padding so we just subtract the HMAC to get the text length */
1376     *outputlen = inputlen - 10;
1377 
1378     return SASL_OK;
1379 }
1380 
enc_rc4(context_t * text,const char * input,unsigned inputlen,unsigned char digest[16],char * output,unsigned * outputlen)1381 static int enc_rc4(context_t *text,
1382 		   const char *input,
1383 		   unsigned inputlen,
1384 		   unsigned char digest[16],
1385 		   char *output,
1386 		   unsigned *outputlen)
1387 {
1388     /* pad is zero */
1389     *outputlen = inputlen+10;
1390 
1391     /* encrypt the text part */
1392     rc4_encrypt((rc4_context_t *) text->cipher_enc_context,
1393                 input,
1394                 output,
1395                 inputlen);
1396 
1397     /* encrypt the HMAC part */
1398     rc4_encrypt((rc4_context_t *) text->cipher_enc_context,
1399                 (const char *) digest,
1400 		(output)+inputlen, 10);
1401 
1402     return SASL_OK;
1403 }
1404 #endif /* HAVE_OPENSSL */
1405 #endif /* WITH_RC4 */
1406 
1407 struct digest_cipher available_ciphers[] =
1408 {
1409 #ifdef WITH_RC4
1410     { "rc4-40", 40, 5, 0x01, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 },
1411     { "rc4-56", 56, 7, 0x02, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 },
1412     { "rc4", 128, 16, 0x04, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 },
1413 #endif
1414 #ifdef WITH_DES
1415     { "des", 55, 16, 0x08, &enc_des, &dec_des, &init_des, &free_des },
1416     { "3des", 112, 16, 0x10, &enc_3des, &dec_3des, &init_3des, &free_des },
1417 #endif
1418     { NULL, 0, 0, 0, NULL, NULL, NULL, NULL }
1419 };
1420 
create_layer_keys(context_t * text,const sasl_utils_t * utils,HASH key,int keylen,unsigned char enckey[16],unsigned char deckey[16])1421 static int create_layer_keys(context_t *text,
1422 			     const sasl_utils_t *utils,
1423 			     HASH key, int keylen,
1424 			     unsigned char enckey[16],
1425 			     unsigned char deckey[16])
1426 {
1427     MD5_CTX Md5Ctx;
1428 
1429     utils->log(utils->conn, SASL_LOG_DEBUG,
1430 	       "DIGEST-MD5 create_layer_keys()");
1431 
1432     utils->MD5Init(&Md5Ctx);
1433     utils->MD5Update(&Md5Ctx, key, keylen);
1434     if (text->i_am == SERVER) {
1435 	utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT,
1436 			 (unsigned) strlen(SEALING_SERVER_CLIENT));
1437     } else {
1438 	utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER,
1439 			 (unsigned) strlen(SEALING_CLIENT_SERVER));
1440     }
1441     utils->MD5Final(enckey, &Md5Ctx);
1442 
1443     utils->MD5Init(&Md5Ctx);
1444     utils->MD5Update(&Md5Ctx, key, keylen);
1445     if (text->i_am != SERVER) {
1446 	utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT,
1447 			 (unsigned) strlen(SEALING_SERVER_CLIENT));
1448     } else {
1449 	utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER,
1450 			 (unsigned) strlen(SEALING_CLIENT_SERVER));
1451     }
1452     utils->MD5Final(deckey, &Md5Ctx);
1453 
1454     /* create integrity keys */
1455     /* sending */
1456     utils->MD5Init(&Md5Ctx);
1457     utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN);
1458     if (text->i_am == SERVER) {
1459 	utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT,
1460 			 (unsigned) strlen(SIGNING_SERVER_CLIENT));
1461     } else {
1462 	utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER,
1463 			 (unsigned) strlen(SIGNING_CLIENT_SERVER));
1464     }
1465     utils->MD5Final(text->Ki_send, &Md5Ctx);
1466 
1467     /* receiving */
1468     utils->MD5Init(&Md5Ctx);
1469     utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN);
1470     if (text->i_am != SERVER) {
1471 	utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT,
1472 			 (unsigned) strlen(SIGNING_SERVER_CLIENT));
1473     } else {
1474 	utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER,
1475 			 (unsigned) strlen(SIGNING_CLIENT_SERVER));
1476     }
1477     utils->MD5Final(text->Ki_receive, &Md5Ctx);
1478 
1479     return SASL_OK;
1480 }
1481 
1482 static const unsigned short version = 1;
1483 
1484 /*
1485  * privacy:
1486  * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum
1487  *
1488  * integrity:
1489  * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum
1490  */
digestmd5_encode(void * context,const struct iovec * invec,unsigned numiov,const char ** output,unsigned * outputlen)1491 static int digestmd5_encode(void *context,
1492 			    const struct iovec *invec,
1493 			    unsigned numiov,
1494 			    const char **output,
1495 			    unsigned *outputlen)
1496 {
1497     context_t *text = (context_t *) context;
1498     int tmp;
1499     unsigned int tmpnum;
1500     unsigned short int tmpshort;
1501     int ret;
1502     char *out;
1503     struct buffer_info *inblob, bufinfo;
1504 
1505     if(!context || !invec || !numiov || !output || !outputlen) {
1506 	if (text) PARAMERROR(text->utils);
1507 	return SASL_BADPARAM;
1508     }
1509 
1510     if (numiov > 1) {
1511 	ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
1512 	if (ret != SASL_OK) return ret;
1513 	inblob = text->enc_in_buf;
1514     } else {
1515 	/* avoid the data copy */
1516 	bufinfo.data = invec[0].iov_base;
1517 	bufinfo.curlen = invec[0].iov_len;
1518 	inblob = &bufinfo;
1519     }
1520 
1521     /* make sure the output buffer is big enough for this blob */
1522     ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
1523 			  &(text->encode_buf_len),
1524 			  (4 +			/* for length */
1525 			   inblob->curlen +	/* for content */
1526 			   10 +			/* for MAC */
1527 			   8 +			/* maximum pad */
1528 			   6));			/* for ver and seqnum */
1529     if(ret != SASL_OK) return ret;
1530 
1531     /* skip by the length for now */
1532     out = (text->encode_buf)+4;
1533 
1534     /* construct (seqnum, msg)
1535      *
1536      * Use the output buffer so that the message text is already in place
1537      * for an integrity-only layer.
1538      */
1539     tmpnum = htonl(text->seqnum);
1540     memcpy(text->encode_buf, &tmpnum, 4);
1541     memcpy(text->encode_buf + 4, inblob->data, inblob->curlen);
1542 
1543     if (text->cipher_enc) {
1544 	unsigned char digest[16];
1545 
1546 	/* HMAC(ki, (seqnum, msg) ) */
1547 	text->utils->hmac_md5((const unsigned char *) text->encode_buf,
1548 			      inblob->curlen + 4,
1549 			      text->Ki_send, HASHLEN, digest);
1550 
1551 	/* calculate the encrypted part */
1552 	text->cipher_enc(text, inblob->data, inblob->curlen,
1553 			 digest, out, outputlen);
1554 	out+=(*outputlen);
1555     }
1556     else {
1557 	/* HMAC(ki, (seqnum, msg) ) -- put directly into output buffer */
1558 	text->utils->hmac_md5((const unsigned char *) text->encode_buf,
1559 			      inblob->curlen + 4,
1560 			      text->Ki_send, HASHLEN,
1561 			      (unsigned char *) text->encode_buf +
1562 						inblob->curlen + 4);
1563 
1564 	*outputlen = inblob->curlen + 10; /* for message + CMAC */
1565 	out+=inblob->curlen + 10;
1566     }
1567 
1568     /* copy in version */
1569     tmpshort = htons(version);
1570     memcpy(out, &tmpshort, 2);	/* 2 bytes = version */
1571 
1572     out+=2;
1573     (*outputlen)+=2; /* for version */
1574 
1575     /* put in seqnum */
1576     tmpnum = htonl(text->seqnum);
1577     memcpy(out, &tmpnum, 4);	/* 4 bytes = seq # */
1578 
1579     (*outputlen)+=4; /* for seqnum */
1580 
1581     /* put the 1st 4 bytes in */
1582     tmp=htonl(*outputlen);
1583     memcpy(text->encode_buf, &tmp, 4);
1584 
1585     (*outputlen)+=4;
1586 
1587     *output = text->encode_buf;
1588     text->seqnum++;
1589 
1590     return SASL_OK;
1591 }
1592 
digestmd5_decode_packet(void * context,const char * input,unsigned inputlen,char ** output,unsigned * outputlen)1593 static int digestmd5_decode_packet(void *context,
1594 					   const char *input,
1595 					   unsigned inputlen,
1596 					   char **output,
1597 					   unsigned *outputlen)
1598 {
1599     context_t *text = (context_t *) context;
1600     int result;
1601     unsigned char *digest;
1602     int tmpnum;
1603     int lup;
1604     unsigned short ver;
1605     unsigned int seqnum;
1606     unsigned char checkdigest[16];
1607 
1608     if (inputlen < 16) {
1609 	text->utils->seterror(text->utils->conn, 0, "DIGEST-MD5 SASL packets must be at least 16 bytes long");
1610 	return SASL_FAIL;
1611     }
1612 
1613     /* check the version number */
1614     memcpy(&ver, input+inputlen-6, 2);
1615     ver = ntohs(ver);
1616     if (ver != version) {
1617 	text->utils->seterror(text->utils->conn, 0, "Wrong Version");
1618 	return SASL_FAIL;
1619     }
1620 
1621     /* check the sequence number */
1622     memcpy(&seqnum, input+inputlen-4, 4);
1623     seqnum = ntohl(seqnum);
1624 
1625     if (seqnum != text->rec_seqnum) {
1626 	text->utils->seterror(text->utils->conn, 0,
1627 	    "Incorrect Sequence Number: received %u, expected %u",
1628 	    seqnum,
1629 	    text->rec_seqnum);
1630 	return SASL_FAIL;
1631     }
1632 
1633     /* allocate a buffer large enough for the output */
1634     result = _plug_buf_alloc(text->utils, &text->decode_packet_buf,
1635 			     &text->decode_packet_buf_len,
1636 			     inputlen	/* length of message */
1637 			     - 6	/* skip ver and seqnum */
1638 			     + 4);	/* prepend seqnum */
1639     if (result != SASL_OK) return result;
1640 
1641     /* construct (seqnum, msg) */
1642     tmpnum = htonl(text->rec_seqnum);
1643     memcpy(text->decode_packet_buf, &tmpnum, 4);
1644 
1645     text->rec_seqnum++; /* now increment it */
1646 
1647     *output = text->decode_packet_buf + 4; /* skip seqnum */
1648 
1649     if (text->cipher_dec) {
1650 	/* decrypt message & HMAC into output buffer */
1651 	result = text->cipher_dec(text, input, inputlen-6, NULL,
1652 				  *output, outputlen);
1653 	if (result != SASL_OK) return result;
1654     }
1655     else {
1656 	/* copy message & HMAC into output buffer */
1657 	memcpy(*output, input, inputlen - 6);
1658 	*outputlen = inputlen - 16; /* -16 to skip HMAC, ver and seqnum */
1659     }
1660     digest = (unsigned char *) *output + (inputlen - 16);
1661 
1662     /* check the CMAC */
1663 
1664     /* HMAC(ki, (seqnum, msg) ) */
1665     text->utils->hmac_md5((const unsigned char *) text->decode_packet_buf,
1666 			  (*outputlen) + 4,
1667 			  text->Ki_receive, HASHLEN, checkdigest);
1668 
1669     /* now check it */
1670     for (lup = 0; lup < 10; lup++)
1671 	if (checkdigest[lup] != digest[lup]) {
1672 	    text->utils->seterror(text->utils->conn, 0,
1673 				  "CMAC doesn't match at byte %d!", lup);
1674 	    return SASL_FAIL;
1675 	}
1676 
1677     return SASL_OK;
1678 }
1679 
digestmd5_decode(void * context,const char * input,unsigned inputlen,const char ** output,unsigned * outputlen)1680 static int digestmd5_decode(void *context,
1681 				    const char *input, unsigned inputlen,
1682 				    const char **output, unsigned *outputlen)
1683 {
1684     context_t *text = (context_t *) context;
1685     int ret;
1686 
1687     ret = _plug_decode(&text->decode_context, input, inputlen,
1688 		       &text->decode_buf, &text->decode_buf_len, outputlen,
1689 		       digestmd5_decode_packet, text);
1690 
1691     *output = text->decode_buf;
1692 
1693     return ret;
1694 }
1695 
digestmd5_common_mech_dispose(void * conn_context,const sasl_utils_t * utils)1696 static void digestmd5_common_mech_dispose(void *conn_context,
1697 					  const sasl_utils_t *utils)
1698 {
1699     context_t *text = (context_t *) conn_context;
1700     int lup;
1701 
1702     if (!text || !utils) return;
1703 
1704     utils->log(utils->conn, SASL_LOG_DEBUG,
1705 	       "DIGEST-MD5 common mech dispose");
1706 
1707     if (text->authid) utils->free(text->authid);
1708     if (text->realm) utils->free(text->realm);
1709 
1710     if (text->realms) {
1711 	/* need to free all the realms */
1712 	for (lup = 0; lup < text->realm_cnt; lup++)
1713 	    utils->free (text->realms[lup]);
1714 
1715 	utils->free(text->realms);
1716     }
1717 
1718     if (text->nonce) utils->free(text->nonce);
1719     if (text->cnonce) utils->free(text->cnonce);
1720 
1721     if (text->cipher_free) text->cipher_free(text);
1722 
1723     /* free the stuff in the context */
1724     if (text->response_value) utils->free(text->response_value);
1725 
1726     _plug_decode_free(&text->decode_context);
1727     if (text->encode_buf) utils->free(text->encode_buf);
1728     if (text->decode_buf) utils->free(text->decode_buf);
1729     if (text->decode_packet_buf) utils->free(text->decode_packet_buf);
1730     if (text->out_buf) utils->free(text->out_buf);
1731 
1732     if (text->enc_in_buf) {
1733 	if (text->enc_in_buf->data) utils->free(text->enc_in_buf->data);
1734 	utils->free(text->enc_in_buf);
1735     }
1736 
1737     utils->free(conn_context);
1738 }
1739 
clear_reauth_entry(reauth_entry_t * reauth,enum Context_type type,const sasl_utils_t * utils)1740 static void clear_reauth_entry(reauth_entry_t *reauth, enum Context_type type,
1741 			       const sasl_utils_t *utils)
1742 {
1743     if (!reauth) return;
1744 
1745     if (reauth->authid) utils->free(reauth->authid);
1746     if (reauth->realm) utils->free(reauth->realm);
1747     if (reauth->nonce) utils->free(reauth->nonce);
1748     if (reauth->cnonce) utils->free(reauth->cnonce);
1749 
1750     if (type == CLIENT) {
1751 	if (reauth->u.c.serverFQDN) utils->free(reauth->u.c.serverFQDN);
1752     }
1753 
1754     memset(reauth, 0, sizeof(reauth_entry_t));
1755 }
1756 
digestmd5_common_mech_free(void * glob_context,const sasl_utils_t * utils)1757 static void digestmd5_common_mech_free(void *glob_context,
1758 				       const sasl_utils_t *utils)
1759 {
1760     digest_glob_context_t *my_glob_context =
1761 	(digest_glob_context_t *) glob_context;
1762     reauth_cache_t *reauth_cache = my_glob_context->reauth;
1763     size_t n;
1764 
1765     utils->log(utils->conn, SASL_LOG_DEBUG,
1766 	       "DIGEST-MD5 common mech free");
1767 
1768     /* Prevent anybody else from freeing this as well */
1769     my_glob_context->reauth = NULL;
1770 
1771     if (!reauth_cache) return;
1772 
1773     for (n = 0; n < reauth_cache->size; n++) {
1774 	clear_reauth_entry(&reauth_cache->e[n], reauth_cache->i_am, utils);
1775     }
1776     if (reauth_cache->e) utils->free(reauth_cache->e);
1777 
1778     if (reauth_cache->mutex) {
1779 	utils->mutex_free(reauth_cache->mutex);
1780 	reauth_cache->mutex = NULL;
1781     }
1782 
1783     utils->free(reauth_cache);
1784 }
1785 
1786 /*****************************  Server Section  *****************************/
1787 
1788 typedef struct server_context {
1789     context_t common;
1790 
1791     time_t timestamp;
1792     int stale;				/* last nonce is stale */
1793     sasl_ssf_t limitssf, requiressf;	/* application defined bounds */
1794 } server_context_t;
1795 
1796 static digest_glob_context_t server_glob_context;
1797 
DigestCalcHA1FromSecret(context_t * text,const sasl_utils_t * utils,HASH HA1,unsigned char * authorization_id,unsigned char * pszNonce,unsigned char * pszCNonce,HASHHEX SessionKey)1798 static void DigestCalcHA1FromSecret(context_t * text,
1799 				    const sasl_utils_t * utils,
1800 				    HASH HA1,
1801 				    unsigned char *authorization_id,
1802 				    unsigned char *pszNonce,
1803 				    unsigned char *pszCNonce,
1804 				    HASHHEX SessionKey)
1805 {
1806     MD5_CTX Md5Ctx;
1807 
1808     /* calculate session key */
1809     utils->MD5Init(&Md5Ctx);
1810     if (text->http_mode) {
1811 	/* per RFC 2617 Errata ID 1649 */
1812 	HASHHEX HA1Hex;
1813 
1814 	CvtHex(HA1, HA1Hex);
1815 	utils->MD5Update(&Md5Ctx, HA1Hex, HASHHEXLEN);
1816     }
1817     else {
1818 	/* per RFC 2831 */
1819 	utils->MD5Update(&Md5Ctx, HA1, HASHLEN);
1820     }
1821     utils->MD5Update(&Md5Ctx, COLON, 1);
1822     utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce));
1823     utils->MD5Update(&Md5Ctx, COLON, 1);
1824     utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce));
1825     if (authorization_id != NULL) {
1826 	utils->MD5Update(&Md5Ctx, COLON, 1);
1827 	utils->MD5Update(&Md5Ctx, authorization_id,
1828 			(unsigned) strlen((char *) authorization_id));
1829     }
1830     utils->MD5Final(HA1, &Md5Ctx);
1831 
1832     CvtHex(HA1, SessionKey);
1833 
1834 
1835     /* save HA1 because we need it to make the privacy and integrity keys */
1836     memcpy(text->HA1, HA1, sizeof(HASH));
1837 }
1838 
create_response(context_t * text,const sasl_utils_t * utils,unsigned char * nonce,unsigned int ncvalue,unsigned char * cnonce,char * qop,const sasl_http_request_t * request,HASH Secret,char * authorization_id,char ** response_value)1839 static char *create_response(context_t * text,
1840 			     const sasl_utils_t * utils,
1841 			     unsigned char *nonce,
1842 			     unsigned int ncvalue,
1843 			     unsigned char *cnonce,
1844 			     char *qop,
1845 			     const sasl_http_request_t *request,
1846 			     HASH Secret,
1847 			     char *authorization_id,
1848 			     char **response_value)
1849 {
1850     HASHHEX         SessionKey;
1851     HASH            EntityHash;
1852     HASHHEX         HEntity;
1853     HASHHEX         Response;
1854     char           *result;
1855 
1856     if (qop == NULL) qop = "auth";
1857 
1858     DigestCalcHA1FromSecret(text,
1859 			    utils,
1860 			    Secret,
1861 			    (unsigned char *) authorization_id,
1862 			    nonce,
1863 			    cnonce,
1864 			    SessionKey);
1865 
1866     if (text->http_mode) {
1867 	/* per RFC 2617 */
1868 	MD5_CTX Md5Ctx;
1869 
1870 	utils->MD5Init(&Md5Ctx);
1871 	utils->MD5Update(&Md5Ctx, request->entity, request->elen);
1872 	utils->MD5Final(EntityHash, &Md5Ctx);
1873     }
1874     else {
1875 	/* per RFC 2831 */
1876 	memset(EntityHash, 0, HASHLEN);
1877     }
1878     CvtHex(EntityHash, HEntity);
1879 
1880     /* Calculate response for comparison with client's response */
1881     DigestCalcResponse(utils,
1882 		       SessionKey,/* HEX(H(A1)) */
1883 		       nonce,	/* nonce from server */
1884 		       ncvalue,	/* 8 hex digits */
1885 		       cnonce,	/* client nonce */
1886 		       (unsigned char *) qop,	/* qop-value: "", "auth",
1887 						 * "auth-int" */
1888 		       (unsigned char *) request->uri,	/* requested URL */
1889 		       (unsigned char *) request->method,
1890 		       HEntity,	/* H(entity body) if qop="auth-int" */
1891 		       Response	/* request-digest or response-digest */
1892 	);
1893 
1894     result = utils->malloc(HASHHEXLEN + 1);
1895     memcpy(result, Response, HASHHEXLEN);
1896     result[HASHHEXLEN] = 0;
1897 
1898     /* Calculate response value for mutual auth with the client (NO Method) */
1899     if (response_value != NULL) {
1900 	char * new_response_value;
1901 
1902 	DigestCalcResponse(utils,
1903 			   SessionKey,	/* HEX(H(A1)) */
1904 			   nonce,	/* nonce from server */
1905 			   ncvalue,	/* 8 hex digits */
1906 			   cnonce,	/* client nonce */
1907 			   (unsigned char *) qop,	/* qop-value: "", "auth",
1908 							 * "auth-int" */
1909 			   (unsigned char *) request->uri, /* requested URL */
1910 			   NULL,
1911 			   HEntity,	/* H(entity body) if qop="auth-int" */
1912 			   Response	/* request-digest or response-digest */
1913 	    );
1914 
1915 	new_response_value = utils->realloc(*response_value, HASHHEXLEN + 1);
1916 	if (new_response_value == NULL) {
1917 	    free (*response_value);
1918 	    *response_value = NULL;
1919 	    return NULL;
1920 	}
1921 	*response_value = new_response_value;
1922 
1923 	memcpy(*response_value, Response, HASHHEXLEN);
1924 	(*response_value)[HASHHEXLEN] = 0;
1925     }
1926     return result;
1927 }
1928 
get_server_realm(sasl_server_params_t * params,char ** realm)1929 static int get_server_realm(sasl_server_params_t * params, char **realm)
1930 {
1931     /* look at user realm first */
1932     if (params->user_realm != NULL) {
1933 	if(params->user_realm[0] != '\0') {
1934 	    *realm = (char *) params->user_realm;
1935 	} else {
1936 	    /* Catch improperly converted apps */
1937 	    params->utils->seterror(params->utils->conn, 0,
1938 				    "user_realm is an empty string!");
1939 	    return SASL_BADPARAM;
1940 	}
1941     } else if (params->serverFQDN != NULL) {
1942 	*realm = (char *) params->serverFQDN;
1943     } else {
1944 	params->utils->seterror(params->utils->conn, 0,
1945 				"no way to obtain DIGEST-MD5 realm");
1946 	return SASL_FAIL;
1947     }
1948 
1949     return SASL_OK;
1950 }
1951 
1952 /*
1953  * Convert hex string to int
1954  */
htoi(unsigned char * hexin,unsigned int * res)1955 static int htoi(unsigned char *hexin, unsigned int *res)
1956 {
1957     size_t             lup, inlen;
1958     inlen = strlen((char *) hexin);
1959 
1960     *res = 0;
1961     for (lup = 0; lup < inlen; lup++) {
1962 	switch (hexin[lup]) {
1963 	case '0':
1964 	case '1':
1965 	case '2':
1966 	case '3':
1967 	case '4':
1968 	case '5':
1969 	case '6':
1970 	case '7':
1971 	case '8':
1972 	case '9':
1973 	    *res = (*res << 4) + (hexin[lup] - '0');
1974 	    break;
1975 
1976 	case 'a':
1977 	case 'b':
1978 	case 'c':
1979 	case 'd':
1980 	case 'e':
1981 	case 'f':
1982 	    *res = (*res << 4) + (hexin[lup] - 'a' + 10);
1983 	    break;
1984 
1985 	case 'A':
1986 	case 'B':
1987 	case 'C':
1988 	case 'D':
1989 	case 'E':
1990 	case 'F':
1991 	    *res = (*res << 4) + (hexin[lup] - 'A' + 10);
1992 	    break;
1993 
1994 	default:
1995 	    return SASL_BADPARAM;
1996 	}
1997 
1998     }
1999 
2000     return SASL_OK;
2001 }
2002 
digestmd5_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)2003 static int digestmd5_server_mech_new(void *glob_context,
2004 				     sasl_server_params_t * sparams,
2005 				     const char *challenge __attribute__((unused)),
2006 				     unsigned challen __attribute__((unused)),
2007 				     void **conn_context)
2008 {
2009     context_t *text;
2010 
2011     /* holds state are in -- allocate server size */
2012     text = sparams->utils->malloc(sizeof(server_context_t));
2013     if (text == NULL)
2014 	return SASL_NOMEM;
2015     memset((server_context_t *)text, 0, sizeof(server_context_t));
2016 
2017     text->state = 1;
2018     text->i_am = SERVER;
2019     text->http_mode = (sparams->flags & SASL_NEED_HTTP);
2020     text->reauth = ((digest_glob_context_t *) glob_context)->reauth;
2021 
2022     *conn_context = text;
2023     return SASL_OK;
2024 }
2025 
2026 static int
digestmd5_server_mech_step1(server_context_t * stext,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)2027 digestmd5_server_mech_step1(server_context_t *stext,
2028 			    sasl_server_params_t *sparams,
2029 			    const char *clientin __attribute__((unused)),
2030 			    unsigned clientinlen __attribute__((unused)),
2031 			    const char **serverout,
2032 			    unsigned *serveroutlen,
2033 			    sasl_out_params_t * oparams __attribute__((unused)))
2034 {
2035     context_t *text = (context_t *) stext;
2036     int             result;
2037     char           *realm;
2038     unsigned char  *nonce;
2039     char           *charset = "utf-8";
2040     char qop[1024], cipheropts[1024];
2041     struct digest_cipher *cipher;
2042     unsigned       resplen;
2043     int added_conf = 0;
2044     char maxbufstr[64];
2045 
2046     sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2047 			"DIGEST-MD5 server step 1");
2048 
2049     /* get realm */
2050     result = get_server_realm(sparams, &realm);
2051     if(result != SASL_OK) return result;
2052 
2053     /* what options should we offer the client? */
2054     qop[0] = '\0';
2055     cipheropts[0] = '\0';
2056     if (stext->requiressf == 0) {
2057 	if (*qop) strcat(qop, ",");
2058 	strcat(qop, "auth");
2059     }
2060     if (stext->requiressf <= 1 && stext->limitssf >= 1) {
2061 	if (*qop) strcat(qop, ",");
2062 	strcat(qop, "auth-int");
2063     }
2064 
2065     cipher = available_ciphers;
2066     while (cipher->name) {
2067 	/* do we allow this particular cipher? */
2068 	if (stext->requiressf <= cipher->ssf &&
2069 	    stext->limitssf >= cipher->ssf) {
2070 	    if (!added_conf) {
2071 		if (*qop) strcat(qop, ",");
2072 		strcat(qop, "auth-conf");
2073 		added_conf = 1;
2074 	    }
2075 	    if (strlen(cipheropts) + strlen(cipher->name) + 1 >= 1024)
2076 		return SASL_FAIL;
2077 	    if (*cipheropts) strcat(cipheropts, ",");
2078 	    strcat(cipheropts, cipher->name);
2079 	}
2080 	cipher++;
2081     }
2082 
2083     if (*qop == '\0') {
2084 	/* we didn't allow anything?!? we'll return SASL_TOOWEAK, since
2085 	   that's close enough */
2086 	return SASL_TOOWEAK;
2087     }
2088 
2089     /*
2090      * digest-challenge  = 1#( realm | nonce | qop-options | stale | maxbuf |
2091      * charset | cipher-opts | auth-param )
2092      */
2093 
2094     nonce = create_nonce(sparams->utils);
2095     if (nonce == NULL) {
2096 	SETERROR(sparams->utils, "internal erorr: failed creating a nonce");
2097 	return SASL_FAIL;
2098     }
2099 
2100     resplen = 0;
2101     text->out_buf = NULL;
2102     text->out_buf_len = 0;
2103     if (add_to_challenge(sparams->utils,
2104 				  &text->out_buf, &text->out_buf_len, &resplen,
2105 				  "nonce", (unsigned char *) nonce,
2106 				  TRUE) != SASL_OK) {
2107 	SETERROR(sparams->utils, "internal error: add_to_challenge failed");
2108 	return SASL_FAIL;
2109     }
2110 
2111     /* add to challenge; if we chose not to specify a realm, we won't
2112      * send one to the client */
2113     if (realm && add_to_challenge(sparams->utils,
2114 				  &text->out_buf, &text->out_buf_len, &resplen,
2115 				  "realm", (unsigned char *) realm,
2116 				  TRUE) != SASL_OK) {
2117 	SETERROR(sparams->utils, "internal error: add_to_challenge failed");
2118 	return SASL_FAIL;
2119     }
2120     /*
2121      * qop-options A quoted string of one or more tokens indicating the
2122      * "quality of protection" values supported by the server.  The value
2123      * "auth" indicates authentication; the value "auth-int" indicates
2124      * authentication with integrity protection; the value "auth-conf"
2125      * indicates authentication with integrity protection and encryption.
2126      */
2127 
2128     /* add qop to challenge */
2129     if (add_to_challenge(sparams->utils,
2130 			 &text->out_buf, &text->out_buf_len, &resplen,
2131 			 "qop",
2132 			 (unsigned char *) qop, TRUE) != SASL_OK) {
2133 	SETERROR(sparams->utils, "internal error: add_to_challenge 3 failed");
2134 	return SASL_FAIL;
2135     }
2136 
2137     /*
2138      *  Cipheropts - list of ciphers server supports
2139      */
2140     /* add cipher-opts to challenge; only add if there are some */
2141     if (strcmp(cipheropts,"")!=0)
2142 	{
2143 	    if (add_to_challenge(sparams->utils,
2144 				 &text->out_buf, &text->out_buf_len, &resplen,
2145 				 "cipher", (unsigned char *) cipheropts,
2146 				 TRUE) != SASL_OK) {
2147 		SETERROR(sparams->utils,
2148 			 "internal error: add_to_challenge 4 failed");
2149 		return SASL_FAIL;
2150 	    }
2151 	}
2152 
2153     /* "stale" is true if a reauth failed because of a nonce timeout */
2154     if (stext->stale &&
2155 	add_to_challenge(sparams->utils,
2156 			 &text->out_buf, &text->out_buf_len, &resplen,
2157 			 "stale", (unsigned char *) "true", FALSE) != SASL_OK) {
2158 	SETERROR(sparams->utils, "internal error: add_to_challenge failed");
2159 	return SASL_FAIL;
2160     }
2161 
2162     /*
2163      * maxbuf A number indicating the size of the largest buffer the server
2164      * is able to receive when using "auth-int". If this directive is
2165      * missing, the default value is 65536. This directive may appear at most
2166      * once; if multiple instances are present, the client should abort the
2167      * authentication exchange.
2168      */
2169     if(sparams->props.maxbufsize) {
2170 	snprintf(maxbufstr, sizeof(maxbufstr), "%u",
2171 		 sparams->props.maxbufsize);
2172 	if (add_to_challenge(sparams->utils,
2173 			     &text->out_buf, &text->out_buf_len, &resplen,
2174 			     "maxbuf",
2175 			     (unsigned char *) maxbufstr, FALSE) != SASL_OK) {
2176 	    SETERROR(sparams->utils,
2177 		     "internal error: add_to_challenge 5 failed");
2178 	    return SASL_FAIL;
2179 	}
2180     }
2181 
2182     if (add_to_challenge(sparams->utils,
2183 			 &text->out_buf, &text->out_buf_len, &resplen,
2184 			 "charset",
2185 			 (unsigned char *) charset, FALSE) != SASL_OK) {
2186 	SETERROR(sparams->utils, "internal error: add_to_challenge 6 failed");
2187 	return SASL_FAIL;
2188     }
2189 
2190 
2191     /*
2192      * algorithm
2193      *  This directive is required for backwards compatibility with HTTP
2194      *  Digest, which supports other algorithms. This directive is
2195      *  required and MUST appear exactly once; if not present, or if multiple
2196      *  instances are present, the client should abort the authentication
2197      *  exchange.
2198      *
2199      * algorithm         = "algorithm" "=" "md5-sess"
2200      */
2201 
2202     if (add_to_challenge(sparams->utils,
2203 			 &text->out_buf, &text->out_buf_len, &resplen,
2204 			 "algorithm",
2205 			 (unsigned char *) "md5-sess", FALSE)!=SASL_OK) {
2206 	SETERROR(sparams->utils, "internal error: add_to_challenge 7 failed");
2207 	return SASL_FAIL;
2208     }
2209 
2210     /*
2211      * The size of a digest-challenge MUST be less than 2048 bytes!!!
2212      */
2213     if (*serveroutlen > 2048) {
2214 	SETERROR(sparams->utils,
2215 		 "internal error: challenge larger than 2048 bytes");
2216 	return SASL_FAIL;
2217     }
2218 
2219     text->authid = NULL;
2220     if (_plug_strdup(sparams->utils, realm, &text->realm, NULL) != SASL_OK) {
2221 	SETERROR(sparams->utils,
2222 		 "internal error: out of memory when saving realm");
2223 	return SASL_FAIL;
2224     }
2225 
2226     if (text->http_mode && text->reauth->timeout &&
2227 	sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
2228 
2229 	/* Create an initial cache entry for non-persistent HTTP connections */
2230 	unsigned val = hash((char *) nonce) % text->reauth->size;
2231 
2232 	clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
2233 	text->reauth->e[val].authid = NULL;
2234 	text->reauth->e[val].realm = text->realm; text->realm = NULL;
2235 	text->reauth->e[val].nonce = nonce;
2236 	text->reauth->e[val].nonce_count = 1;
2237 	text->reauth->e[val].cnonce = NULL;
2238 	text->reauth->e[val].u.s.timestamp = time(0);
2239 
2240 	sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
2241     }
2242     else {
2243 	text->nonce = nonce;
2244 	text->nonce_count = 1;
2245 	text->cnonce = NULL;
2246 	stext->timestamp = time(0);
2247     }
2248 
2249     *serveroutlen = (unsigned) strlen(text->out_buf);
2250     *serverout = text->out_buf;
2251 
2252     text->state = 2;
2253 
2254     return SASL_CONTINUE;
2255 }
2256 
digestmd5_server_mech_step2(server_context_t * stext,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)2257 static int digestmd5_server_mech_step2(server_context_t *stext,
2258 				       sasl_server_params_t *sparams,
2259 				       const char *clientin,
2260 				       unsigned clientinlen,
2261 				       const char **serverout,
2262 				       unsigned *serveroutlen,
2263 				       sasl_out_params_t * oparams)
2264 {
2265     context_t *text = (context_t *) stext;
2266     /* verify digest */
2267     sasl_secret_t  *sec = NULL;
2268     int             result;
2269     char           *serverresponse = NULL;
2270     char           *username = NULL;
2271     char           *authorization_id = NULL;
2272     char           *realm = NULL;
2273     unsigned char  *nonce = NULL, *cnonce = NULL;
2274     unsigned int   noncecount = 0;
2275     char           *qop = NULL;
2276     char           *digesturi = NULL;
2277     sasl_http_request_t rfc2831_request;
2278     const sasl_http_request_t *request;
2279     char           *response = NULL;
2280 
2281     /* setting the default value (65536) */
2282     unsigned long  client_maxbuf = 65536;
2283     int            maxbuf_count = 0;  /* How many maxbuf instances was found */
2284 
2285     char           *charset = NULL;
2286     char           *cipher = NULL;
2287     unsigned int   n = 0;
2288 
2289     HASH           Secret;
2290     HASH           SecretBogus;
2291     bool           Try_8859_1 = FALSE;
2292     int            client_ignores_realm = 0;
2293     char           *full_username = NULL;
2294     char           *internal_username = NULL;
2295     int            canon_flags;
2296 
2297     /* password prop_request */
2298     const char *password_request[] = { SASL_AUX_PASSWORD,
2299 #if defined(OBSOLETE_DIGEST_ATTR)
2300 				       "*cmusaslsecretDIGEST-MD5",
2301 #endif
2302 				       NULL };
2303     size_t len;
2304     struct propval auxprop_values[2];
2305 
2306     /* can we mess with clientin? copy it to be safe */
2307     char           *in_start = NULL;
2308     char           *in = NULL;
2309     cipher_free_t  *old_cipher_free = NULL;
2310 
2311     sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2312 			"DIGEST-MD5 server step 2");
2313 
2314     if (clientinlen == 0) {
2315 	SETERROR(sparams->utils, "input expected in DIGEST-MD5, step 2");
2316 	result = SASL_BADAUTH;
2317 	goto FreeAllMem;
2318     }
2319 
2320     if (text->http_mode) {
2321 	/* per RFC 2617 (HTTP Request as set by calling application) */
2322 	request = sparams->http_request;
2323 	if (!request) {
2324 	    SETERROR(sparams->utils,
2325 		     "missing HTTP request in DIGEST-MD5, step 2");
2326 	    result = SASL_BADPARAM;
2327 	    goto FreeAllMem;
2328 	}
2329     }
2330     else {
2331 	/* per RFC 2831 */
2332 	rfc2831_request.method = "AUTHENTICATE";
2333 	rfc2831_request.uri = NULL;  /* to be filled in below from response */
2334 	rfc2831_request.entity = NULL;
2335 	rfc2831_request.elen = 0;
2336 	rfc2831_request.non_persist = 0;
2337 	request = &rfc2831_request;
2338     }
2339 
2340     in = sparams->utils->malloc(clientinlen + 1);
2341 
2342     memcpy(in, clientin, clientinlen);
2343     in[clientinlen] = 0;
2344 
2345     in_start = in;
2346 
2347 
2348     /* parse what we got */
2349     while (in[0] != '\0') {
2350 	char           *name = NULL, *value = NULL;
2351 	get_pair(&in, &name, &value);
2352 
2353 	if (name == NULL) {
2354 	    SETERROR(sparams->utils,
2355 		     "Parse error");
2356 	    result = SASL_BADAUTH;
2357 	    goto FreeAllMem;
2358 	}
2359 
2360 	if (*name == '\0') {
2361 	    break;
2362 	}
2363 
2364 	/* Extracting parameters */
2365 
2366 	/*
2367 	 * digest-response  = 1#( username | realm | nonce | cnonce |
2368 	 * nonce-count | qop | digest-uri | response | maxbuf | charset |
2369 	 * cipher | auth-param )
2370 	 */
2371 
2372 	if (strcasecmp(name, "username") == 0) {
2373 	    _plug_strdup(sparams->utils, value, &username, NULL);
2374 	} else if (strcasecmp(name, "authzid") == 0) {
2375 	    _plug_strdup(sparams->utils, value, &authorization_id, NULL);
2376 	} else if (strcasecmp(name, "cnonce") == 0) {
2377 	    _plug_strdup(sparams->utils, value, (char **) &cnonce, NULL);
2378 	} else if (strcasecmp(name, "nc") == 0) {
2379 	    if (htoi((unsigned char *) value, &noncecount) != SASL_OK) {
2380 		SETERROR(sparams->utils,
2381 			 "error converting hex to int");
2382 		result = SASL_BADAUTH;
2383 		goto FreeAllMem;
2384 	    }
2385 	} else if (strcasecmp(name, "realm") == 0) {
2386 	    if (realm) {
2387 		SETERROR(sparams->utils,
2388 			 "duplicate realm: authentication aborted");
2389 		result = SASL_FAIL;
2390 		goto FreeAllMem;
2391 	    }
2392 	    _plug_strdup(sparams->utils, value, &realm, NULL);
2393 	} else if (strcasecmp(name, "nonce") == 0) {
2394 	    _plug_strdup(sparams->utils, value, (char **) &nonce, NULL);
2395 	} else if (strcasecmp(name, "qop") == 0) {
2396 	    if (qop) {
2397 		SETERROR(sparams->utils,
2398 			 "duplicate qop: authentication aborted");
2399 		result = SASL_FAIL;
2400 		goto FreeAllMem;
2401 	    }
2402 	    _plug_strdup(sparams->utils, value, &qop, NULL);
2403 	} else if (strcasecmp(name, "digest-uri") == 0 ||  /* per RFC 2831 */
2404 		   (text->http_mode &&
2405 		    strcasecmp(name, "uri") == 0)) {	   /* per RFC 2617 */
2406             size_t service_len;
2407 
2408 	    if (digesturi) {
2409 		SETERROR(sparams->utils,
2410 			 "duplicate digest-uri: authentication aborted");
2411 		result = SASL_FAIL;
2412 		goto FreeAllMem;
2413 	    }
2414 
2415 	    _plug_strdup(sparams->utils, value, &digesturi, NULL);
2416 
2417 	    if (text->http_mode && request && request->uri) {
2418 		/* Verify digest-uri matches HTTP request (per RFC 2617) */
2419 		if (strcmp(digesturi, request->uri)) {
2420 		    result = SASL_BADAUTH;
2421 		    SETERROR(sparams->utils,
2422 			     "bad digest-uri: doesn't match HTTP request");
2423 		    goto FreeAllMem;
2424 		}
2425 	    }
2426 	    else {
2427 		/* Verify digest-uri format (per RFC 2831):
2428 		 *
2429 		 * digest-uri-value  = serv-type "/" host [ "/" serv-name ]
2430 		 */
2431 
2432 		/* make sure it's the service that we're expecting */
2433 		service_len = strlen(sparams->service);
2434 		if (strncasecmp(digesturi, sparams->service, service_len) ||
2435 		    digesturi[service_len] != '/') {
2436 		    result = SASL_BADAUTH;
2437 		    SETERROR(sparams->utils,
2438 			     "bad digest-uri: doesn't match service");
2439 		    goto FreeAllMem;
2440 		}
2441 
2442 		/* xxx we don't verify the hostname component */
2443 
2444 		rfc2831_request.uri = digesturi;
2445 	    }
2446 
2447 	} else if (strcasecmp(name, "response") == 0) {
2448 	    _plug_strdup(sparams->utils, value, &response, NULL);
2449 	} else if (strcasecmp(name, "cipher") == 0) {
2450 	    _plug_strdup(sparams->utils, value, &cipher, NULL);
2451 	} else if (strcasecmp(name, "maxbuf") == 0) {
2452 	    maxbuf_count++;
2453 	    if (maxbuf_count != 1) {
2454 		result = SASL_BADAUTH;
2455 		SETERROR(sparams->utils,
2456 			 "duplicate maxbuf: authentication aborted");
2457 		goto FreeAllMem;
2458 	    } else if (str2ul32 (value, &client_maxbuf) == FALSE) {
2459 		result = SASL_BADAUTH;
2460 		SETERROR(sparams->utils, "invalid maxbuf parameter");
2461 		goto FreeAllMem;
2462 	    } else {
2463 		if (client_maxbuf <= 16) {
2464 		    result = SASL_BADAUTH;
2465 		    SETERROR(sparams->utils,
2466 			     "maxbuf parameter too small");
2467 		    goto FreeAllMem;
2468 		}
2469 
2470 		if (client_maxbuf > MAX_SASL_BUFSIZE) {
2471 		    result = SASL_BADAUTH;
2472 		    SETERROR(sparams->utils,
2473 			     "maxbuf parameter too big");
2474 		    goto FreeAllMem;
2475 		}
2476 	    }
2477 	} else if (strcasecmp(name, "charset") == 0) {
2478 	    if (strcasecmp(value, "utf-8") != 0) {
2479 		SETERROR(sparams->utils, "client doesn't support UTF-8");
2480 		result = SASL_FAIL;
2481 		goto FreeAllMem;
2482 	    }
2483 	    _plug_strdup(sparams->utils, value, &charset, NULL);
2484 	} else if (strcasecmp(name,"algorithm") == 0) {
2485 	    /* per RFC 2831: algorithm MUST be ignored if received */
2486 	    if (text->http_mode && strcasecmp(value, "md5-sess") != 0) {
2487 		/* per RFC 2617: algorithm MUST match that sent in challenge */
2488 		SETERROR(sparams->utils, "'algorithm' isn't 'md5-sess'");
2489 		result = SASL_FAIL;
2490 		goto FreeAllMem;
2491 	    }
2492 	} else {
2493 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2494 				"DIGEST-MD5 unrecognized pair %s/%s: ignoring",
2495 				name, value);
2496 	}
2497     }
2498 
2499     /*
2500      * username         = "username" "=" <"> username-value <">
2501      * username-value   = qdstr-val
2502      * cnonce           = "cnonce" "=" <"> cnonce-value <">
2503      * cnonce-value     = qdstr-val
2504      * nonce-count      = "nc" "=" nc-value
2505      * nc-value         = 8LHEX
2506      * qop              = "qop" "=" qop-value
2507      * digest-uri       = "digest-uri" "=" digest-uri-value
2508      * digest-uri-value = serv-type "/" host [ "/" serv-name ]
2509      * serv-type        = 1*ALPHA
2510      * host             = 1*( ALPHA | DIGIT | "-" | "." )
2511      * service          = host
2512      * response         = "response" "=" <"> response-value <">
2513      * response-value   = 32LHEX
2514      * LHEX             = "0" | "1" | "2" | "3" | "4" | "5" |
2515      * "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f"
2516      * cipher           = "cipher" "=" cipher-value
2517      */
2518     /* Verifing that all required parameters were received */
2519     if ((username == NULL)) {
2520 	SETERROR(sparams->utils, "required parameters missing: username");
2521 	result = SASL_BADAUTH;
2522 	goto FreeAllMem;
2523     }
2524     if ((nonce == NULL)) {
2525 	SETERROR(sparams->utils, "required parameters missing: nonce");
2526 	result = SASL_BADAUTH;
2527 	goto FreeAllMem;
2528     }
2529     if ((noncecount == 0)) {
2530 	SETERROR(sparams->utils, "required parameters missing: noncecount");
2531 	result = SASL_BADAUTH;
2532 	goto FreeAllMem;
2533     }
2534     if ((cnonce == NULL)) {
2535 	SETERROR(sparams->utils, "required parameters missing: cnonce");
2536 	result = SASL_BADAUTH;
2537 	goto FreeAllMem;
2538     }
2539     if ((digesturi == NULL)) {
2540 	SETERROR(sparams->utils, "required parameters missing: digesturi");
2541 	result = SASL_BADAUTH;
2542 	goto FreeAllMem;
2543     }
2544     if ((response == NULL)) {
2545 	SETERROR(sparams->utils, "required parameters missing: response");
2546 	result = SASL_BADAUTH;
2547 	goto FreeAllMem;
2548     }
2549 
2550     if (realm == NULL) {
2551         /* From 2831bis:
2552            If the directive is missing, "realm-value" will set to
2553            the empty string when computing A1. */
2554 	_plug_strdup(sparams->utils, "", &realm, NULL);
2555 	sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2556 			"The client didn't send a realm, assuming empty string.");
2557 #if 0
2558         if (text->realm[0] != '\0') {
2559             SETERROR(sparams->utils,
2560 		 "realm changed: authentication aborted");
2561             result = SASL_BADAUTH;
2562             goto FreeAllMem;
2563         }
2564 #endif
2565     }
2566 
2567     if (!text->nonce && text->reauth->timeout && text->reauth->size > 0) {
2568 	unsigned val = hash((char *) nonce) % text->reauth->size;
2569 
2570 	/* reauth attempt or continuation of HTTP Digest on a
2571 	   non-persistent connection, see if we have any info for this nonce */
2572 	if (sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
2573 	    if (text->reauth->e[val].realm &&
2574 		!strcmp(realm, text->reauth->e[val].realm) &&
2575 		((text->reauth->e[val].nonce_count == 1) ||
2576 		 (text->reauth->e[val].authid &&
2577 		  !strcmp(username, text->reauth->e[val].authid)))) {
2578 
2579 		_plug_strdup(sparams->utils, text->reauth->e[val].realm,
2580 			     &text->realm, NULL);
2581 		_plug_strdup(sparams->utils, (char *) text->reauth->e[val].nonce,
2582 			     (char **) &text->nonce, NULL);
2583 		text->nonce_count = text->reauth->e[val].nonce_count;
2584 #if 0  /* XXX  Neither RFC 2617 nor RFC 2831 state that the cnonce
2585 	  needs to remain constant for subsequent authentication to work */
2586 		_plug_strdup(sparams->utils, (char *) text->reauth->e[val].cnonce,
2587 			     (char **) &text->cnonce, NULL);
2588 #endif
2589 		stext->timestamp = text->reauth->e[val].u.s.timestamp;
2590 	    }
2591 	    sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
2592 	}
2593 
2594 	if (!text->nonce) {
2595 	    /* we don't have any reauth info */
2596 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2597 				"No reauth info for '%s' found", nonce);
2598 
2599 	    /* we will continue processing the response to determine
2600 	       if the client knows the password and return stale accordingly */
2601 	}
2602     }
2603 
2604     /* Sanity check the parameters */
2605     if (text->nonce) {
2606 	/* CLAIM: realm is not NULL below */
2607 	if (text->realm == NULL) {
2608 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2609 				"The client specifies a realm when the server hasn't provided one. Using client's realm.");
2610 	    _plug_strdup(sparams->utils, realm, &text->realm, NULL);
2611 	} else if ((strcmp(realm, text->realm) != 0) &&
2612 		   /* XXX - Not sure why the check for text->realm not being empty is needed,
2613 		      as it should always be non-empty */
2614 		   (text->realm[0] != 0)) {
2615 
2616 	    client_ignores_realm = 1;
2617 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG,
2618 				"The client tries to override server provided realm");
2619 	    if (text->realm) sparams->utils->free(text->realm);
2620 	    _plug_strdup(sparams->utils, realm, &text->realm, NULL);
2621 	}
2622 
2623 	if (strcmp((char *) nonce, (char *) text->nonce) != 0) {
2624 	    SETERROR(sparams->utils,
2625 		     "nonce changed: authentication aborted");
2626 	    result = SASL_BADAUTH;
2627 	    goto FreeAllMem;
2628 	}
2629 #if 0	/* XXX  Possible replay attack, but we will continue processing
2630 	 * the response to determine if the client knows the password and
2631 	 return stale accordingly */
2632 	if (noncecount != text->nonce_count) {
2633 	    SETERROR(sparams->utils,
2634 		     "incorrect nonce-count: authentication aborted");
2635 	    result = SASL_BADAUTH;
2636 	    goto FreeAllMem;
2637 	}
2638 #endif
2639 #if 0	/* XXX  Neither RFC 2617 nor RFC 2831 state that the cnonce
2640 	   needs to remain constant for subsequent authentication to work */
2641 	if (text->cnonce && strcmp((char *) cnonce, (char *) text->cnonce) != 0) {
2642 	    SETERROR(sparams->utils,
2643 		     "cnonce changed: authentication aborted");
2644 	    result = SASL_BADAUTH;
2645 	    goto FreeAllMem;
2646 	}
2647 #endif
2648     }
2649 
2650     result = sparams->utils->prop_request(sparams->propctx, password_request);
2651     if(result != SASL_OK) {
2652 	SETERROR(sparams->utils, "unable to obtain user password");
2653 	goto FreeAllMem;
2654     }
2655 
2656     /* this will trigger the getting of the aux properties */
2657     /* Note that if we don't have an authorization id, we don't use it... */
2658 
2659     if (client_ignores_realm) {
2660 	if (strlen(text->realm) == 0) {
2661 	    /* Don't put @ at the end of the username, if the realm is empty */
2662 	    _plug_strdup(sparams->utils, username, &full_username, NULL);
2663 	} else {
2664 	    full_username = (char *) sparams->utils->malloc(strlen(username) +
2665 					strlen(text->realm) + 2);
2666 	    full_username[0] = '\0';
2667 	    sprintf (full_username, "%s@%s", username, text->realm);
2668 	}
2669 	internal_username = full_username;
2670     } else {
2671 	internal_username = username;
2672     }
2673 
2674     canon_flags = SASL_CU_AUTHID;
2675     if (!authorization_id || !*authorization_id) {
2676 	canon_flags |= SASL_CU_AUTHZID;
2677     }
2678 
2679     result = sparams->canon_user(sparams->utils->conn,
2680 				 internal_username,
2681 				 0,
2682 				 canon_flags,
2683 				 oparams);
2684     if (result != SASL_OK) {
2685 	SETERROR(sparams->utils, "unable to canonify user and get auxprops");
2686 	goto FreeAllMem;
2687     }
2688 
2689     if (authorization_id != NULL && *authorization_id != '\0') {
2690 	result = sparams->canon_user(sparams->utils->conn,
2691 				     authorization_id, 0, SASL_CU_AUTHZID,
2692 				     oparams);
2693     }
2694     if (result != SASL_OK) {
2695 	SETERROR(sparams->utils, "unable to canonify authorization ID");
2696 	goto FreeAllMem;
2697     }
2698 
2699     result = sparams->utils->prop_getnames(sparams->propctx, password_request,
2700 					   auxprop_values);
2701     if (result < 0 ||
2702         ((!auxprop_values[0].name || !auxprop_values[0].values)
2703 #if defined(OBSOLETE_DIGEST_ATTR)
2704          && (!auxprop_values[1].name || !auxprop_values[1].values)
2705 #endif
2706          )) {
2707 	/* We didn't find this username */
2708 	sparams->utils->seterror(sparams->utils->conn, 0,
2709 				 "no secret in database");
2710 	result = sparams->transition ? SASL_TRANS : SASL_NOUSER;
2711 	goto FreeAllMem;
2712     }
2713 
2714     if (auxprop_values[0].name && auxprop_values[0].values) {
2715 	len = strlen(auxprop_values[0].values[0]);
2716 	if (len == 0) {
2717 	    sparams->utils->seterror(sparams->utils->conn,0,
2718 				     "empty secret");
2719 	    result = SASL_FAIL;
2720 	    goto FreeAllMem;
2721 	}
2722 
2723 	sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len);
2724 	if (!sec) {
2725 	    SETERROR(sparams->utils, "unable to allocate secret");
2726 	    result = SASL_FAIL;
2727 	    goto FreeAllMem;
2728 	}
2729 
2730 	sec->len = (unsigned) len;
2731 	strncpy((char *) sec->data, auxprop_values[0].values[0], len + 1);
2732 
2733 	/*
2734 	 * Verifying response obtained from client
2735 	 *
2736 	 * H_URP = H({ username-value,":",realm-value,":",passwd}) sec->data
2737 	 * contains H_URP
2738 	 */
2739 
2740 	/* Calculate the secret from the plaintext password */
2741 	{
2742 	    /*
2743 	     * Secret = { H( { username-value, ":", realm-value, ":", passwd } ) }
2744 	     *
2745 	     * (used to build A1)
2746 	     */
2747 
2748 	    Try_8859_1 = DigestCalcSecret(sparams->utils,
2749 					  (unsigned char *) username,
2750 					  (unsigned char *) realm,
2751 					  sec->data,
2752 					  sec->len,
2753 					  FALSE,
2754 					  Secret);
2755 	    Secret[HASHLEN] = '\0';
2756 	}
2757 	if (Try_8859_1) {
2758 	    /*
2759 	     * Secret = { H( { username-value, ":", realm-value, ":", passwd } ) }
2760 	     *
2761 	     * (used to build A1)
2762 	     */
2763 
2764 	    DigestCalcSecret(sparams->utils,
2765 			     (unsigned char *) username,
2766 			     (unsigned char *) realm,
2767 			     sec->data,
2768 			     sec->len,
2769 			     TRUE,
2770 			     SecretBogus);
2771 	    SecretBogus[HASHLEN] = '\0';
2772 	}
2773 
2774 	/* We're done with sec now. Let's get rid of it */
2775 	_plug_free_secret(sparams->utils, &sec);
2776 #if defined(OBSOLETE_DIGEST_ATTR)
2777     } else if (auxprop_values[1].name && auxprop_values[1].values) {
2778         /* NB: This will most likely fail for clients that
2779 	   choose to ignore server-advertised realm */
2780 	memcpy(Secret, auxprop_values[1].values[0], HASHLEN);
2781 	Secret[HASHLEN] = '\0';
2782 #endif
2783     } else {
2784 	sparams->utils->seterror(sparams->utils->conn, 0,
2785 				 "Have neither type of secret");
2786 	return SASL_FAIL;
2787     }
2788 
2789     /* erase the plaintext password */
2790     sparams->utils->prop_erase(sparams->propctx, password_request[0]);
2791 
2792     /* defaulting qop to "auth" if not specified */
2793     if (qop == NULL) {
2794 	_plug_strdup(sparams->utils, "auth", &qop, NULL);
2795     }
2796 
2797     if (oparams->mech_ssf > 1) {
2798 	/* Remember the old cipher free function (if any).
2799 	   It will be called later, once we are absolutely
2800 	   sure that authentication was successful. */
2801 	old_cipher_free = text->cipher_free;
2802 	/* free the old cipher context first */
2803     }
2804 
2805     /* check which layer/cipher to use */
2806     if ((!strcasecmp(qop, "auth-conf")) && (cipher != NULL)) {
2807 	/* see what cipher was requested */
2808 	struct digest_cipher *cptr;
2809 
2810 	cptr = available_ciphers;
2811 	while (cptr->name) {
2812 	    /* find the cipher requested & make sure it's one we're happy
2813 	       with by policy */
2814 	    if (!strcasecmp(cipher, cptr->name) &&
2815 		stext->requiressf <= cptr->ssf &&
2816 		stext->limitssf >= cptr->ssf) {
2817 		/* found it! */
2818 		break;
2819 	    }
2820 	    cptr++;
2821 	}
2822 
2823 	if (cptr->name) {
2824 	    text->cipher_enc = cptr->cipher_enc;
2825 	    text->cipher_dec = cptr->cipher_dec;
2826 	    text->cipher_init = cptr->cipher_init;
2827 	    text->cipher_free = cptr->cipher_free;
2828 	    oparams->mech_ssf = cptr->ssf;
2829 	    n = cptr->n;
2830 	} else {
2831 	    /* erg? client requested something we didn't advertise! */
2832 	    sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN,
2833 				"protocol violation: client requested invalid cipher");
2834 	    SETERROR(sparams->utils, "client requested invalid cipher");
2835 	    /* Mark that we attempted security layer negotiation */
2836 	    oparams->mech_ssf = 2;
2837 	    result = SASL_FAIL;
2838 	    goto FreeAllMem;
2839 	}
2840 
2841 	oparams->encode=&digestmd5_encode;
2842 	oparams->decode=&digestmd5_decode;
2843     } else if (!strcasecmp(qop, "auth-int") &&
2844 	       stext->requiressf <= 1 && stext->limitssf >= 1) {
2845 	oparams->encode = &digestmd5_encode;
2846 	oparams->decode = &digestmd5_decode;
2847 	oparams->mech_ssf = 1;
2848     } else if (!strcasecmp(qop, "auth") && stext->requiressf == 0) {
2849 	oparams->encode = NULL;
2850 	oparams->decode = NULL;
2851 	oparams->mech_ssf = 0;
2852     } else {
2853 	SETERROR(sparams->utils,
2854 		 "protocol violation: client requested invalid qop");
2855 	result = SASL_FAIL;
2856 	goto FreeAllMem;
2857     }
2858 
2859     serverresponse = create_response(text,
2860 				     sparams->utils,
2861 				     nonce,
2862 				     noncecount,
2863 				     cnonce,
2864 				     qop,
2865 				     request,
2866 				     Secret,
2867 				     authorization_id,
2868 				     &text->response_value);
2869 
2870     if (serverresponse == NULL) {
2871 	SETERROR(sparams->utils, "internal error: unable to create response");
2872 	result = SASL_NOMEM;
2873 	goto FreeAllMem;
2874     }
2875 
2876     /* if ok verified */
2877     if (strcmp(serverresponse, response) != 0) {
2878 	if (Try_8859_1) {
2879             sparams->utils->free(serverresponse);
2880 	    serverresponse = create_response(text,
2881 					     sparams->utils,
2882 					     nonce,
2883 					     noncecount,
2884 					     cnonce,
2885 					     qop,
2886 					     request,
2887 					     SecretBogus,
2888 					     authorization_id,
2889 					     &text->response_value);
2890 
2891 	    if (serverresponse == NULL) {
2892 		SETERROR(sparams->utils, "internal error: unable to create response");
2893 		result = SASL_NOMEM;
2894 		goto FreeAllMem;
2895 	    }
2896 
2897 	    /* if ok verified */
2898 	    if (strcmp(serverresponse, response) != 0) {
2899 		SETERROR(sparams->utils,
2900 			 "client response doesn't match what we generated (tried bogus)");
2901 		result = SASL_BADAUTH;
2902 
2903 		goto FreeAllMem;
2904 	    }
2905 
2906 	} else {
2907 	    SETERROR(sparams->utils,
2908 		     "client response doesn't match what we generated");
2909 	    result = SASL_BADAUTH;
2910 
2911 	    goto FreeAllMem;
2912 	}
2913     }
2914 
2915     /* see if our nonce expired */
2916     if (!text->nonce ||
2917 	(noncecount != text->nonce_count) ||
2918 	(text->reauth->timeout &&
2919 	 time(0) - stext->timestamp > text->reauth->timeout)) {
2920 	if (!text->nonce) SETERROR(sparams->utils, "no cached server nonce");
2921 	else if (noncecount != text->nonce_count)
2922 	    SETERROR(sparams->utils, "incorrect nonce-count");
2923 	else SETERROR(sparams->utils, "server nonce expired");
2924 	stext->stale = 1;
2925 	result = SASL_BADAUTH;
2926 
2927 	goto FreeAllMem;
2928     }
2929 
2930     /*
2931      * nothing more to do; authenticated set oparams information
2932      */
2933     oparams->doneflag = 1;
2934     oparams->maxoutbuf = client_maxbuf - 4;
2935     if (oparams->mech_ssf > 1) {
2936 	/* MAC block (privacy) */
2937 	oparams->maxoutbuf -= 25;
2938     } else if(oparams->mech_ssf == 1) {
2939 	/* MAC block (integrity) */
2940 	oparams->maxoutbuf -= 16;
2941     }
2942 
2943     oparams->param_version = 0;
2944 
2945     text->seqnum = 0;		/* for integrity/privacy */
2946     text->rec_seqnum = 0;	/* for integrity/privacy */
2947     text->utils = sparams->utils;
2948 
2949     /* Free the old security layer, if any */
2950     if (old_cipher_free) old_cipher_free(text);
2951 
2952     /* used by layers */
2953     _plug_decode_init(&text->decode_context, text->utils,
2954 		      sparams->props.maxbufsize ? sparams->props.maxbufsize :
2955 		      DEFAULT_BUFSIZE);
2956 
2957     if (oparams->mech_ssf > 0) {
2958 	unsigned char enckey[16];
2959 	unsigned char deckey[16];
2960 
2961 	create_layer_keys(text, sparams->utils,text->HA1,n,enckey,deckey);
2962 
2963 	/* initialize cipher if need be */
2964 	if (text->cipher_init) {
2965 	    if (text->cipher_init(text, enckey, deckey) != SASL_OK) {
2966 		sparams->utils->seterror(sparams->utils->conn, 0,
2967 					 "couldn't init cipher");
2968 	    }
2969 	}
2970     }
2971 
2972     /*
2973      * The server receives and validates the "digest-response". The server
2974      * checks that the nonce-count is "00000001". If it supports subsequent
2975      * authentication, it saves the value of the nonce and the nonce-count.
2976      */
2977 
2978     /*
2979      * The "username-value", "realm-value" and "passwd" are encoded according
2980      * to the value of the "charset" directive. If "charset=UTF-8" is
2981      * present, and all the characters of either "username-value" or "passwd"
2982      * are in the ISO 8859-1 character set, then it must be converted to
2983      * UTF-8 before being hashed. A sample implementation of this conversion
2984      * is in section 8.
2985      */
2986 
2987     /* add to challenge */
2988     {
2989 	unsigned resplen = 0;
2990 
2991 	if (add_to_challenge(sparams->utils,
2992 			     &text->out_buf, &text->out_buf_len, &resplen,
2993 			     "rspauth", (unsigned char *) text->response_value,
2994 			     text->http_mode ? TRUE : FALSE) != SASL_OK) {
2995 	    SETERROR(sparams->utils, "internal error: add_to_challenge failed");
2996 	    result = SASL_FAIL;
2997 	    goto FreeAllMem;
2998 	}
2999 
3000 	if (text->http_mode) {
3001 	    /* per RFC 2617 */
3002 	    char ncvalue[10];
3003 
3004 	    if (add_to_challenge(sparams->utils,
3005 				 &text->out_buf, &text->out_buf_len, &resplen,
3006 				 "cnonce", cnonce, TRUE) != SASL_OK) {
3007 		result = SASL_FAIL;
3008 		goto FreeAllMem;
3009 	    }
3010 	    snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count);
3011 	    if (add_to_challenge(sparams->utils,
3012 				 &text->out_buf, &text->out_buf_len, &resplen,
3013 				 "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) {
3014 		result = SASL_FAIL;
3015 		goto FreeAllMem;
3016 	    }
3017 	    if (add_to_challenge(sparams->utils,
3018 				 &text->out_buf, &text->out_buf_len, &resplen,
3019 				 "qop", (unsigned char *) qop, TRUE) != SASL_OK) {
3020 		result = SASL_FAIL;
3021 		goto FreeAllMem;
3022 	    }
3023 	}
3024 
3025 	/* self check */
3026 	if (strlen(text->out_buf) > 2048) {
3027 	    result = SASL_FAIL;
3028 	    goto FreeAllMem;
3029 	}
3030     }
3031 
3032     *serveroutlen = (unsigned) strlen(text->out_buf);
3033     *serverout = text->out_buf;
3034 
3035     result = SASL_OK;
3036 
3037   FreeAllMem:
3038     if (clientinlen > 0 &&
3039 	text->reauth->timeout &&
3040 	sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
3041 
3042 	/* Look for an entry for the nonce value */
3043 	unsigned val = hash((char *) nonce) % text->reauth->size;
3044 
3045 	switch (result) {
3046 	case SASL_OK:
3047 	    /* successful auth, setup for future reauth */
3048 	    if (text->nonce_count == 1) {
3049 		/* successful initial auth, create new entry */
3050 		clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
3051 		text->reauth->e[val].authid = username; username = NULL;
3052 		text->reauth->e[val].realm = text->realm; text->realm = NULL;
3053 		text->reauth->e[val].nonce = text->nonce; text->nonce = NULL;
3054 		text->reauth->e[val].cnonce = cnonce; cnonce = NULL;
3055 	    }
3056 	    if (text->nonce_count < text->reauth->e[val].nonce_count) {
3057 		/* paranoia.  prevent replay attacks */
3058 		clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
3059 	    }
3060 	    else {
3061 		text->reauth->e[val].nonce_count = ++text->nonce_count;
3062 		text->reauth->e[val].u.s.timestamp = time(0);
3063 	    }
3064 	    break;
3065 	default:
3066 	    if (text->nonce_count > 1) {
3067 		/* failed reauth, clear entry */
3068 		clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils);
3069 	    }
3070 	    else {
3071 		/* failed initial auth, leave existing cache */
3072 	    }
3073 	}
3074 	sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
3075     }
3076 
3077     /* free everything */
3078     if (in_start) sparams->utils->free (in_start);
3079 
3080     if (full_username != NULL)
3081 	sparams->utils->free (full_username);
3082     if (username != NULL)
3083 	sparams->utils->free (username);
3084     if (authorization_id != NULL)
3085 	sparams->utils->free (authorization_id);
3086     if (realm != NULL)
3087 	sparams->utils->free (realm);
3088     if (nonce != NULL)
3089 	sparams->utils->free (nonce);
3090     if (cnonce != NULL)
3091 	sparams->utils->free (cnonce);
3092     if (response != NULL)
3093 	sparams->utils->free (response);
3094     if (cipher != NULL)
3095 	sparams->utils->free (cipher);
3096     if (serverresponse != NULL)
3097 	sparams->utils->free(serverresponse);
3098     if (charset != NULL)
3099 	sparams->utils->free (charset);
3100     if (digesturi != NULL)
3101 	sparams->utils->free (digesturi);
3102     if (qop!=NULL)
3103 	sparams->utils->free (qop);
3104     if (sec)
3105 	_plug_free_secret(sparams->utils, &sec);
3106 
3107     return result;
3108 }
3109 
digestmd5_server_mech_step(void * conn_context,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)3110 static int digestmd5_server_mech_step(void *conn_context,
3111 				      sasl_server_params_t *sparams,
3112 				      const char *clientin,
3113 				      unsigned clientinlen,
3114 				      const char **serverout,
3115 				      unsigned *serveroutlen,
3116 				      sasl_out_params_t *oparams)
3117 {
3118     context_t *text = (context_t *) conn_context;
3119     server_context_t *stext = (server_context_t *) conn_context;
3120 
3121     *serverout = NULL;
3122     *serveroutlen = 0;
3123 
3124     if (clientinlen > 4096) return SASL_BADPROT;
3125 
3126     if (text == NULL) {
3127 	return SASL_BADPROT;
3128     }
3129 
3130     switch (text->state) {
3131 
3132     case 1:
3133 	/* setup SSF limits */
3134 	if (!text->http_mode &&		/* HTTP Digest doesn't need buffer */
3135 	    !sparams->props.maxbufsize) {
3136 	    stext->limitssf = 0;
3137 	    stext->requiressf = 0;
3138 	} else {
3139 	    if (sparams->props.max_ssf < sparams->external_ssf) {
3140 		stext->limitssf = 0;
3141 	    } else {
3142 		stext->limitssf =
3143 		    sparams->props.max_ssf - sparams->external_ssf;
3144 	    }
3145 	    if (sparams->props.min_ssf < sparams->external_ssf) {
3146 		stext->requiressf = 0;
3147 	    } else {
3148 		stext->requiressf =
3149 		    sparams->props.min_ssf - sparams->external_ssf;
3150 	    }
3151 	}
3152 
3153         if (clientin && text->reauth->timeout) {
3154 	    /* here's where we attempt fast reauth if possible */
3155 	    if (digestmd5_server_mech_step2(stext, sparams,
3156 					    clientin, clientinlen,
3157 					    serverout, serveroutlen,
3158 					    oparams) == SASL_OK) {
3159 		return SASL_OK;
3160 	    }
3161 
3162 	    sparams->utils->log(NULL, SASL_LOG_WARN,
3163 				"DIGEST-MD5 reauth failed\n");
3164 
3165 	    /* re-initialize everything for a fresh start */
3166 	    memset(oparams, 0, sizeof(sasl_out_params_t));
3167 	    if (text->nonce) sparams->utils->free(text->nonce);
3168 	    if (text->realm) sparams->utils->free(text->realm);
3169 	    text->realm = NULL;
3170 	    text->nonce = NULL;
3171 
3172 	    /* fall through and issue challenge */
3173 	}
3174 
3175 	return digestmd5_server_mech_step1(stext, sparams,
3176 					   clientin, clientinlen,
3177 					   serverout, serveroutlen, oparams);
3178 
3179     case 2:
3180 	return digestmd5_server_mech_step2(stext, sparams,
3181 					   clientin, clientinlen,
3182 					   serverout, serveroutlen, oparams);
3183 
3184     default:
3185 	sparams->utils->log(NULL, SASL_LOG_ERR,
3186 			    "Invalid DIGEST-MD5 server step %d\n", text->state);
3187 	return SASL_FAIL;
3188     }
3189 
3190     return SASL_FAIL; /* should never get here */
3191 }
3192 
digestmd5_server_mech_dispose(void * conn_context,const sasl_utils_t * utils)3193 static void digestmd5_server_mech_dispose(void *conn_context,
3194 					  const sasl_utils_t *utils)
3195 {
3196     server_context_t *stext = (server_context_t *) conn_context;
3197 
3198     if (!stext || !utils) return;
3199 
3200     digestmd5_common_mech_dispose(conn_context, utils);
3201 }
3202 
3203 static sasl_server_plug_t digestmd5_server_plugins[] =
3204 {
3205     {
3206 	"DIGEST-MD5",			/* mech_name */
3207 #ifdef WITH_RC4
3208 	128,				/* max_ssf */
3209 #elif defined(WITH_DES)
3210 	112,
3211 #else
3212 	1,
3213 #endif
3214 	SASL_SEC_NOPLAINTEXT
3215 	| SASL_SEC_NOANONYMOUS
3216 	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
3217 	SASL_FEAT_ALLOWS_PROXY
3218 	| SASL_FEAT_SUPPORTS_HTTP,	/* features */
3219 	&server_glob_context,		/* glob_context */
3220 	&digestmd5_server_mech_new,	/* mech_new */
3221 	&digestmd5_server_mech_step,	/* mech_step */
3222 	&digestmd5_server_mech_dispose,	/* mech_dispose */
3223 	&digestmd5_common_mech_free,	/* mech_free */
3224 	NULL,				/* setpass */
3225 	NULL,				/* user_query */
3226 	NULL,				/* idle */
3227 	NULL,				/* mech avail */
3228 	NULL				/* spare */
3229     }
3230 };
3231 
digestmd5_server_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)3232 int digestmd5_server_plug_init(sasl_utils_t *utils,
3233 			       int maxversion,
3234 			       int *out_version,
3235 			       sasl_server_plug_t **pluglist,
3236 			       int *plugcount)
3237 {
3238     reauth_cache_t *reauth_cache;
3239     const char *timeout = NULL;
3240     unsigned int len;
3241 
3242     if (maxversion < SASL_SERVER_PLUG_VERSION) {
3243 	return SASL_BADVERS;
3244     }
3245 
3246     /* reauth cache */
3247     reauth_cache = utils->malloc(sizeof(reauth_cache_t));
3248     if (reauth_cache == NULL) {
3249 	return SASL_NOMEM;
3250     }
3251     memset(reauth_cache, 0, sizeof(reauth_cache_t));
3252     reauth_cache->i_am = SERVER;
3253 
3254     /* fetch and canonify the reauth_timeout */
3255     utils->getopt(utils->getopt_context, "DIGEST-MD5", "reauth_timeout",
3256 		  &timeout, &len);
3257     if (timeout) {
3258 	reauth_cache->timeout = (time_t) 60 * strtol(timeout, NULL, 10);
3259     }
3260     if (reauth_cache->timeout < 0) {
3261 	reauth_cache->timeout = 0;
3262     }
3263 
3264     if (reauth_cache->timeout) {
3265 	/* mutex */
3266 	reauth_cache->mutex = utils->mutex_alloc();
3267 	if (!reauth_cache->mutex) {
3268 	    utils->free(reauth_cache);
3269 	    return SASL_FAIL;
3270 	}
3271 
3272 	/* entries */
3273 	reauth_cache->size = 100;
3274 	reauth_cache->e = utils->malloc(reauth_cache->size *
3275 					sizeof(reauth_entry_t));
3276 	if (reauth_cache->e == NULL) {
3277 	    utils->mutex_free(reauth_cache->mutex);
3278 	    utils->free(reauth_cache);
3279 	    return SASL_NOMEM;
3280 	}
3281 	memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t));
3282     }
3283 
3284     ((digest_glob_context_t *) digestmd5_server_plugins[0].glob_context)->reauth = reauth_cache;
3285 
3286     *out_version = SASL_SERVER_PLUG_VERSION;
3287     *pluglist = digestmd5_server_plugins;
3288     *plugcount = 1;
3289 
3290     return SASL_OK;
3291 }
3292 
3293 /*****************************  Client Section  *****************************/
3294 
3295 typedef struct client_context {
3296     context_t common;
3297 
3298     sasl_secret_t *password;	/* user password */
3299     unsigned int free_password; /* set if we need to free password */
3300 
3301     int protection;
3302     struct digest_cipher *cipher;
3303     unsigned long server_maxbuf;
3304 
3305     /* for HTTP mode (RFC 2617) only */
3306     char *algorithm;
3307     unsigned char *opaque;
3308 } client_context_t;
3309 
3310 static digest_glob_context_t client_glob_context;
3311 
3312 /* calculate H(A1) as per spec */
DigestCalcHA1(context_t * text,const sasl_utils_t * utils,char * pszAlg,unsigned char * pszUserName,unsigned char * pszRealm,sasl_secret_t * pszPassword,unsigned char * pszAuthorization_id,unsigned char * pszNonce,unsigned char * pszCNonce,HASHHEX SessionKey)3313 static void DigestCalcHA1(context_t * text,
3314 			  const sasl_utils_t * utils,
3315 			  char *pszAlg,
3316 			  unsigned char *pszUserName,
3317 			  unsigned char *pszRealm,
3318 			  sasl_secret_t * pszPassword,
3319 			  unsigned char *pszAuthorization_id,
3320 			  unsigned char *pszNonce,
3321 			  unsigned char *pszCNonce,
3322 			  HASHHEX SessionKey)
3323 {
3324     MD5_CTX         Md5Ctx;
3325     HASH            HA1;
3326 
3327     DigestCalcSecret(utils,
3328 		     pszUserName,
3329 		     pszRealm,
3330 		     (unsigned char *) pszPassword->data,
3331 		     pszPassword->len,
3332 		     FALSE,
3333 		     HA1);
3334 
3335     if (!text->http_mode ||				    /* per RFC 2831 */
3336 	(pszAlg && strcasecmp(pszAlg, "md5-sess") == 0)) {  /* per RFC 2617 */
3337 	/* calculate the session key */
3338 	utils->MD5Init(&Md5Ctx);
3339 	if (text->http_mode) {
3340 	    /* per RFC 2617 Errata ID 1649 */
3341 	    HASHHEX HA1Hex;
3342 
3343 	    CvtHex(HA1, HA1Hex);
3344 	    utils->MD5Update(&Md5Ctx, HA1Hex, HASHHEXLEN);
3345 	}
3346 	else {
3347 	    /* per RFC 2831 */
3348 	    utils->MD5Update(&Md5Ctx, HA1, HASHLEN);
3349 	}
3350 	utils->MD5Update(&Md5Ctx, COLON, 1);
3351 	utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce));
3352 	utils->MD5Update(&Md5Ctx, COLON, 1);
3353 	utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce));
3354 	if (pszAuthorization_id != NULL) {
3355 	    utils->MD5Update(&Md5Ctx, COLON, 1);
3356 	    utils->MD5Update(&Md5Ctx, pszAuthorization_id,
3357 			     (unsigned) strlen((char *) pszAuthorization_id));
3358 	}
3359 	utils->MD5Final(HA1, &Md5Ctx);
3360     }
3361 
3362     CvtHex(HA1, SessionKey);
3363 
3364     /* xxx rc-* use different n */
3365 
3366     /* save HA1 because we'll need it for the privacy and integrity keys */
3367     memcpy(text->HA1, HA1, sizeof(HASH));
3368 
3369 }
3370 
calculate_response(context_t * text,const sasl_utils_t * utils,char * algorithm,unsigned char * username,unsigned char * realm,unsigned char * nonce,unsigned int ncvalue,unsigned char * cnonce,char * qop,const sasl_http_request_t * request,sasl_secret_t * passwd,unsigned char * authorization_id,char ** response_value)3371 static char *calculate_response(context_t * text,
3372 				const sasl_utils_t * utils,
3373 				char *algorithm,
3374 				unsigned char *username,
3375 				unsigned char *realm,
3376 				unsigned char *nonce,
3377 				unsigned int ncvalue,
3378 				unsigned char *cnonce,
3379 				char *qop,
3380 				const sasl_http_request_t *request,
3381 				sasl_secret_t * passwd,
3382 				unsigned char *authorization_id,
3383 				char **response_value)
3384 {
3385     HASHHEX         SessionKey;
3386     HASH            EntityHash;
3387     HASHHEX         HEntity;
3388     HASHHEX         Response;
3389     char           *result;
3390 
3391     /* Verifing that all parameters was defined */
3392     if(!username || !cnonce || !nonce || !ncvalue || !request || !passwd) {
3393 	PARAMERROR( utils );
3394 	return NULL;
3395     }
3396 
3397     if (realm == NULL) {
3398 	/* a NULL realm is equivalent to the empty string */
3399 	realm = (unsigned char *) "";
3400     }
3401 
3402     if (qop == NULL) {
3403 	/* default to a qop of just authentication */
3404 	qop = "auth";
3405     }
3406 
3407     DigestCalcHA1(text,
3408 		  utils,
3409 		  algorithm,
3410 		  username,
3411 		  realm,
3412 		  passwd,
3413 		  authorization_id,
3414 		  nonce,
3415 		  cnonce,
3416 		  SessionKey);
3417 
3418     if (text->http_mode) {
3419 	/* per RFC 2617 */
3420 	MD5_CTX Md5Ctx;
3421 
3422 	utils->MD5Init(&Md5Ctx);
3423 	utils->MD5Update(&Md5Ctx, request->entity, request->elen);
3424 	utils->MD5Final(EntityHash, &Md5Ctx);
3425     }
3426     else {
3427 	/* per RFC 2831 */
3428 	memset(EntityHash, 0, HASHLEN);
3429     }
3430     CvtHex(EntityHash, HEntity);
3431 
3432     DigestCalcResponse(utils,
3433 		       SessionKey,/* HEX(H(A1)) */
3434 		       nonce,	/* nonce from server */
3435 		       ncvalue,	/* 8 hex digits */
3436 		       cnonce,	/* client nonce */
3437 		       (unsigned char *) qop,	/* qop-value: "", "auth",
3438 						 * "auth-int" */
3439 		       (unsigned char *) request->uri,	/* requested URL */
3440 		       (unsigned char *) request->method,
3441 		       HEntity,	/* H(entity body) if qop="auth-int" */
3442 		       Response	/* request-digest or response-digest */
3443 	);
3444 
3445     result = utils->malloc(HASHHEXLEN + 1);
3446     memcpy(result, Response, HASHHEXLEN);
3447     result[HASHHEXLEN] = 0;
3448 
3449     if (response_value != NULL) {
3450 	char * new_response_value;
3451 
3452 	DigestCalcResponse(utils,
3453 			   SessionKey,	/* HEX(H(A1)) */
3454 			   nonce,	/* nonce from server */
3455 			   ncvalue,	/* 8 hex digits */
3456 			   cnonce,	/* client nonce */
3457 			   (unsigned char *) qop,	/* qop-value: "", "auth",
3458 							 * "auth-int" */
3459 			   (unsigned char *) request->uri,	/* requested URL */
3460 			   NULL,
3461 			   HEntity,	/* H(entity body) if qop="auth-int" */
3462 			   Response	/* request-digest or response-digest */
3463 	    );
3464 
3465 	new_response_value = utils->realloc(*response_value, HASHHEXLEN + 1);
3466 	if (new_response_value == NULL) {
3467 	    free (*response_value);
3468 	    *response_value = NULL;
3469 	    return NULL;
3470 	}
3471 	*response_value = new_response_value;
3472 
3473 	memcpy(*response_value, Response, HASHHEXLEN);
3474 	(*response_value)[HASHHEXLEN] = 0;
3475 
3476     }
3477 
3478     return result;
3479 }
3480 
make_client_response(context_t * text,sasl_client_params_t * params,sasl_out_params_t * oparams)3481 static int make_client_response(context_t *text,
3482 				sasl_client_params_t *params,
3483 				sasl_out_params_t *oparams)
3484 {
3485     client_context_t *ctext = (client_context_t *) text;
3486     char *qop = NULL;
3487     unsigned nbits = 0;
3488     char  *digesturi = NULL;
3489     bool            IsUTF8 = FALSE;
3490     char           ncvalue[10];
3491     char           maxbufstr[64];
3492     char           *response = NULL;
3493     unsigned        resplen = 0;
3494     int result = SASL_OK;
3495     cipher_free_t  *old_cipher_free = NULL;
3496     sasl_http_request_t rfc2831_request;
3497     const sasl_http_request_t *request;
3498 
3499     params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
3500 		       "DIGEST-MD5 make_client_response()");
3501 
3502     if (oparams->mech_ssf > 1) {
3503 	/* Remember the old cipher free function (if any).
3504 	   It will be called later, once we are absolutely
3505 	   sure that authentication was successful. */
3506 	old_cipher_free = text->cipher_free;
3507 	/* free the old cipher context first */
3508     }
3509 
3510     switch (ctext->protection) {
3511     case DIGEST_PRIVACY:
3512 	qop = "auth-conf";
3513 	oparams->encode = &digestmd5_encode;
3514 	oparams->decode = &digestmd5_decode;
3515 	oparams->mech_ssf = ctext->cipher->ssf;
3516 
3517 	nbits = ctext->cipher->n;
3518 	text->cipher_enc = ctext->cipher->cipher_enc;
3519 	text->cipher_dec = ctext->cipher->cipher_dec;
3520 	text->cipher_free = ctext->cipher->cipher_free;
3521 	text->cipher_init = ctext->cipher->cipher_init;
3522 	break;
3523     case DIGEST_INTEGRITY:
3524 	qop = "auth-int";
3525 	oparams->encode = &digestmd5_encode;
3526 	oparams->decode = &digestmd5_decode;
3527 	oparams->mech_ssf = 1;
3528 	break;
3529     case DIGEST_NOLAYER:
3530     default:
3531 	qop = "auth";
3532 	oparams->encode = NULL;
3533 	oparams->decode = NULL;
3534 	oparams->mech_ssf = 0;
3535     }
3536 
3537     if (text->http_mode) {
3538 	/* per RFC 2617 (HTTP Request as set by calling application) */
3539 	request = params->http_request;
3540     }
3541     else {
3542 	/* per RFC 2831 */
3543 	digesturi = params->utils->malloc(strlen(params->service) + 1 +
3544 					  strlen(params->serverFQDN) + 1 +
3545 					  1);
3546 	if (digesturi == NULL) {
3547 	    result = SASL_NOMEM;
3548 	    goto FreeAllocatedMem;
3549 	}
3550 
3551 	/* allocated exactly this. safe */
3552 	strcpy(digesturi, params->service);
3553 	strcat(digesturi, "/");
3554 	strcat(digesturi, params->serverFQDN);
3555 	/*
3556 	 * strcat (digesturi, "/"); strcat (digesturi, params->serverFQDN);
3557 	 */
3558 
3559 	rfc2831_request.method = "AUTHENTICATE";
3560 	rfc2831_request.uri = digesturi;
3561 	rfc2831_request.entity = NULL;
3562 	rfc2831_request.elen = 0;
3563 	rfc2831_request.non_persist = 0;
3564 	request = &rfc2831_request;
3565     }
3566 
3567     /* response */
3568     response =
3569 	calculate_response(text,
3570 			   params->utils,
3571 			   ctext->algorithm,
3572 			   (unsigned char *) oparams->authid,
3573 			   (unsigned char *) text->realm,
3574 			   text->nonce,
3575 			   text->nonce_count,
3576 			   text->cnonce,
3577 			   qop,
3578 			   request,
3579 			   ctext->password,
3580 			   strcmp(oparams->user, oparams->authid) ?
3581 			   (unsigned char *) oparams->user : NULL,
3582 			   &text->response_value);
3583 
3584 
3585     resplen = 0;
3586     if (text->out_buf) params->utils->free(text->out_buf);
3587     text->out_buf = NULL;
3588     text->out_buf_len = 0;
3589     if (add_to_challenge(params->utils,
3590 			 &text->out_buf, &text->out_buf_len, &resplen,
3591 			 "username", (unsigned char *) oparams->authid,
3592 			 TRUE) != SASL_OK) {
3593 	result = SASL_FAIL;
3594 	goto FreeAllocatedMem;
3595     }
3596 
3597     if (add_to_challenge(params->utils,
3598 			 &text->out_buf, &text->out_buf_len, &resplen,
3599 			 "realm", (unsigned char *) text->realm,
3600 			 TRUE) != SASL_OK) {
3601 	result = SASL_FAIL;
3602 	goto FreeAllocatedMem;
3603     }
3604     if (strcmp(oparams->user, oparams->authid)) {
3605 	if (add_to_challenge(params->utils,
3606 			     &text->out_buf, &text->out_buf_len, &resplen,
3607 			     "authzid", (unsigned char *) oparams->user, TRUE) != SASL_OK) {
3608 	    result = SASL_FAIL;
3609 	    goto FreeAllocatedMem;
3610 	}
3611     }
3612     if (add_to_challenge(params->utils,
3613 			 &text->out_buf, &text->out_buf_len, &resplen,
3614 			 "nonce", text->nonce, TRUE) != SASL_OK) {
3615 	result = SASL_FAIL;
3616 	goto FreeAllocatedMem;
3617     }
3618     if (add_to_challenge(params->utils,
3619 			 &text->out_buf, &text->out_buf_len, &resplen,
3620 			 "cnonce", text->cnonce, TRUE) != SASL_OK) {
3621 	result = SASL_FAIL;
3622 	goto FreeAllocatedMem;
3623     }
3624     snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count);
3625     if (add_to_challenge(params->utils,
3626 			 &text->out_buf, &text->out_buf_len, &resplen,
3627 			 "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) {
3628 	result = SASL_FAIL;
3629 	goto FreeAllocatedMem;
3630     }
3631     if (add_to_challenge(params->utils,
3632 			 &text->out_buf, &text->out_buf_len, &resplen,
3633 			 "qop", (unsigned char *) qop, FALSE) != SASL_OK) {
3634 	result = SASL_FAIL;
3635 	goto FreeAllocatedMem;
3636     }
3637     if (ctext->cipher != NULL) {
3638 	if (add_to_challenge(params->utils,
3639 			     &text->out_buf, &text->out_buf_len, &resplen,
3640 			     "cipher",
3641 			     (unsigned char *) ctext->cipher->name,
3642 			     FALSE) != SASL_OK) {
3643 	    result = SASL_FAIL;
3644 	    goto FreeAllocatedMem;
3645 	}
3646     }
3647 
3648     if (params->props.maxbufsize) {
3649 	snprintf(maxbufstr, sizeof(maxbufstr), "%d", params->props.maxbufsize);
3650 	if (add_to_challenge(params->utils,
3651 			     &text->out_buf, &text->out_buf_len, &resplen,
3652 			     "maxbuf", (unsigned char *) maxbufstr,
3653 			     FALSE) != SASL_OK) {
3654 	    SETERROR(params->utils,
3655 		     "internal error: add_to_challenge maxbuf failed");
3656 	    goto FreeAllocatedMem;
3657 	}
3658     }
3659 
3660     if (IsUTF8) {
3661 	if (add_to_challenge(params->utils,
3662 			     &text->out_buf, &text->out_buf_len, &resplen,
3663 			     "charset", (unsigned char *) "utf-8",
3664 			     FALSE) != SASL_OK) {
3665 	    result = SASL_FAIL;
3666 	    goto FreeAllocatedMem;
3667 	}
3668     }
3669     if (add_to_challenge(params->utils,
3670 			 &text->out_buf, &text->out_buf_len, &resplen,
3671 			 text->http_mode ? "uri"	     /* per RFC 2617 */
3672 			 : "digest-uri",   		     /* per RFC 2831 */
3673 			 (unsigned char *) request->uri,
3674 			 TRUE) != SASL_OK) {
3675 	result = SASL_FAIL;
3676 	goto FreeAllocatedMem;
3677     }
3678     if (text->http_mode) {
3679 	/* per RFC 2617: algorithm & opaque MUST be sent back to server */
3680 	if (add_to_challenge(params->utils,
3681 			     &text->out_buf, &text->out_buf_len, &resplen,
3682 			     "algorithm", (unsigned char *) ctext->algorithm,
3683 			     FALSE) != SASL_OK) {
3684 	    result = SASL_FAIL;
3685 	    goto FreeAllocatedMem;
3686 	}
3687 	if (ctext->opaque) {
3688 	    if (add_to_challenge(params->utils,
3689 				 &text->out_buf, &text->out_buf_len, &resplen,
3690 				 "opaque", ctext->opaque, TRUE) != SASL_OK) {
3691 		result = SASL_FAIL;
3692 		goto FreeAllocatedMem;
3693 	    }
3694 	}
3695     }
3696     if (add_to_challenge(params->utils,
3697 			 &text->out_buf, &text->out_buf_len, &resplen,
3698 			 "response", (unsigned char *) response,
3699 			 FALSE) != SASL_OK) {
3700 
3701 	result = SASL_FAIL;
3702 	goto FreeAllocatedMem;
3703     }
3704 
3705     /* self check */
3706     if (strlen(text->out_buf) > 2048) {
3707 	result = SASL_FAIL;
3708 	goto FreeAllocatedMem;
3709     }
3710 
3711     /* set oparams */
3712     oparams->maxoutbuf = ctext->server_maxbuf;
3713     if(oparams->mech_ssf > 1) {
3714 	/* MAC block (privacy) */
3715 	oparams->maxoutbuf -= 25;
3716     } else if(oparams->mech_ssf == 1) {
3717 	/* MAC block (integrity) */
3718 	oparams->maxoutbuf -= 16;
3719     }
3720 
3721     text->seqnum = 0;	/* for integrity/privacy */
3722     text->rec_seqnum = 0;	/* for integrity/privacy */
3723     text->utils = params->utils;
3724 
3725     /* Free the old security layer, if any */
3726     if (old_cipher_free) old_cipher_free(text);
3727 
3728     /* used by layers */
3729     _plug_decode_init(&text->decode_context, text->utils,
3730 		      params->props.maxbufsize ? params->props.maxbufsize :
3731 		      DEFAULT_BUFSIZE);
3732 
3733     if (oparams->mech_ssf > 0) {
3734 	unsigned char enckey[16];
3735 	unsigned char deckey[16];
3736 
3737 	create_layer_keys(text, params->utils, text->HA1, nbits,
3738 			  enckey, deckey);
3739 
3740 	/* initialize cipher if need be */
3741 	if (text->cipher_init) {
3742 	    text->cipher_init(text, enckey, deckey);
3743 	}
3744     }
3745 
3746     result = SASL_OK;
3747 
3748   FreeAllocatedMem:
3749     if (digesturi) params->utils->free(digesturi);
3750     if (response) params->utils->free(response);
3751 
3752     return result;
3753 }
3754 
parse_server_challenge(client_context_t * ctext,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,char *** outrealms,int * noutrealm)3755 static int parse_server_challenge(client_context_t *ctext,
3756 				  sasl_client_params_t *params,
3757 				  const char *serverin, unsigned serverinlen,
3758 				  char ***outrealms, int *noutrealm)
3759 {
3760     context_t *text = (context_t *) ctext;
3761     int result = SASL_OK;
3762     char *in_start = NULL;
3763     char *in = NULL;
3764     char **realms = NULL;
3765     int nrealm = 0;
3766     sasl_ssf_t limit, musthave = 0;
3767     sasl_ssf_t external;
3768     int protection = 0;
3769     int saw_qop = 0;
3770     int ciphers = 0;
3771     int maxbuf_count = 0;
3772     int algorithm_count = 0;
3773     int opaque_count = 0;
3774 
3775     params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
3776 		       "DIGEST-MD5 parse_server_challenge()");
3777 
3778     if (!serverin || !serverinlen) {
3779 	params->utils->seterror(params->utils->conn, 0,
3780 				"no server challenge");
3781 	return SASL_FAIL;
3782     }
3783 
3784     in_start = in = params->utils->malloc(serverinlen + 1);
3785     if (in == NULL) return SASL_NOMEM;
3786 
3787     memcpy(in, serverin, serverinlen);
3788     in[serverinlen] = 0;
3789 
3790     ctext->server_maxbuf = 65536; /* Default value for maxbuf */
3791 
3792     /* create a new cnonce */
3793     text->cnonce = create_nonce(params->utils);
3794     if (text->cnonce == NULL) {
3795 	params->utils->seterror(params->utils->conn, 0,
3796 				"failed to create cnonce");
3797 	result = SASL_FAIL;
3798 	goto FreeAllocatedMem;
3799     }
3800 
3801     /* parse the challenge */
3802     while (in[0] != '\0') {
3803 	char *name, *value;
3804 
3805 	get_pair(&in, &name, &value);
3806 
3807 	/* if parse error */
3808 	if (name == NULL) {
3809 	    params->utils->seterror(params->utils->conn, 0, "Parse error");
3810 	    result = SASL_BADAUTH;
3811 	    goto FreeAllocatedMem;
3812 	}
3813 
3814 	if (*name == '\0') {
3815 	    break;
3816 	}
3817 
3818 	if (strcasecmp(name, "realm") == 0) {
3819 	    nrealm++;
3820 
3821 	    if(!realms)
3822 		realms = params->utils->malloc(sizeof(char *) * (nrealm + 1));
3823 	    else
3824 		realms = params->utils->realloc(realms,
3825 						sizeof(char *) * (nrealm + 1));
3826 
3827 	    if (realms == NULL) {
3828 		result = SASL_NOMEM;
3829 		goto FreeAllocatedMem;
3830 	    }
3831 
3832 	    _plug_strdup(params->utils, value, &realms[nrealm-1], NULL);
3833 	    realms[nrealm] = NULL;
3834 	} else if (strcasecmp(name, "nonce") == 0) {
3835 	    _plug_strdup(params->utils, value, (char **) &text->nonce,
3836 			 NULL);
3837 	    text->nonce_count = 1;
3838 	} else if (strcasecmp(name, "qop") == 0) {
3839 	    saw_qop = 1;
3840 	    while (value && *value) {
3841 		char *comma;
3842 		char *end_val;
3843 
3844 SKIP_SPACES_IN_QOP:
3845 		/* skipping spaces: */
3846 		value = skip_lws(value);
3847 		if (*value == '\0') {
3848 		    break;
3849 		}
3850 
3851 		/* check for an extreme case when there is no data: LWSP ',' */
3852 		if (*value == ',') {
3853 		    value++;
3854 		    goto SKIP_SPACES_IN_QOP;
3855 		}
3856 
3857 		comma = strchr(value, ',');
3858 
3859 		if (comma != NULL) {
3860 		    *comma++ = '\0';
3861 		}
3862 
3863 		/* skip LWSP at the end of the value (if any), skip_r_lws returns pointer to
3864 		   the first LWSP character, NUL (if there were none) or NULL if the value
3865 		   is entirely from LWSP characters */
3866 		end_val = skip_r_lws (value);
3867 		if (end_val == NULL) {
3868 		    value = comma;
3869 		    continue;
3870 		} else {
3871 		    /* strip LWSP */
3872 		    *end_val = '\0';
3873 		}
3874 
3875 		if (strcasecmp(value, "auth-conf") == 0) {
3876 		    protection |= DIGEST_PRIVACY;
3877 		} else if (strcasecmp(value, "auth-int") == 0) {
3878 		    protection |= DIGEST_INTEGRITY;
3879 		} else if (strcasecmp(value, "auth") == 0) {
3880 		    protection |= DIGEST_NOLAYER;
3881 		} else {
3882 		    params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
3883 				       "Server supports unknown layer: %s\n",
3884 				       value);
3885 		}
3886 
3887 		value = comma;
3888 	    }
3889 	} else if (strcasecmp(name, "cipher") == 0) {
3890 	    while (value && *value) {
3891 		struct digest_cipher *cipher = available_ciphers;
3892 		char *comma;
3893 		char *end_val;
3894 
3895 SKIP_SPACES_IN_CIPHER:
3896 		/* skipping spaces: */
3897 		value = skip_lws(value);
3898 		if (*value == '\0') {
3899 		    break;
3900 		}
3901 
3902 		/* check for an extreme case when there is no data: LWSP ',' */
3903 		if (*value == ',') {
3904 		    value++;
3905 		    goto SKIP_SPACES_IN_CIPHER;
3906 		}
3907 
3908 		comma = strchr(value, ',');
3909 
3910 		if (comma != NULL) {
3911 		    *comma++ = '\0';
3912 		}
3913 
3914 		/* skip LWSP at the end of the value, skip_r_lws returns pointer to
3915 		   the first LWSP character or NULL */
3916 		end_val = skip_r_lws (value);
3917 		if (end_val == NULL) {
3918 		    value = comma;
3919 		    continue;
3920 		} else {
3921 		    /* strip LWSP */
3922 		    *end_val = '\0';
3923 		}
3924 
3925 		/* do we support this cipher? */
3926 		while (cipher->name) {
3927 		    if (!strcasecmp(value, cipher->name)) break;
3928 		    cipher++;
3929 		}
3930 		if (cipher->name) {
3931 		    ciphers |= cipher->flag;
3932 		} else {
3933 		    params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
3934 				       "Server supports unknown cipher: %s\n",
3935 				       value);
3936 		}
3937 
3938 		value = comma;
3939 	    }
3940 	} else if (strcasecmp(name, "stale") == 0 && ctext->password) {
3941 	    /* clear any cached password */
3942 	    if (ctext->free_password)
3943 		_plug_free_secret(params->utils, &ctext->password);
3944 	    ctext->password = NULL;
3945 	} else if (strcasecmp(name, "maxbuf") == 0) {
3946 	    /* maxbuf A number indicating the size of the largest
3947 	     * buffer the server is able to receive when using
3948 	     * "auth-int". If this directive is missing, the default
3949 	     * value is 65536. This directive may appear at most once;
3950 	     * if multiple instances are present, the client should
3951 	     * abort the authentication exchange.
3952 	     */
3953 	    maxbuf_count++;
3954 
3955 	    if (maxbuf_count != 1) {
3956 		result = SASL_BADAUTH;
3957 		params->utils->seterror(params->utils->conn, 0,
3958 					"At least two maxbuf directives found. Authentication aborted");
3959 		goto FreeAllocatedMem;
3960 	    }
3961 
3962 	    if (str2ul32 (value, &ctext->server_maxbuf) == FALSE) {
3963 		result = SASL_BADAUTH;
3964 		params->utils->seterror(params->utils->conn, 0,
3965 					"Invalid maxbuf parameter received from server (%s)", value);
3966 		goto FreeAllocatedMem;
3967 	    }
3968 
3969 	    if (ctext->server_maxbuf <= 16) {
3970 		result = SASL_BADAUTH;
3971 		params->utils->seterror(params->utils->conn, 0,
3972 					"Invalid maxbuf parameter received from server (too small: %s)", value);
3973 		goto FreeAllocatedMem;
3974 	    }
3975 
3976 	    if (ctext->server_maxbuf > MAX_SASL_BUFSIZE) {
3977 		result = SASL_BADAUTH;
3978 		params->utils->seterror(params->utils->conn, 0,
3979 					"Invalid maxbuf parameter received from server (too big: %s)", value);
3980 		goto FreeAllocatedMem;
3981 	    }
3982 	} else if (strcasecmp(name, "charset") == 0) {
3983 	    if (strcasecmp(value, "utf-8") != 0) {
3984 		result = SASL_BADAUTH;
3985 		params->utils->seterror(params->utils->conn, 0,
3986 					"Charset must be UTF-8");
3987 		goto FreeAllocatedMem;
3988 	    }
3989 	} else if (strcasecmp(name,"algorithm")==0) {
3990 	    if (text->http_mode && strcasecmp(value, "md5") == 0) {
3991 		/* per RFC 2617: need to support both "md5" and "md5-sess" */
3992 	    }
3993 	    else if (strcasecmp(value, "md5-sess") != 0)
3994 		{
3995 		    params->utils->seterror(params->utils->conn, 0,
3996 					    "'algorithm' isn't 'md5-sess'");
3997 		    result = SASL_FAIL;
3998 		    goto FreeAllocatedMem;
3999 		}
4000 
4001 	    if (text->http_mode) {
4002 		/* per RFC 2617: algorithm MUST be saved */
4003 		_plug_strdup(params->utils, value, (char **) &ctext->algorithm,
4004 			     NULL);
4005 	    }
4006 	    algorithm_count++;
4007 	    if (algorithm_count > 1)
4008 		{
4009 		    params->utils->seterror(params->utils->conn, 0,
4010 					    "Must see 'algorithm' only once");
4011 		    result = SASL_FAIL;
4012 		    goto FreeAllocatedMem;
4013 		}
4014 	} else if (strcasecmp(name,"opaque")==0) {
4015 	    /* per RFC 2831: opaque MUST be ignored if received */
4016 	    if (text->http_mode) {
4017 		/* per RFC 2617: opaque MUST be saved */
4018 		_plug_strdup(params->utils, value, (char **) &ctext->opaque,
4019 			     NULL);
4020 		opaque_count++;
4021 		if (opaque_count > 1)
4022 		    {
4023 			params->utils->seterror(params->utils->conn, 0,
4024 						"Must see 'opaque' only once");
4025 			result = SASL_FAIL;
4026 			goto FreeAllocatedMem;
4027 		    }
4028 	    }
4029 	} else {
4030 	    params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4031 			       "DIGEST-MD5 unrecognized pair %s/%s: ignoring",
4032 			       name, value);
4033 	}
4034     }
4035 
4036     if (protection == 0) {
4037 	/* From RFC 2831[bis]:
4038 	   This directive is optional; if not present it defaults to "auth". */
4039 	if (saw_qop == 0) {
4040 	    protection = DIGEST_NOLAYER;
4041 	} else {
4042 	    result = SASL_BADAUTH;
4043 	    params->utils->seterror(params->utils->conn, 0,
4044 				    "Server doesn't support any known qop level");
4045 	    goto FreeAllocatedMem;
4046 	}
4047     }
4048 
4049     if (algorithm_count != 1) {
4050 	params->utils->seterror(params->utils->conn, 0,
4051 				"Must see 'algorithm' once. Didn't see at all");
4052 	result = SASL_FAIL;
4053 	goto FreeAllocatedMem;
4054     }
4055 
4056     /* make sure we have everything we require */
4057     if (text->nonce == NULL) {
4058 	params->utils->seterror(params->utils->conn, 0,
4059 				"Don't have nonce.");
4060 	result = SASL_FAIL;
4061 	goto FreeAllocatedMem;
4062     }
4063 
4064     /* get requested ssf */
4065     external = params->external_ssf;
4066 
4067     /* what do we _need_?  how much is too much? */
4068     if (!text->http_mode &&    	       	   /* HTTP Digest doesn't need buffer */
4069 	params->props.maxbufsize == 0) {
4070 	musthave = 0;
4071 	limit = 0;
4072     } else {
4073 	if (params->props.max_ssf > external) {
4074 	    limit = params->props.max_ssf - external;
4075 	} else {
4076 	    limit = 0;
4077 	}
4078 	if (params->props.min_ssf > external) {
4079 	    musthave = params->props.min_ssf - external;
4080 	} else {
4081 	    musthave = 0;
4082 	}
4083     }
4084 
4085     /* we now go searching for an option that gives us at least "musthave"
4086        and at most "limit" bits of ssf. */
4087     if ((limit > 1) && (protection & DIGEST_PRIVACY)) {
4088 	struct digest_cipher *cipher;
4089 
4090 	/* let's find an encryption scheme that we like */
4091 	cipher = available_ciphers;
4092 	while (cipher->name) {
4093 	    /* examine each cipher we support, see if it meets our security
4094 	       requirements, and see if the server supports it.
4095 	       choose the best one of these */
4096 	    if ((limit >= cipher->ssf) && (musthave <= cipher->ssf) &&
4097 		(ciphers & cipher->flag) &&
4098 		(!ctext->cipher || (cipher->ssf > ctext->cipher->ssf))) {
4099 		ctext->cipher = cipher;
4100 	    }
4101 	    cipher++;
4102 	}
4103 
4104 	if (ctext->cipher) {
4105 	    /* we found a cipher we like */
4106 	    ctext->protection = DIGEST_PRIVACY;
4107 	} else {
4108 	    /* we didn't find any ciphers we like */
4109 	    params->utils->seterror(params->utils->conn, 0,
4110 				    "No good privacy layers");
4111 	}
4112     }
4113 
4114     if (ctext->cipher == NULL) {
4115 	/* we failed to find an encryption layer we liked;
4116 	   can we use integrity or nothing? */
4117 
4118 	if ((limit >= 1) && (musthave <= 1)
4119 	    && (protection & DIGEST_INTEGRITY)) {
4120 	    /* integrity */
4121 	    ctext->protection = DIGEST_INTEGRITY;
4122 	} else if (musthave <= 0) {
4123 	    /* no layer */
4124 	    ctext->protection = DIGEST_NOLAYER;
4125 
4126 	    /* See if server supports not having a layer */
4127 	    if ((protection & DIGEST_NOLAYER) != DIGEST_NOLAYER) {
4128 		params->utils->seterror(params->utils->conn, 0,
4129 					"Server doesn't support \"no layer\"");
4130 		result = SASL_FAIL;
4131 		goto FreeAllocatedMem;
4132 	    }
4133 	} else {
4134 	    params->utils->seterror(params->utils->conn, 0,
4135 				    "Can't find an acceptable layer");
4136 	    result = SASL_TOOWEAK;
4137 	    goto FreeAllocatedMem;
4138 	}
4139     }
4140 
4141     *outrealms = realms;
4142     *noutrealm = nrealm;
4143 
4144   FreeAllocatedMem:
4145     if (in_start) params->utils->free(in_start);
4146 
4147     if (result != SASL_OK && realms) {
4148 	int lup;
4149 
4150 	/* need to free all the realms */
4151 	for (lup = 0;lup < nrealm; lup++)
4152 	    params->utils->free(realms[lup]);
4153 
4154 	params->utils->free(realms);
4155     }
4156 
4157     return result;
4158 }
4159 
ask_user_info(client_context_t * ctext,sasl_client_params_t * params,char ** realms,int nrealm,sasl_interact_t ** prompt_need,sasl_out_params_t * oparams)4160 static int ask_user_info(client_context_t *ctext,
4161 			 sasl_client_params_t *params,
4162 			 char **realms, int nrealm,
4163 			 sasl_interact_t **prompt_need,
4164 			 sasl_out_params_t *oparams)
4165 {
4166     context_t *text = (context_t *) ctext;
4167     int result = SASL_OK;
4168     const char *authid = NULL, *userid = NULL, *realm = NULL;
4169     char *realm_chal = NULL;
4170     int user_result = SASL_OK;
4171     int auth_result = SASL_OK;
4172     int pass_result = SASL_OK;
4173     int realm_result = SASL_FAIL;
4174     int i;
4175     size_t len;
4176 
4177     params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4178 		       "DIGEST-MD5 ask_user_info()");
4179 
4180     /* try to get the authid */
4181     if (oparams->authid == NULL) {
4182 	auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
4183 
4184 	if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) {
4185 	    return auth_result;
4186 	}
4187     }
4188 
4189     /* try to get the userid */
4190     if (oparams->user == NULL) {
4191 	user_result = _plug_get_userid(params->utils, &userid, prompt_need);
4192 
4193 	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
4194 	    return user_result;
4195 	}
4196     }
4197 
4198     /* try to get the password */
4199     if (ctext->password == NULL) {
4200 	pass_result = _plug_get_password(params->utils, &ctext->password,
4201 					 &ctext->free_password, prompt_need);
4202 	if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) {
4203 	    return pass_result;
4204 	}
4205     }
4206 
4207     /* try to get the realm */
4208     if (text->realm == NULL) {
4209 	if (realms) {
4210 	    if(nrealm == 1) {
4211 		/* only one choice */
4212 		realm = realms[0];
4213 		realm_result = SASL_OK;
4214 	    } else {
4215 		/* ask the user */
4216 		realm_result = _plug_get_realm(params->utils,
4217 					       (const char **) realms,
4218 					       (const char **) &realm,
4219 					       prompt_need);
4220 	    }
4221 	}
4222 
4223 	/* fake the realm if we must */
4224 	if ((realm_result != SASL_OK) && (realm_result != SASL_INTERACT)) {
4225 	    if (params->serverFQDN) {
4226 		realm = params->serverFQDN;
4227 	    } else {
4228 		return realm_result;
4229 	    }
4230 	}
4231     }
4232 
4233     /* free prompts we got */
4234     if (prompt_need && *prompt_need) {
4235 	params->utils->free(*prompt_need);
4236 	*prompt_need = NULL;
4237     }
4238 
4239     /* if there are prompts not filled in */
4240     if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
4241 	(pass_result == SASL_INTERACT) || (realm_result == SASL_INTERACT)) {
4242 
4243 	/* make our default realm */
4244 	if (realm_result == SASL_INTERACT) {
4245 	    if (realms) {
4246 		len = strlen(REALM_CHAL_PREFIX);
4247 		for (i = 0; i < nrealm; i++) {
4248 		    len += strlen(realms[i]) + 4 /* " {}," */;
4249 		}
4250 		realm_chal = params->utils->malloc(len + 1);
4251 		strcpy (realm_chal, REALM_CHAL_PREFIX);
4252 		for (i = 0; i < nrealm; i++) {
4253 		    strcat (realm_chal, " {");
4254 		    strcat (realm_chal, realms[i]);
4255 		    strcat (realm_chal, "},");
4256 		}
4257 		/* Replace the terminating comma with dot */
4258 		realm_chal[len-1] = '.';
4259 
4260 	    } else if (params->serverFQDN) {
4261 		realm_chal = params->utils->malloc(3+strlen(params->serverFQDN));
4262 		if (realm_chal) {
4263 		    sprintf(realm_chal, "{%s}", params->serverFQDN);
4264 		} else {
4265 		    return SASL_NOMEM;
4266 		}
4267 	    }
4268 	}
4269 
4270 	/* make the prompt list */
4271 	result =
4272 	    _plug_make_prompts(params->utils, prompt_need,
4273 			       user_result == SASL_INTERACT ?
4274 			       "Please enter your authorization name" : NULL,
4275 			       NULL,
4276 			       auth_result == SASL_INTERACT ?
4277 			       "Please enter your authentication name" : NULL,
4278 			       NULL,
4279 			       pass_result == SASL_INTERACT ?
4280 			       "Please enter your password" : NULL, NULL,
4281 			       NULL, NULL, NULL,
4282 			       realm_chal ? realm_chal : "{}",
4283 			       realm_result == SASL_INTERACT ?
4284 			       "Please enter your realm" : NULL,
4285 			       params->serverFQDN ? params->serverFQDN : NULL);
4286 
4287 	if (result == SASL_OK) return SASL_INTERACT;
4288 
4289 	return result;
4290     }
4291 
4292     if (oparams->authid == NULL) {
4293 	if (!userid || !*userid) {
4294 	    result = params->canon_user(params->utils->conn, authid, 0,
4295 					SASL_CU_AUTHID | SASL_CU_AUTHZID,
4296 					oparams);
4297 	}
4298 	else {
4299 	    result = params->canon_user(params->utils->conn,
4300 					authid, 0, SASL_CU_AUTHID, oparams);
4301 	    if (result != SASL_OK) return result;
4302 
4303 	    result = params->canon_user(params->utils->conn,
4304 					userid, 0, SASL_CU_AUTHZID, oparams);
4305 	}
4306 	if (result != SASL_OK) return result;
4307     }
4308 
4309     /* Get an allocated version of the realm into the structure */
4310     if (realm && text->realm == NULL) {
4311 	_plug_strdup(params->utils, realm, (char **) &text->realm, NULL);
4312     }
4313 
4314     return result;
4315 }
4316 
digestmd5_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)4317 static int digestmd5_client_mech_new(void *glob_context,
4318 				     sasl_client_params_t * params,
4319 				     void **conn_context)
4320 {
4321     context_t *text;
4322 
4323     if ((params->flags & SASL_NEED_HTTP) && !params->http_request) {
4324 	SETERROR(params->utils,
4325 		 "DIGEST-MD5 unavailable due to lack of HTTP request");
4326 	return SASL_BADPARAM;
4327     }
4328 
4329     /* holds state are in -- allocate client size */
4330     text = params->utils->malloc(sizeof(client_context_t));
4331     if (text == NULL)
4332 	return SASL_NOMEM;
4333     memset((client_context_t *)text, 0, sizeof(client_context_t));
4334 
4335     text->state = 1;
4336     text->i_am = CLIENT;
4337     text->http_mode = (params->flags & SASL_NEED_HTTP);
4338     text->reauth = ((digest_glob_context_t *) glob_context)->reauth;
4339 
4340     *conn_context = text;
4341 
4342     return SASL_OK;
4343 }
4344 
4345 static int
digestmd5_client_mech_step1(client_context_t * ctext,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)4346 digestmd5_client_mech_step1(client_context_t *ctext,
4347 			    sasl_client_params_t *params,
4348 			    const char *serverin __attribute__((unused)),
4349 			    unsigned serverinlen __attribute__((unused)),
4350 			    sasl_interact_t **prompt_need,
4351 			    const char **clientout,
4352 			    unsigned *clientoutlen,
4353 			    sasl_out_params_t *oparams)
4354 {
4355     context_t *text = (context_t *) ctext;
4356     int result = SASL_FAIL;
4357     unsigned val;
4358 
4359     params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4360 		       "DIGEST-MD5 client step 1");
4361 
4362     result = ask_user_info(ctext, params, NULL, 0, prompt_need, oparams);
4363     if (result != SASL_OK) return result;
4364 
4365     /* check if we have cached info for this user on this server */
4366     val = hash(params->serverFQDN) % text->reauth->size;
4367     if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
4368 	if (text->reauth->e[val].u.c.serverFQDN &&
4369 	    !strcasecmp(text->reauth->e[val].u.c.serverFQDN,
4370 			params->serverFQDN) &&
4371 	    !strcmp(text->reauth->e[val].authid, oparams->authid)) {
4372 
4373 	    /* we have info, so use it */
4374 	    if (text->realm) params->utils->free(text->realm);
4375 	    _plug_strdup(params->utils, text->reauth->e[val].realm,
4376 			 &text->realm, NULL);
4377 	    _plug_strdup(params->utils, (char *) text->reauth->e[val].nonce,
4378 			 (char **) &text->nonce, NULL);
4379 	    text->nonce_count = ++text->reauth->e[val].nonce_count;
4380 	    _plug_strdup(params->utils, (char *) text->reauth->e[val].cnonce,
4381 			 (char **) &text->cnonce, NULL);
4382 	    if (text->http_mode) {
4383 		/* per RFC 2617: algorithm & opaque MUST be sent back to server */
4384 		_plug_strdup(params->utils,
4385 			     (char *) text->reauth->e[val].u.c.algorithm,
4386 			     (char **) &ctext->algorithm, NULL);
4387 		if (text->reauth->e[val].u.c.opaque) {
4388 		    _plug_strdup(params->utils,
4389 				 (char *) text->reauth->e[val].u.c.opaque,
4390 				 (char **) &ctext->opaque, NULL);
4391 		}
4392 	    }
4393 	    ctext->protection = text->reauth->e[val].u.c.protection;
4394 	    ctext->cipher = text->reauth->e[val].u.c.cipher;
4395 	    ctext->server_maxbuf = text->reauth->e[val].u.c.server_maxbuf;
4396 	}
4397 	params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
4398     }
4399 
4400     if (!text->nonce) {
4401 	/* we don't have any reauth info, so just return
4402 	 * that there is no initial client send */
4403 	text->state = 2;
4404 	return SASL_CONTINUE;
4405     }
4406 
4407     /*
4408      * (username | realm | nonce | cnonce | nonce-count | qop digest-uri |
4409      * response | maxbuf | charset | auth-param )
4410      */
4411 
4412     result = make_client_response(text, params, oparams);
4413     if (result != SASL_OK) return result;
4414 
4415     *clientoutlen = (unsigned) strlen(text->out_buf);
4416     *clientout = text->out_buf;
4417 
4418     /* check for next state (2 or 3) is done in digestmd5_client_mech_step() */
4419     return SASL_CONTINUE;
4420 }
4421 
digestmd5_client_mech_step2(client_context_t * ctext,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)4422 static int digestmd5_client_mech_step2(client_context_t *ctext,
4423 				       sasl_client_params_t *params,
4424 				       const char *serverin,
4425 				       unsigned serverinlen,
4426 				       sasl_interact_t **prompt_need,
4427 				       const char **clientout,
4428 				       unsigned *clientoutlen,
4429 				       sasl_out_params_t *oparams)
4430 {
4431     context_t *text = (context_t *) ctext;
4432     int result = SASL_FAIL;
4433     char **realms = NULL;
4434     int nrealm = 0;
4435 
4436     params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4437 		       "DIGEST-MD5 client step 2");
4438 
4439     if (params->props.min_ssf > params->props.max_ssf) {
4440 	return SASL_BADPARAM;
4441     }
4442 
4443     /* don't bother parsing the challenge more than once */
4444     if (text->nonce == NULL) {
4445 	result = parse_server_challenge(ctext, params, serverin, serverinlen,
4446 					&realms, &nrealm);
4447 	if (result != SASL_OK) goto FreeAllocatedMem;
4448 
4449 	if (nrealm == 1) {
4450 	    /* only one choice! */
4451 	    if (text->realm) params->utils->free(text->realm);
4452 	    text->realm = realms[0];
4453 
4454 	    /* free realms */
4455 	    params->utils->free(realms);
4456 	    realms = NULL;
4457 	} else {
4458 	    /* Save realms for later use */
4459 	    text->realms = realms;
4460 	    text->realm_cnt = nrealm;
4461 	}
4462     } else {
4463 	/* Restore the list of realms */
4464 	realms = text->realms;
4465 	nrealm = text->realm_cnt;
4466     }
4467 
4468     result = ask_user_info(ctext, params, realms, nrealm,
4469 			   prompt_need, oparams);
4470     if (result != SASL_OK) goto FreeAllocatedMem;
4471 
4472     /*
4473      * (username | realm | nonce | cnonce | nonce-count | qop | digest-uri |
4474      *  response | maxbuf | charset | auth-param )
4475      */
4476 
4477     result = make_client_response(text, params, oparams);
4478     if (result != SASL_OK) goto FreeAllocatedMem;
4479 
4480     *clientoutlen = (unsigned) strlen(text->out_buf);
4481     *clientout = text->out_buf;
4482 
4483     text->state = 3;
4484 
4485     result = SASL_CONTINUE;
4486 
4487   FreeAllocatedMem:
4488     return result;
4489 }
4490 
4491 static int
digestmd5_client_mech_step3(client_context_t * ctext,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)4492 digestmd5_client_mech_step3(client_context_t *ctext,
4493 			    sasl_client_params_t *params,
4494 			    const char *serverin,
4495 			    unsigned serverinlen,
4496 			    sasl_interact_t **prompt_need __attribute__((unused)),
4497 			    const char **clientout __attribute__((unused)),
4498 			    unsigned *clientoutlen __attribute__((unused)),
4499 			    sasl_out_params_t *oparams)
4500 {
4501     context_t *text = (context_t *) ctext;
4502     char           *in = NULL;
4503     char           *in_start;
4504     int result = SASL_FAIL;
4505 
4506     params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4507 		       "DIGEST-MD5 client step 3");
4508 
4509     /* Verify that server is really what he claims to be */
4510     in_start = in = params->utils->malloc(serverinlen + 1);
4511     if (in == NULL) return SASL_NOMEM;
4512 
4513     memcpy(in, serverin, serverinlen);
4514     in[serverinlen] = 0;
4515 
4516     /* parse the response */
4517     while (in[0] != '\0') {
4518 	char *name, *value;
4519 	get_pair(&in, &name, &value);
4520 
4521 	if (name == NULL) {
4522 	    params->utils->seterror(params->utils->conn, 0,
4523 				    "DIGEST-MD5 Received Garbage");
4524 	    result = SASL_BADAUTH;
4525 	    break;
4526 	}
4527 
4528 	if (*name == '\0') {
4529 	    break;
4530 	}
4531 
4532 	if (strcasecmp(name, "rspauth") == 0) {
4533 
4534 	    if (strcmp(text->response_value, value) != 0) {
4535 		params->utils->seterror(params->utils->conn, 0,
4536 					"DIGEST-MD5: This server wants us to believe that he knows shared secret");
4537 		result = SASL_BADSERV;
4538 	    } else {
4539 		oparams->doneflag = 1;
4540 		oparams->param_version = 0;
4541 
4542 		result = SASL_OK;
4543 	    }
4544 	    break;
4545 	} else {
4546 	    params->utils->log(params->utils->conn, SASL_LOG_DEBUG,
4547 			       "DIGEST-MD5 unrecognized pair %s/%s: ignoring",
4548 			       name, value);
4549 	}
4550     }
4551 
4552     params->utils->free(in_start);
4553 
4554     if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
4555 	unsigned val = hash(params->serverFQDN) % text->reauth->size;
4556 	switch (result) {
4557 	case SASL_OK:
4558 	    if (text->nonce_count == 1) {
4559 		/* successful initial auth, setup for future reauth */
4560 		clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils);
4561 		_plug_strdup(params->utils, oparams->authid,
4562 			     &text->reauth->e[val].authid, NULL);
4563 		text->reauth->e[val].realm = text->realm; text->realm = NULL;
4564 		text->reauth->e[val].nonce = text->nonce; text->nonce = NULL;
4565 		text->reauth->e[val].nonce_count = text->nonce_count;
4566 		text->reauth->e[val].cnonce = text->cnonce; text->cnonce = NULL;
4567 		_plug_strdup(params->utils, params->serverFQDN,
4568 			     &text->reauth->e[val].u.c.serverFQDN, NULL);
4569 		if (text->http_mode) {
4570 		    /* per RFC 2617: algorithm & opaque MUST be saved */
4571 		    text->reauth->e[val].u.c.algorithm = ctext->algorithm;
4572 		    ctext->algorithm = NULL;
4573 		    text->reauth->e[val].u.c.opaque = ctext->opaque;
4574 		    ctext->opaque = NULL;
4575 		}
4576 		text->reauth->e[val].u.c.protection = ctext->protection;
4577 		text->reauth->e[val].u.c.cipher = ctext->cipher;
4578 		text->reauth->e[val].u.c.server_maxbuf = ctext->server_maxbuf;
4579 	    }
4580 	    else {
4581 		/* reauth, we already incremented nonce_count */
4582 	    }
4583 	    break;
4584 	default:
4585 	    if (text->nonce_count > 1) {
4586 		/* failed reauth, clear cache */
4587 		clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils);
4588 	    }
4589 	    else {
4590 		/* failed initial auth, leave existing cache */
4591 	    }
4592 	}
4593 	params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
4594     }
4595 
4596     return result;
4597 }
4598 
digestmd5_client_mech_step(void * conn_context,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)4599 static int digestmd5_client_mech_step(void *conn_context,
4600 				      sasl_client_params_t *params,
4601 				      const char *serverin,
4602 				      unsigned serverinlen,
4603 				      sasl_interact_t **prompt_need,
4604 				      const char **clientout,
4605 				      unsigned *clientoutlen,
4606 				      sasl_out_params_t *oparams)
4607 {
4608     context_t *text = (context_t *) conn_context;
4609     client_context_t *ctext = (client_context_t *) conn_context;
4610     unsigned val = hash(params->serverFQDN) % text->reauth->size;
4611 
4612     if (serverinlen > 2048) return SASL_BADPROT;
4613 
4614     *clientout = NULL;
4615     *clientoutlen = 0;
4616 
4617     switch (text->state) {
4618 
4619     case 1:
4620 	if (!serverin) {
4621 	    /* here's where we attempt fast reauth if possible */
4622 	    int reauth = 0;
4623 
4624 	    /* check if we have saved info for this server */
4625 	    if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
4626 		reauth = text->reauth->e[val].u.c.serverFQDN &&
4627 		    !strcasecmp(text->reauth->e[val].u.c.serverFQDN,
4628 				params->serverFQDN);
4629 		params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
4630 	    }
4631 	    if (reauth) {
4632 		return digestmd5_client_mech_step1(ctext, params,
4633 						   serverin, serverinlen,
4634 						   prompt_need,
4635 						   clientout, clientoutlen,
4636 						   oparams);
4637 	    }
4638 	    else {
4639 		/* we don't have any reauth info, so just return
4640 		 * that there is no initial client send */
4641 		text->state = 2;
4642 		return SASL_CONTINUE;
4643 	    }
4644 	}
4645 	else if (!strncasecmp(serverin, "rspauth=", 8)) {
4646 	    /* server accepted fast reauth */
4647 	    text->state = 3;
4648 	    goto step3;
4649 	}
4650 
4651 	/* fall through and respond to challenge */
4652 	text->state = 2;
4653 
4654 	/* cleanup after a failed reauth attempt */
4655 	if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */
4656 	    clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils);
4657 
4658 	    params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */
4659 	}
4660 
4661 	if (text->realm) params->utils->free(text->realm);
4662 	if (text->nonce) params->utils->free(text->nonce);
4663 	if (text->cnonce) params->utils->free(text->cnonce);
4664 	text->realm = NULL;
4665 	text->nonce = text->cnonce = NULL;
4666 	ctext->cipher = NULL;
4667 
4668         GCC_FALLTHROUGH
4669 
4670     case 2:
4671 	return digestmd5_client_mech_step2(ctext, params,
4672 					   serverin, serverinlen,
4673 					   prompt_need,
4674 					   clientout, clientoutlen,
4675 					   oparams);
4676 
4677     case 3:
4678     step3:
4679 	return digestmd5_client_mech_step3(ctext, params,
4680 					   serverin, serverinlen,
4681 					   prompt_need,
4682 					   clientout, clientoutlen,
4683 					   oparams);
4684 
4685     default:
4686 	params->utils->log(NULL, SASL_LOG_ERR,
4687 			   "Invalid DIGEST-MD5 client step %d\n", text->state);
4688 	return SASL_FAIL;
4689     }
4690 
4691     return SASL_FAIL; /* should never get here */
4692 }
4693 
digestmd5_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)4694 static void digestmd5_client_mech_dispose(void *conn_context,
4695 					  const sasl_utils_t *utils)
4696 {
4697     client_context_t *ctext = (client_context_t *) conn_context;
4698 
4699     if (!ctext || !utils) return;
4700 
4701     utils->log(utils->conn, SASL_LOG_DEBUG,
4702 	       "DIGEST-MD5 client mech dispose");
4703 
4704     if (ctext->free_password) _plug_free_secret(utils, &ctext->password);
4705     if (ctext->algorithm) utils->free(ctext->algorithm);
4706     if (ctext->opaque) utils->free(ctext->opaque);
4707 
4708     digestmd5_common_mech_dispose(conn_context, utils);
4709 }
4710 
4711 static sasl_client_plug_t digestmd5_client_plugins[] =
4712 {
4713     {
4714 	"DIGEST-MD5",
4715 #ifdef WITH_RC4				/* mech_name */
4716 	128,				/* max ssf */
4717 #elif defined(WITH_DES)
4718 	112,
4719 #else
4720 	1,
4721 #endif
4722 	SASL_SEC_NOPLAINTEXT
4723 	| SASL_SEC_NOANONYMOUS
4724 	| SASL_SEC_MUTUAL_AUTH,		/* security_flags */
4725 	SASL_FEAT_NEEDSERVERFQDN
4726 	| SASL_FEAT_ALLOWS_PROXY
4727 	| SASL_FEAT_SUPPORTS_HTTP,	/* features */
4728 	NULL,				/* required_prompts */
4729 	&client_glob_context,		/* glob_context */
4730 	&digestmd5_client_mech_new,	/* mech_new */
4731 	&digestmd5_client_mech_step,	/* mech_step */
4732 	&digestmd5_client_mech_dispose,	/* mech_dispose */
4733 	&digestmd5_common_mech_free,	/* mech_free */
4734 	NULL,				/* idle */
4735 	NULL,				/* spare1 */
4736 	NULL				/* spare2 */
4737     }
4738 };
4739 
digestmd5_client_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)4740 int digestmd5_client_plug_init(sasl_utils_t *utils,
4741 			       int maxversion,
4742 			       int *out_version,
4743 			       sasl_client_plug_t **pluglist,
4744 			       int *plugcount)
4745 {
4746     reauth_cache_t *reauth_cache;
4747 
4748     if (maxversion < SASL_CLIENT_PLUG_VERSION)
4749 	return SASL_BADVERS;
4750 
4751     /* reauth cache */
4752     reauth_cache = utils->malloc(sizeof(reauth_cache_t));
4753     if (reauth_cache == NULL)
4754 	return SASL_NOMEM;
4755     memset(reauth_cache, 0, sizeof(reauth_cache_t));
4756     reauth_cache->i_am = CLIENT;
4757 
4758     /* mutex */
4759     reauth_cache->mutex = utils->mutex_alloc();
4760     if (!reauth_cache->mutex)
4761 	return SASL_FAIL;
4762 
4763     /* entries */
4764     reauth_cache->size = 10;
4765     reauth_cache->e = utils->malloc(reauth_cache->size *
4766 				    sizeof(reauth_entry_t));
4767     if (reauth_cache->e == NULL)
4768 	return SASL_NOMEM;
4769     memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t));
4770 
4771     ((digest_glob_context_t *) digestmd5_client_plugins[0].glob_context)->reauth = reauth_cache;
4772 
4773     *out_version = SASL_CLIENT_PLUG_VERSION;
4774     *pluglist = digestmd5_client_plugins;
4775     *plugcount = 1;
4776 
4777     return SASL_OK;
4778 }
4779