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