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