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