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>
91static int uef_init(const sasl_utils_t *utils);
92#endif /* _SUN_SDK_ && USE_UEF */
93
94#ifndef WIN32
95extern int strcasecmp(const char *s1, const char *s2);
96#endif /* end WIN32 */
97
98#ifdef macintosh
99#include <sasl_md5_plugin_decl.h>
100#endif
101
102/* external definitions */
103
104#ifndef _SUN_SDK_
105#ifdef sun
106/* gotta define gethostname ourselves on suns */
107extern 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_
123static 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
136typedef unsigned char HASH[HASHLEN + 1];
137#define HASHHEXLEN 32
138typedef unsigned char HASHHEX[HASHHEXLEN + 1];
139
140#define MAC_SIZE 10
141#define MAC_OFFS 2
142
143const char *SEALING_CLIENT_SERVER="Digest H(A1) to client-to-server sealing key magic constant";
144const char *SEALING_SERVER_CLIENT="Digest H(A1) to server-to-client sealing key magic constant";
145
146const char *SIGNING_CLIENT_SERVER="Digest session key to client-to-server signing key magic constant";
147const 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
155struct context;
156
157/* function definitions for cipher encode/decode */
158typedef int cipher_function_t(struct context *,
159			      const char *,
160			      unsigned,
161			      unsigned char[],
162			      char *,
163			      unsigned *);
164
165#ifdef _SUN_SDK_
166typedef int cipher_init_t(struct context *, char [16],
167                                            char [16]);
168#else
169typedef int cipher_init_t(struct context *, unsigned char [16],
170                                            unsigned char [16]);
171#endif /* _SUN_SDK_ */
172
173typedef void cipher_free_t(struct context *);
174
175enum Context_type { SERVER = 0, CLIENT = 1 };
176
177typedef struct cipher_context cipher_context_t;
178
179/* cached auth info used for fast reauth */
180typedef 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
201typedef 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 */
212typedef 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
271struct 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_
284static const unsigned char *COLON = (unsigned char *)":";
285#else
286static const unsigned char *COLON = ":";
287#endif /* _SUN_SDK_ */
288
289/* Hashes a string to produce an unsigned short */
290static 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
305static 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 */
328void
329DigestCalcResponse(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
390static 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 */
412void 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
442static 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
481static 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
533static 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
564static 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_
578static char *skip_token (char *s, int caseinsensitive  __attribute__((unused)))
579#else
580static 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 */
613static 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
659static 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
722struct 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
728typedef 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. */
732static 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
750static 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
790static 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
824static 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
871static 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
914static 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
951static 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
982static 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 */
993struct rc4_context_s {
994    unsigned char sbox[256];
995    int i, j;
996};
997
998typedef struct rc4_context_s rc4_context_t;
999
1000static 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
1027static 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
1061static 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
1095static 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
1107static 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
1142static 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
1163static 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
1189struct 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
1205DEFINE_STATIC_MUTEX(uef_init_mutex);
1206#define DES_CIPHER_INDEX	3
1207#define DES3_CIPHER_INDEX	4
1208
1209static int got_uef_slot = FALSE;
1210static sasl_ssf_t uef_max_ssf = 0;
1211static CK_SLOT_ID rc4_slot_id;
1212static CK_SLOT_ID des_slot_id;
1213static CK_SLOT_ID des3_slot_id;
1214
1215struct uef_context_s {
1216    CK_SESSION_HANDLE hSession;
1217    CK_OBJECT_HANDLE hKey;
1218};
1219
1220typedef 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
1231static 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
1251static 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
1384static 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
1391static 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
1398static 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
1405static void
1406free_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
1470static int
1471dec_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
1510static int
1511enc_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
1551static int
1552dec_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
1605static int
1606enc_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
1645struct 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
1660struct digest_cipher *available_ciphers1 = uef_ciphers;
1661#endif /* USE_UEF */
1662
1663static 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
1720static const unsigned short version = 1;
1721
1722/* len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum */
1723
1724static int
1725digestmd5_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
1811static int
1812digestmd5_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
1986static 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
2002static int
2003digestmd5_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
2080static int
2081create_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
2124static int
2125check_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
2166static int
2167digestmd5_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
2247static 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
2263static void
2264digestmd5_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
2296static void
2297clear_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
2314static void
2315digestmd5_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
2333typedef 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
2341static void
2342DigestCalcHA1FromSecret(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
2372static 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
2445static int
2446get_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 */
2483static 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
2531static 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
2553static int
2554digestmd5_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
2836static int
2837digestmd5_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
3583static int
3584digestmd5_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
3670static void
3671digestmd5_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
3680static 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
3708int 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
3788typedef 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 */
3803static void
3804DigestCalcHA1(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
3847static 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
3942static int
3943make_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
4209static 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
4605static 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
4764static int
4765digestmd5_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
4786static int
4787digestmd5_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
4866static int
4867digestmd5_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
4937static int
4938digestmd5_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
5045static int
5046digestmd5_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
5148static void
5149digestmd5_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
5164static 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
5191int 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 */
5255static int
5256getSlotID(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
5336static int
5337uef_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