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