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