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