1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate * CDDL HEADER START
3*0Sstevel@tonic-gate *
4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
7*0Sstevel@tonic-gate * with the License.
8*0Sstevel@tonic-gate *
9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate * and limitations under the License.
13*0Sstevel@tonic-gate *
14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate *
20*0Sstevel@tonic-gate * CDDL HEADER END
21*0Sstevel@tonic-gate */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24*0Sstevel@tonic-gate * Use is subject to license terms.
25*0Sstevel@tonic-gate */
26*0Sstevel@tonic-gate
27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
28*0Sstevel@tonic-gate
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate * Copyright (c) 1998-1999 Innosoft International, Inc. All Rights Reserved.
31*0Sstevel@tonic-gate *
32*0Sstevel@tonic-gate * Copyright (c) 1996-1997 Critical Angle Inc. All Rights Reserved.
33*0Sstevel@tonic-gate */
34*0Sstevel@tonic-gate
35*0Sstevel@tonic-gate #include <stdio.h>
36*0Sstevel@tonic-gate #include <string.h>
37*0Sstevel@tonic-gate #include <stdlib.h>
38*0Sstevel@tonic-gate #include <unistd.h>
39*0Sstevel@tonic-gate #include <fcntl.h>
40*0Sstevel@tonic-gate #include <ctype.h>
41*0Sstevel@tonic-gate #include <md5.h>
42*0Sstevel@tonic-gate #include <sys/time.h>
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gate #include "lber.h"
45*0Sstevel@tonic-gate #include "ldap.h"
46*0Sstevel@tonic-gate #include "ldap-int.h"
47*0Sstevel@tonic-gate
48*0Sstevel@tonic-gate /*
49*0Sstevel@tonic-gate * DIGEST-MD5 SASL Mechanism
50*0Sstevel@tonic-gate */
51*0Sstevel@tonic-gate
52*0Sstevel@tonic-gate /* use this instead of "const unsigned char" to eliminate compiler warnings */
53*0Sstevel@tonic-gate typedef /* const */ unsigned char CONST_UCHAR;
54*0Sstevel@tonic-gate
55*0Sstevel@tonic-gate /* size of a digest result */
56*0Sstevel@tonic-gate #define DIGEST_SIZE 16
57*0Sstevel@tonic-gate
58*0Sstevel@tonic-gate /* size of a digest hex string */
59*0Sstevel@tonic-gate #define DIGEST_HEX_SIZE (DIGEST_SIZE * 2 + 1)
60*0Sstevel@tonic-gate
61*0Sstevel@tonic-gate /*
62*0Sstevel@tonic-gate * extra bytes which a client response needs in addition to size of
63*0Sstevel@tonic-gate * server challenge */
64*0Sstevel@tonic-gate #define DIGEST_CLIENT_EXTRA (DIGEST_HEX_SIZE + 128)
65*0Sstevel@tonic-gate
66*0Sstevel@tonic-gate /* erase a digest_attrs_t structure */
67*0Sstevel@tonic-gate #define digest_clear(attrs) memset((attrs), 0, sizeof (digest_attrs_t))
68*0Sstevel@tonic-gate
69*0Sstevel@tonic-gate /*
70*0Sstevel@tonic-gate * broken-out digest attributes (with quotes removed)
71*0Sstevel@tonic-gate * probably not NUL terminated.
72*0Sstevel@tonic-gate */
73*0Sstevel@tonic-gate typedef struct {
74*0Sstevel@tonic-gate const char *realm, *nonce, *cnonce, *qop, *user, *resp, *dom;
75*0Sstevel@tonic-gate const char *max, *stale, *ncount, *uri, *charset;
76*0Sstevel@tonic-gate int rlen, nlen, clen, qlen, ulen, resplen, dlen;
77*0Sstevel@tonic-gate int mlen, slen, nclen, urilen, charsetlen;
78*0Sstevel@tonic-gate char ncbuf[9];
79*0Sstevel@tonic-gate } digest_attrs_t;
80*0Sstevel@tonic-gate
81*0Sstevel@tonic-gate static const char hextab[] = "0123456789abcdef";
82*0Sstevel@tonic-gate static CONST_UCHAR colon[] = ":";
83*0Sstevel@tonic-gate
84*0Sstevel@tonic-gate /*
85*0Sstevel@tonic-gate * Make a nonce (NUL terminated)
86*0Sstevel@tonic-gate * buf -- buffer for result
87*0Sstevel@tonic-gate * maxlen -- max length of result
88*0Sstevel@tonic-gate * returns final length or -1 on error
89*0Sstevel@tonic-gate */
90*0Sstevel@tonic-gate static int
digest_nonce(char * buf,int maxlen)91*0Sstevel@tonic-gate digest_nonce(char *buf, int maxlen)
92*0Sstevel@tonic-gate {
93*0Sstevel@tonic-gate /*
94*0Sstevel@tonic-gate * it shouldn't matter too much if two threads step on this counter
95*0Sstevel@tonic-gate * at the same time, but mutexing it wouldn't hurt
96*0Sstevel@tonic-gate */
97*0Sstevel@tonic-gate static int counter;
98*0Sstevel@tonic-gate char *dst;
99*0Sstevel@tonic-gate int len;
100*0Sstevel@tonic-gate struct chal_info {
101*0Sstevel@tonic-gate time_t mytime;
102*0Sstevel@tonic-gate unsigned char digest[16];
103*0Sstevel@tonic-gate } cinfo;
104*0Sstevel@tonic-gate MD5_CTX ctx;
105*0Sstevel@tonic-gate long r;
106*0Sstevel@tonic-gate static int set_rand = 0;
107*0Sstevel@tonic-gate unsigned char *p;
108*0Sstevel@tonic-gate int j;
109*0Sstevel@tonic-gate int fd;
110*0Sstevel@tonic-gate int got_random;
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gate /* initialize challenge */
113*0Sstevel@tonic-gate if (maxlen < 2 * sizeof (cinfo))
114*0Sstevel@tonic-gate return (-1);
115*0Sstevel@tonic-gate dst = buf;
116*0Sstevel@tonic-gate
117*0Sstevel@tonic-gate /* get a timestamp */
118*0Sstevel@tonic-gate time(&cinfo.mytime);
119*0Sstevel@tonic-gate
120*0Sstevel@tonic-gate /* get some randomness */
121*0Sstevel@tonic-gate
122*0Sstevel@tonic-gate got_random = 0;
123*0Sstevel@tonic-gate fd = open("/dev/urandom", O_RDONLY);
124*0Sstevel@tonic-gate if (fd != -1) {
125*0Sstevel@tonic-gate got_random =
126*0Sstevel@tonic-gate (read(fd, &r, sizeof (r)) == sizeof (r));
127*0Sstevel@tonic-gate close(fd);
128*0Sstevel@tonic-gate }
129*0Sstevel@tonic-gate
130*0Sstevel@tonic-gate if (!got_random) {
131*0Sstevel@tonic-gate if (set_rand == 0) {
132*0Sstevel@tonic-gate struct timeval tv;
133*0Sstevel@tonic-gate
134*0Sstevel@tonic-gate r = cinfo.mytime - (getpid() *65536) + (random() & 0xffff);
135*0Sstevel@tonic-gate
136*0Sstevel@tonic-gate gettimeofday(&tv, NULL);
137*0Sstevel@tonic-gate r ^= tv.tv_usec;
138*0Sstevel@tonic-gate r ^= gethostid();
139*0Sstevel@tonic-gate
140*0Sstevel@tonic-gate srandom(r);
141*0Sstevel@tonic-gate set_rand = 1;
142*0Sstevel@tonic-gate }
143*0Sstevel@tonic-gate
144*0Sstevel@tonic-gate r = random();
145*0Sstevel@tonic-gate }
146*0Sstevel@tonic-gate
147*0Sstevel@tonic-gate MD5Init(&ctx);
148*0Sstevel@tonic-gate MD5Update(&ctx, (unsigned char *) &r, sizeof (r));
149*0Sstevel@tonic-gate MD5Update(&ctx, (unsigned char *) &counter, sizeof (counter));
150*0Sstevel@tonic-gate ++counter;
151*0Sstevel@tonic-gate MD5Final(cinfo.digest, &ctx);
152*0Sstevel@tonic-gate
153*0Sstevel@tonic-gate /* compute hex for result */
154*0Sstevel@tonic-gate for (j = 0, p = (unsigned char *)&cinfo; j < sizeof (cinfo); ++j) {
155*0Sstevel@tonic-gate dst[j * 2] = hextab[p[j] >> 4];
156*0Sstevel@tonic-gate dst[j * 2 + 1] = hextab[p[j] & 0xf];
157*0Sstevel@tonic-gate }
158*0Sstevel@tonic-gate
159*0Sstevel@tonic-gate /* take the entire time_t, plus at least 6 bytes of MD5 output */
160*0Sstevel@tonic-gate len = ((sizeof (time_t) + 6) * 2);
161*0Sstevel@tonic-gate dst += len;
162*0Sstevel@tonic-gate maxlen -= len;
163*0Sstevel@tonic-gate
164*0Sstevel@tonic-gate *dst = '\0';
165*0Sstevel@tonic-gate
166*0Sstevel@tonic-gate return (dst - buf);
167*0Sstevel@tonic-gate }
168*0Sstevel@tonic-gate
169*0Sstevel@tonic-gate /*
170*0Sstevel@tonic-gate * if the string is entirely in the 8859-1 subset of UTF-8, then translate
171*0Sstevel@tonic-gate * to 8859-1 prior to MD5
172*0Sstevel@tonic-gate */
173*0Sstevel@tonic-gate static void
MD5_UTF8_8859_1(MD5_CTX * ctx,CONST_UCHAR * base,int len)174*0Sstevel@tonic-gate MD5_UTF8_8859_1(MD5_CTX *ctx, CONST_UCHAR *base, int len)
175*0Sstevel@tonic-gate {
176*0Sstevel@tonic-gate CONST_UCHAR *scan, *end;
177*0Sstevel@tonic-gate unsigned char cbuf;
178*0Sstevel@tonic-gate
179*0Sstevel@tonic-gate end = base + len;
180*0Sstevel@tonic-gate for (scan = base; scan < end; ++scan) {
181*0Sstevel@tonic-gate if (*scan > 0xC3) break; /* abort if outside 8859-1 */
182*0Sstevel@tonic-gate if (*scan >= 0xC0 && *scan <= 0xC3) {
183*0Sstevel@tonic-gate if (++scan == end || *scan < 0x80 || *scan > 0xBF) break;
184*0Sstevel@tonic-gate }
185*0Sstevel@tonic-gate }
186*0Sstevel@tonic-gate /* if we found a character outside 8859-1, don't alter string */
187*0Sstevel@tonic-gate if (scan < end) {
188*0Sstevel@tonic-gate MD5Update(ctx, base, len);
189*0Sstevel@tonic-gate return;
190*0Sstevel@tonic-gate }
191*0Sstevel@tonic-gate
192*0Sstevel@tonic-gate /* convert to 8859-1 prior to applying hash */
193*0Sstevel@tonic-gate do {
194*0Sstevel@tonic-gate for (scan = base; scan < end && *scan < 0xC0; ++scan)
195*0Sstevel@tonic-gate ;
196*0Sstevel@tonic-gate if (scan != base) MD5Update(ctx, base, scan - base);
197*0Sstevel@tonic-gate if (scan + 1 >= end) break;
198*0Sstevel@tonic-gate cbuf = ((scan[0] & 0x3) << 6) | (scan[1] & 0x3f);
199*0Sstevel@tonic-gate MD5Update(ctx, &cbuf, 1);
200*0Sstevel@tonic-gate base = scan + 2;
201*0Sstevel@tonic-gate } while (base < end);
202*0Sstevel@tonic-gate }
203*0Sstevel@tonic-gate
204*0Sstevel@tonic-gate /*
205*0Sstevel@tonic-gate * Compute MD5( "<user>:<realm>:<pass>" )
206*0Sstevel@tonic-gate * if use8859_1 is non-zero, then user/realm is 8859-1 charset
207*0Sstevel@tonic-gate * if supplied lengths are 0, strlen() is used
208*0Sstevel@tonic-gate * places result in hash_pass (of size DIGEST_SIZE) and returns it.
209*0Sstevel@tonic-gate */
210*0Sstevel@tonic-gate static unsigned char *
digest_hash_pass(const char * user,int ulen,const char * realm,int rlen,const char * pass,int passlen,int use8859_1,unsigned char * hash_pass)211*0Sstevel@tonic-gate digest_hash_pass(const char *user, int ulen, const char *realm, int rlen,
212*0Sstevel@tonic-gate const char *pass, int passlen, int use8859_1,
213*0Sstevel@tonic-gate unsigned char *hash_pass)
214*0Sstevel@tonic-gate {
215*0Sstevel@tonic-gate MD5_CTX ctx;
216*0Sstevel@tonic-gate
217*0Sstevel@tonic-gate MD5Init(&ctx);
218*0Sstevel@tonic-gate if (ulen == 0) ulen = strlen(user);
219*0Sstevel@tonic-gate if (use8859_1) {
220*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) user, ulen);
221*0Sstevel@tonic-gate } else {
222*0Sstevel@tonic-gate MD5_UTF8_8859_1(&ctx, (CONST_UCHAR *) user, ulen);
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
225*0Sstevel@tonic-gate if (rlen == 0) rlen = strlen(realm);
226*0Sstevel@tonic-gate if (use8859_1) {
227*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) realm, rlen);
228*0Sstevel@tonic-gate } else {
229*0Sstevel@tonic-gate MD5_UTF8_8859_1(&ctx, (CONST_UCHAR *) realm, rlen);
230*0Sstevel@tonic-gate }
231*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
232*0Sstevel@tonic-gate if (passlen == 0) passlen = strlen(pass);
233*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) pass, passlen);
234*0Sstevel@tonic-gate MD5Final(hash_pass, &ctx);
235*0Sstevel@tonic-gate
236*0Sstevel@tonic-gate return (hash_pass);
237*0Sstevel@tonic-gate }
238*0Sstevel@tonic-gate
239*0Sstevel@tonic-gate /*
240*0Sstevel@tonic-gate * Compute MD5("<hash_pass>:<nonce>:<cnonce>")
241*0Sstevel@tonic-gate * places result in hash_a1 and returns hash_a1
242*0Sstevel@tonic-gate * note that hash_pass and hash_a1 may be the same
243*0Sstevel@tonic-gate */
244*0Sstevel@tonic-gate static unsigned char *
digest_hash_a1(const digest_attrs_t * attr,CONST_UCHAR * hash_pass,unsigned char * hash_a1)245*0Sstevel@tonic-gate digest_hash_a1(const digest_attrs_t *attr, CONST_UCHAR *hash_pass,
246*0Sstevel@tonic-gate unsigned char *hash_a1)
247*0Sstevel@tonic-gate {
248*0Sstevel@tonic-gate MD5_CTX ctx;
249*0Sstevel@tonic-gate
250*0Sstevel@tonic-gate MD5Init(&ctx);
251*0Sstevel@tonic-gate MD5Update(&ctx, hash_pass, DIGEST_SIZE);
252*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
253*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->nonce, attr->nlen);
254*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
255*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->cnonce, attr->clen);
256*0Sstevel@tonic-gate MD5Final(hash_a1, &ctx);
257*0Sstevel@tonic-gate
258*0Sstevel@tonic-gate return (hash_a1);
259*0Sstevel@tonic-gate }
260*0Sstevel@tonic-gate
261*0Sstevel@tonic-gate /*
262*0Sstevel@tonic-gate * calculate hash response for digest auth.
263*0Sstevel@tonic-gate * outresp must be buffer of at least DIGEST_HEX_SIZE
264*0Sstevel@tonic-gate * outresp and hex_int may be the same
265*0Sstevel@tonic-gate * method may be NULL if mlen is 0
266*0Sstevel@tonic-gate */
267*0Sstevel@tonic-gate static void
digest_calc_resp(const digest_attrs_t * attr,CONST_UCHAR * hash_a1,const char * method,int mlen,CONST_UCHAR * hex_int,char * outresp)268*0Sstevel@tonic-gate digest_calc_resp(const digest_attrs_t *attr,
269*0Sstevel@tonic-gate CONST_UCHAR *hash_a1, const char *method, int mlen,
270*0Sstevel@tonic-gate CONST_UCHAR *hex_int, char *outresp)
271*0Sstevel@tonic-gate {
272*0Sstevel@tonic-gate static CONST_UCHAR defncount[] = ":00000001:";
273*0Sstevel@tonic-gate static CONST_UCHAR empty_hex_int[] =
274*0Sstevel@tonic-gate "00000000000000000000000000000000";
275*0Sstevel@tonic-gate MD5_CTX ctx;
276*0Sstevel@tonic-gate unsigned char resp[DIGEST_SIZE];
277*0Sstevel@tonic-gate unsigned char *hex_a1 = (unsigned char *) outresp;
278*0Sstevel@tonic-gate unsigned char *hex_a2 = (unsigned char *) outresp;
279*0Sstevel@tonic-gate unsigned j;
280*0Sstevel@tonic-gate
281*0Sstevel@tonic-gate /* compute hash of A2 and put in resp */
282*0Sstevel@tonic-gate MD5Init(&ctx);
283*0Sstevel@tonic-gate if (mlen == 0 && method != NULL) mlen = strlen(method);
284*0Sstevel@tonic-gate if (mlen) MD5Update(&ctx, (CONST_UCHAR *) method, mlen);
285*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
286*0Sstevel@tonic-gate if (attr->urilen != 0) {
287*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->uri, attr->urilen);
288*0Sstevel@tonic-gate }
289*0Sstevel@tonic-gate if (attr->qlen != 4 || strncasecmp(attr->qop, "auth", 4) != 0) {
290*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
291*0Sstevel@tonic-gate if (hex_int == NULL) hex_int = empty_hex_int;
292*0Sstevel@tonic-gate MD5Update(&ctx, hex_int, DIGEST_SIZE * 2);
293*0Sstevel@tonic-gate }
294*0Sstevel@tonic-gate MD5Final(resp, &ctx);
295*0Sstevel@tonic-gate
296*0Sstevel@tonic-gate /* compute hex_a1 from hash_a1 */
297*0Sstevel@tonic-gate for (j = 0; j < DIGEST_SIZE; ++j) {
298*0Sstevel@tonic-gate hex_a1[j * 2] = hextab[hash_a1[j] >> 4];
299*0Sstevel@tonic-gate hex_a1[j * 2 + 1] = hextab[hash_a1[j] & 0xf];
300*0Sstevel@tonic-gate }
301*0Sstevel@tonic-gate
302*0Sstevel@tonic-gate /* compute response */
303*0Sstevel@tonic-gate MD5Init(&ctx);
304*0Sstevel@tonic-gate MD5Update(&ctx, hex_a1, DIGEST_SIZE * 2);
305*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
306*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->nonce, attr->nlen);
307*0Sstevel@tonic-gate if (attr->ncount != NULL) {
308*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
309*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->ncount, attr->nclen);
310*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
311*0Sstevel@tonic-gate } else {
312*0Sstevel@tonic-gate MD5Update(&ctx, defncount, sizeof (defncount) - 1);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->cnonce, attr->clen);
315*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
316*0Sstevel@tonic-gate MD5Update(&ctx, (CONST_UCHAR *) attr->qop, attr->qlen);
317*0Sstevel@tonic-gate MD5Update(&ctx, colon, 1);
318*0Sstevel@tonic-gate
319*0Sstevel@tonic-gate /* compute hex_a2 from hash_a2 */
320*0Sstevel@tonic-gate for (j = 0; j < DIGEST_SIZE; ++j) {
321*0Sstevel@tonic-gate hex_a2[j * 2] = hextab[resp[j] >> 4];
322*0Sstevel@tonic-gate hex_a2[j * 2 + 1] = hextab[resp[j] & 0xf];
323*0Sstevel@tonic-gate }
324*0Sstevel@tonic-gate MD5Update(&ctx, hex_a2, DIGEST_SIZE * 2);
325*0Sstevel@tonic-gate MD5Final(resp, &ctx);
326*0Sstevel@tonic-gate
327*0Sstevel@tonic-gate /* generate hex output */
328*0Sstevel@tonic-gate for (j = 0; j < DIGEST_SIZE; ++j) {
329*0Sstevel@tonic-gate outresp[j * 2] = hextab[resp[j] >> 4];
330*0Sstevel@tonic-gate outresp[j * 2 + 1] = hextab[resp[j] & 0xf];
331*0Sstevel@tonic-gate }
332*0Sstevel@tonic-gate outresp[DIGEST_SIZE * 2] = '\0';
333*0Sstevel@tonic-gate memset(resp, 0, sizeof (resp));
334*0Sstevel@tonic-gate }
335*0Sstevel@tonic-gate
336*0Sstevel@tonic-gate /*
337*0Sstevel@tonic-gate * generate the client response from attributes
338*0Sstevel@tonic-gate * either one of hash_pass and hash_a1 may be NULL
339*0Sstevel@tonic-gate * hash_a1 is used on re-authentication and takes precedence over hash_pass
340*0Sstevel@tonic-gate */
341*0Sstevel@tonic-gate static int
digest_client_resp(const char * method,int mlen,CONST_UCHAR * hash_pass,CONST_UCHAR * hash_a1,digest_attrs_t * attr,char * outbuf,int maxout,int * plen)342*0Sstevel@tonic-gate digest_client_resp(const char *method, int mlen,
343*0Sstevel@tonic-gate CONST_UCHAR *hash_pass, CONST_UCHAR *hash_a1,
344*0Sstevel@tonic-gate digest_attrs_t *attr, /* in/out attributes */
345*0Sstevel@tonic-gate char *outbuf, int maxout, int *plen)
346*0Sstevel@tonic-gate {
347*0Sstevel@tonic-gate #define prefixsize (sizeof (prefix) - 4 * 4 - 1)
348*0Sstevel@tonic-gate #define suffixsize (sizeof (rstr) + sizeof (qstr) - 1 + DIGEST_SIZE * 2)
349*0Sstevel@tonic-gate static const char prefix[] =
350*0Sstevel@tonic-gate "username=\"%.*s\",realm=\"%.*s\",nonce=\"%.*s\",nc=%.*s,cnonce=\"";
351*0Sstevel@tonic-gate static const char rstr[] = "\",response=";
352*0Sstevel@tonic-gate static const char qstr[] = ",qop=auth";
353*0Sstevel@tonic-gate static const char chstr[] = "charset=";
354*0Sstevel@tonic-gate char *scan;
355*0Sstevel@tonic-gate int len;
356*0Sstevel@tonic-gate char hexbuf[DIGEST_HEX_SIZE];
357*0Sstevel@tonic-gate unsigned char hashbuf[DIGEST_SIZE];
358*0Sstevel@tonic-gate
359*0Sstevel@tonic-gate /* make sure we have mandatory attributes */
360*0Sstevel@tonic-gate if (attr->nonce == NULL || attr->nlen == 0 ||
361*0Sstevel@tonic-gate attr->realm == NULL || attr->rlen == 0 ||
362*0Sstevel@tonic-gate attr->qop == NULL || attr->qlen == 0 ||
363*0Sstevel@tonic-gate (attr->nclen != 0 && attr->nclen != 8)) {
364*0Sstevel@tonic-gate return (-5);
365*0Sstevel@tonic-gate }
366*0Sstevel@tonic-gate if (mlen != 0 && method == NULL)
367*0Sstevel@tonic-gate return (-7);
368*0Sstevel@tonic-gate
369*0Sstevel@tonic-gate /* initialize ncount */
370*0Sstevel@tonic-gate if (attr->ncount == NULL) {
371*0Sstevel@tonic-gate strcpy(attr->ncbuf, "00000001");
372*0Sstevel@tonic-gate attr->ncount = attr->ncbuf;
373*0Sstevel@tonic-gate attr->nclen = 8;
374*0Sstevel@tonic-gate } else if (attr->ncount == attr->ncbuf) {
375*0Sstevel@tonic-gate /* increment ncount */
376*0Sstevel@tonic-gate scan = attr->ncbuf + 7;
377*0Sstevel@tonic-gate while (scan >= attr->ncbuf) {
378*0Sstevel@tonic-gate if (*scan == '9') {
379*0Sstevel@tonic-gate *scan = 'a';
380*0Sstevel@tonic-gate break;
381*0Sstevel@tonic-gate } else if (*scan != 'f') {
382*0Sstevel@tonic-gate ++*scan;
383*0Sstevel@tonic-gate break;
384*0Sstevel@tonic-gate }
385*0Sstevel@tonic-gate *scan = '0';
386*0Sstevel@tonic-gate --scan;
387*0Sstevel@tonic-gate }
388*0Sstevel@tonic-gate }
389*0Sstevel@tonic-gate
390*0Sstevel@tonic-gate /* sanity check length */
391*0Sstevel@tonic-gate len = prefixsize + attr->ulen + attr->rlen + attr->nlen + attr->nclen;
392*0Sstevel@tonic-gate if (attr->charsetlen > 0) {
393*0Sstevel@tonic-gate /* includes 1 for a comma */
394*0Sstevel@tonic-gate len += sizeof (chstr) + attr->charsetlen;
395*0Sstevel@tonic-gate }
396*0Sstevel@tonic-gate if (len + suffixsize >= maxout)
397*0Sstevel@tonic-gate return (-3);
398*0Sstevel@tonic-gate
399*0Sstevel@tonic-gate scan = outbuf;
400*0Sstevel@tonic-gate
401*0Sstevel@tonic-gate /* charset */
402*0Sstevel@tonic-gate if (attr->charsetlen > 0 && attr->charset != NULL) {
403*0Sstevel@tonic-gate memcpy(scan, chstr, sizeof (chstr) - 1);
404*0Sstevel@tonic-gate scan += sizeof (chstr) - 1;
405*0Sstevel@tonic-gate memcpy(scan, attr->charset, attr->charsetlen);
406*0Sstevel@tonic-gate scan += attr->charsetlen;
407*0Sstevel@tonic-gate *scan++ = ',';
408*0Sstevel@tonic-gate }
409*0Sstevel@tonic-gate
410*0Sstevel@tonic-gate /* generate string up to the client nonce */
411*0Sstevel@tonic-gate sprintf(scan, prefix, attr->ulen, attr->user,
412*0Sstevel@tonic-gate attr->rlen, attr->realm, attr->nlen, attr->nonce,
413*0Sstevel@tonic-gate attr->nclen, attr->ncount);
414*0Sstevel@tonic-gate scan = outbuf + len;
415*0Sstevel@tonic-gate
416*0Sstevel@tonic-gate /* generate client nonce */
417*0Sstevel@tonic-gate len = digest_nonce(scan, maxout - (scan - outbuf));
418*0Sstevel@tonic-gate if (len < 0)
419*0Sstevel@tonic-gate return (len);
420*0Sstevel@tonic-gate attr->cnonce = scan;
421*0Sstevel@tonic-gate attr->clen = len;
422*0Sstevel@tonic-gate scan += len;
423*0Sstevel@tonic-gate if (scan - outbuf + suffixsize > maxout)
424*0Sstevel@tonic-gate return (-3);
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gate /* compute response */
427*0Sstevel@tonic-gate if (hash_a1 == NULL) {
428*0Sstevel@tonic-gate if (hash_pass == NULL)
429*0Sstevel@tonic-gate return (-7);
430*0Sstevel@tonic-gate hash_a1 = digest_hash_a1(attr, hash_pass, hashbuf);
431*0Sstevel@tonic-gate }
432*0Sstevel@tonic-gate digest_calc_resp(attr, hash_a1, method, mlen, NULL, hexbuf);
433*0Sstevel@tonic-gate
434*0Sstevel@tonic-gate /* finish it */
435*0Sstevel@tonic-gate memcpy(scan, rstr, sizeof (rstr) - 1);
436*0Sstevel@tonic-gate scan += sizeof (rstr) - 1;
437*0Sstevel@tonic-gate memcpy(scan, hexbuf, DIGEST_SIZE * 2);
438*0Sstevel@tonic-gate attr->resp = scan;
439*0Sstevel@tonic-gate attr->resplen = DIGEST_SIZE * 2;
440*0Sstevel@tonic-gate scan += DIGEST_SIZE * 2;
441*0Sstevel@tonic-gate memcpy(scan, qstr, sizeof (qstr));
442*0Sstevel@tonic-gate
443*0Sstevel@tonic-gate /* set final length */
444*0Sstevel@tonic-gate if (plen != NULL) *plen = scan - outbuf + sizeof (qstr) - 1;
445*0Sstevel@tonic-gate
446*0Sstevel@tonic-gate return (0);
447*0Sstevel@tonic-gate }
448*0Sstevel@tonic-gate
449*0Sstevel@tonic-gate #define lstreqcase(conststr, val, len) ((len) == sizeof (conststr) - 1 && \
450*0Sstevel@tonic-gate strncasecmp((conststr), (val), sizeof (conststr) - 1) == 0)
451*0Sstevel@tonic-gate
452*0Sstevel@tonic-gate /* parse a digest auth string */
453*0Sstevel@tonic-gate static int
digest_parse(const char * str,int len,digest_attrs_t * attr_out)454*0Sstevel@tonic-gate digest_parse(const char *str, int len, digest_attrs_t *attr_out)
455*0Sstevel@tonic-gate {
456*0Sstevel@tonic-gate static const char rstr[] = "realm";
457*0Sstevel@tonic-gate static const char nstr[] = "nonce";
458*0Sstevel@tonic-gate static const char cstr[] = "cnonce";
459*0Sstevel@tonic-gate static const char qstr[] = "qop";
460*0Sstevel@tonic-gate static const char ustr[] = "username";
461*0Sstevel@tonic-gate static const char respstr[] = "response";
462*0Sstevel@tonic-gate static const char dstr[] = "domain";
463*0Sstevel@tonic-gate static const char mstr[] = "maxbuf";
464*0Sstevel@tonic-gate static const char sstr[] = "stale";
465*0Sstevel@tonic-gate static const char ncstr[] = "nc";
466*0Sstevel@tonic-gate static const char uristr[] = "digest-uri";
467*0Sstevel@tonic-gate static const char charsetstr[] = "charset";
468*0Sstevel@tonic-gate const char *scan, *attr, *val, *end;
469*0Sstevel@tonic-gate int alen, vlen;
470*0Sstevel@tonic-gate
471*0Sstevel@tonic-gate if (len == 0) len = strlen(str);
472*0Sstevel@tonic-gate scan = str;
473*0Sstevel@tonic-gate end = str + len;
474*0Sstevel@tonic-gate for (;;) {
475*0Sstevel@tonic-gate /* skip over commas */
476*0Sstevel@tonic-gate while (scan < end && (*scan == ',' || isspace(*scan))) ++scan;
477*0Sstevel@tonic-gate /* parse attribute */
478*0Sstevel@tonic-gate attr = scan;
479*0Sstevel@tonic-gate while (scan < end && *scan != '=') ++scan;
480*0Sstevel@tonic-gate alen = scan - attr;
481*0Sstevel@tonic-gate if (!alen || scan == end || scan + 1 == end) {
482*0Sstevel@tonic-gate return (-5);
483*0Sstevel@tonic-gate }
484*0Sstevel@tonic-gate
485*0Sstevel@tonic-gate /* parse value */
486*0Sstevel@tonic-gate if (scan[1] == '"') {
487*0Sstevel@tonic-gate scan += 2;
488*0Sstevel@tonic-gate val = scan;
489*0Sstevel@tonic-gate while (scan < end && *scan != '"') {
490*0Sstevel@tonic-gate /* skip over "\" quoting, but don't remove it */
491*0Sstevel@tonic-gate if (*scan == '\\') {
492*0Sstevel@tonic-gate if (scan + 1 == end)
493*0Sstevel@tonic-gate return (-5);
494*0Sstevel@tonic-gate scan += 2;
495*0Sstevel@tonic-gate } else {
496*0Sstevel@tonic-gate ++scan;
497*0Sstevel@tonic-gate }
498*0Sstevel@tonic-gate }
499*0Sstevel@tonic-gate vlen = scan - val;
500*0Sstevel@tonic-gate if (*scan != '"')
501*0Sstevel@tonic-gate return (-5);
502*0Sstevel@tonic-gate ++scan;
503*0Sstevel@tonic-gate } else {
504*0Sstevel@tonic-gate ++scan;
505*0Sstevel@tonic-gate val = scan;
506*0Sstevel@tonic-gate while (scan < end && *scan != ',') ++scan;
507*0Sstevel@tonic-gate vlen = scan - val;
508*0Sstevel@tonic-gate }
509*0Sstevel@tonic-gate if (!vlen)
510*0Sstevel@tonic-gate return (-5);
511*0Sstevel@tonic-gate
512*0Sstevel@tonic-gate /* lookup the attribute */
513*0Sstevel@tonic-gate switch (*attr) {
514*0Sstevel@tonic-gate case 'c':
515*0Sstevel@tonic-gate case 'C':
516*0Sstevel@tonic-gate if (lstreqcase(cstr, attr, alen)) {
517*0Sstevel@tonic-gate attr_out->cnonce = val;
518*0Sstevel@tonic-gate attr_out->clen = vlen;
519*0Sstevel@tonic-gate }
520*0Sstevel@tonic-gate if (lstreqcase(charsetstr, attr, alen)) {
521*0Sstevel@tonic-gate attr_out->charset = val;
522*0Sstevel@tonic-gate attr_out->charsetlen = vlen;
523*0Sstevel@tonic-gate }
524*0Sstevel@tonic-gate break;
525*0Sstevel@tonic-gate case 'd':
526*0Sstevel@tonic-gate case 'D':
527*0Sstevel@tonic-gate if (lstreqcase(dstr, attr, alen)) {
528*0Sstevel@tonic-gate attr_out->dom = val;
529*0Sstevel@tonic-gate attr_out->dlen = vlen;
530*0Sstevel@tonic-gate }
531*0Sstevel@tonic-gate if (lstreqcase(uristr, attr, alen)) {
532*0Sstevel@tonic-gate attr_out->uri = val;
533*0Sstevel@tonic-gate attr_out->urilen = vlen;
534*0Sstevel@tonic-gate }
535*0Sstevel@tonic-gate break;
536*0Sstevel@tonic-gate case 'm':
537*0Sstevel@tonic-gate case 'M':
538*0Sstevel@tonic-gate if (lstreqcase(mstr, attr, alen)) {
539*0Sstevel@tonic-gate attr_out->max = val;
540*0Sstevel@tonic-gate attr_out->mlen = vlen;
541*0Sstevel@tonic-gate }
542*0Sstevel@tonic-gate break;
543*0Sstevel@tonic-gate case 'n':
544*0Sstevel@tonic-gate case 'N':
545*0Sstevel@tonic-gate if (lstreqcase(nstr, attr, alen)) {
546*0Sstevel@tonic-gate attr_out->nonce = val;
547*0Sstevel@tonic-gate attr_out->nlen = vlen;
548*0Sstevel@tonic-gate }
549*0Sstevel@tonic-gate if (lstreqcase(ncstr, attr, alen)) {
550*0Sstevel@tonic-gate attr_out->ncount = val;
551*0Sstevel@tonic-gate attr_out->nclen = vlen;
552*0Sstevel@tonic-gate }
553*0Sstevel@tonic-gate break;
554*0Sstevel@tonic-gate case 'q':
555*0Sstevel@tonic-gate case 'Q':
556*0Sstevel@tonic-gate if (lstreqcase(qstr, attr, alen)) {
557*0Sstevel@tonic-gate attr_out->qop = val;
558*0Sstevel@tonic-gate attr_out->qlen = vlen;
559*0Sstevel@tonic-gate }
560*0Sstevel@tonic-gate break;
561*0Sstevel@tonic-gate case 'r':
562*0Sstevel@tonic-gate case 'R':
563*0Sstevel@tonic-gate if (lstreqcase(rstr, attr, alen)) {
564*0Sstevel@tonic-gate attr_out->realm = val;
565*0Sstevel@tonic-gate attr_out->rlen = vlen;
566*0Sstevel@tonic-gate }
567*0Sstevel@tonic-gate if (lstreqcase(respstr, attr, alen)) {
568*0Sstevel@tonic-gate attr_out->resp = val;
569*0Sstevel@tonic-gate attr_out->resplen = vlen;
570*0Sstevel@tonic-gate }
571*0Sstevel@tonic-gate break;
572*0Sstevel@tonic-gate case 's':
573*0Sstevel@tonic-gate case 'S':
574*0Sstevel@tonic-gate if (lstreqcase(sstr, attr, alen)) {
575*0Sstevel@tonic-gate attr_out->stale = val;
576*0Sstevel@tonic-gate attr_out->slen = vlen;
577*0Sstevel@tonic-gate }
578*0Sstevel@tonic-gate break;
579*0Sstevel@tonic-gate case 'u':
580*0Sstevel@tonic-gate case 'U':
581*0Sstevel@tonic-gate if (lstreqcase(ustr, attr, alen)) {
582*0Sstevel@tonic-gate attr_out->user = val;
583*0Sstevel@tonic-gate attr_out->ulen = vlen;
584*0Sstevel@tonic-gate }
585*0Sstevel@tonic-gate break;
586*0Sstevel@tonic-gate }
587*0Sstevel@tonic-gate
588*0Sstevel@tonic-gate /* we should be at the end of the string or a comma */
589*0Sstevel@tonic-gate if (scan == end) break;
590*0Sstevel@tonic-gate if (*scan != ',')
591*0Sstevel@tonic-gate return (-5);
592*0Sstevel@tonic-gate }
593*0Sstevel@tonic-gate
594*0Sstevel@tonic-gate return (0);
595*0Sstevel@tonic-gate }
596*0Sstevel@tonic-gate
ldap_digest_md5_encode(const char * challenge,const char * username,const char * passwd,char ** digest)597*0Sstevel@tonic-gate static int ldap_digest_md5_encode(
598*0Sstevel@tonic-gate const char *challenge,
599*0Sstevel@tonic-gate const char *username,
600*0Sstevel@tonic-gate const char *passwd,
601*0Sstevel@tonic-gate char **digest
602*0Sstevel@tonic-gate )
603*0Sstevel@tonic-gate {
604*0Sstevel@tonic-gate unsigned char hash_pass[DIGEST_SIZE];
605*0Sstevel@tonic-gate digest_attrs_t attrs;
606*0Sstevel@tonic-gate char *outbuf;
607*0Sstevel@tonic-gate int outlen;
608*0Sstevel@tonic-gate int ret;
609*0Sstevel@tonic-gate
610*0Sstevel@tonic-gate /* validate args */
611*0Sstevel@tonic-gate if (challenge == NULL || username == NULL || passwd == NULL) {
612*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
613*0Sstevel@tonic-gate }
614*0Sstevel@tonic-gate
615*0Sstevel@tonic-gate /* parse the challenge */
616*0Sstevel@tonic-gate digest_clear(&attrs);
617*0Sstevel@tonic-gate ret = digest_parse(challenge, 0, &attrs);
618*0Sstevel@tonic-gate if (ret != 0)
619*0Sstevel@tonic-gate return (LDAP_DECODING_ERROR);
620*0Sstevel@tonic-gate
621*0Sstevel@tonic-gate /* server MUST specify support for charset=utf-8 */
622*0Sstevel@tonic-gate if (attrs.charsetlen != 5 ||
623*0Sstevel@tonic-gate strncasecmp(attrs.charset, "utf-8", 5) != 0) {
624*0Sstevel@tonic-gate LDAPDebug(LDAP_DEBUG_TRACE,
625*0Sstevel@tonic-gate "server did not specify charset=utf-8\n",
626*0Sstevel@tonic-gate 0, 0, 0);
627*0Sstevel@tonic-gate return (LDAP_NOT_SUPPORTED);
628*0Sstevel@tonic-gate }
629*0Sstevel@tonic-gate
630*0Sstevel@tonic-gate /* set up digest attributes */
631*0Sstevel@tonic-gate attrs.user = username;
632*0Sstevel@tonic-gate attrs.ulen = strlen(attrs.user);
633*0Sstevel@tonic-gate
634*0Sstevel@tonic-gate /* allocate the output buffer */
635*0Sstevel@tonic-gate outlen = strlen(challenge) + DIGEST_CLIENT_EXTRA + 1;
636*0Sstevel@tonic-gate outbuf = (char *)malloc(outlen);
637*0Sstevel@tonic-gate if (outbuf == NULL)
638*0Sstevel@tonic-gate return (LDAP_NO_MEMORY);
639*0Sstevel@tonic-gate
640*0Sstevel@tonic-gate /* hash the password */
641*0Sstevel@tonic-gate digest_hash_pass(username, 0, attrs.realm, attrs.rlen,
642*0Sstevel@tonic-gate passwd, 0, 0, hash_pass),
643*0Sstevel@tonic-gate
644*0Sstevel@tonic-gate /* create the response */
645*0Sstevel@tonic-gate ret = digest_client_resp("AUTHENTICATE", 12, hash_pass, NULL,
646*0Sstevel@tonic-gate &attrs, outbuf, outlen, &outlen);
647*0Sstevel@tonic-gate memset(hash_pass, 0, DIGEST_SIZE);
648*0Sstevel@tonic-gate if (ret != 0) {
649*0Sstevel@tonic-gate free(outbuf);
650*0Sstevel@tonic-gate return (LDAP_DECODING_ERROR);
651*0Sstevel@tonic-gate }
652*0Sstevel@tonic-gate
653*0Sstevel@tonic-gate /* null terminate the response */
654*0Sstevel@tonic-gate *(outbuf+outlen) = '\0';
655*0Sstevel@tonic-gate
656*0Sstevel@tonic-gate *digest = outbuf;
657*0Sstevel@tonic-gate return (LDAP_SUCCESS);
658*0Sstevel@tonic-gate }
659*0Sstevel@tonic-gate
ldap_x_sasl_digest_md5_bind_s(LDAP * ld,char * user_name,struct berval * cred,LDAPControl ** serverctrls,LDAPControl ** clientctrls)660*0Sstevel@tonic-gate int ldap_x_sasl_digest_md5_bind_s(
661*0Sstevel@tonic-gate LDAP *ld,
662*0Sstevel@tonic-gate char *user_name,
663*0Sstevel@tonic-gate struct berval *cred,
664*0Sstevel@tonic-gate LDAPControl **serverctrls,
665*0Sstevel@tonic-gate LDAPControl **clientctrls)
666*0Sstevel@tonic-gate {
667*0Sstevel@tonic-gate struct berval *challenge = NULL;
668*0Sstevel@tonic-gate int errnum;
669*0Sstevel@tonic-gate char *digest = NULL;
670*0Sstevel@tonic-gate struct berval resp;
671*0Sstevel@tonic-gate
672*0Sstevel@tonic-gate LDAPDebug(LDAP_DEBUG_TRACE, "ldap_x_sasl_digest_md5_bind_s\n", 0, 0, 0);
673*0Sstevel@tonic-gate
674*0Sstevel@tonic-gate /* Add debug */
675*0Sstevel@tonic-gate if (ld == NULL || user_name == NULL || cred == NULL ||
676*0Sstevel@tonic-gate cred->bv_val == NULL)
677*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
678*0Sstevel@tonic-gate
679*0Sstevel@tonic-gate if (ld->ld_version < LDAP_VERSION3)
680*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
681*0Sstevel@tonic-gate
682*0Sstevel@tonic-gate errnum = ldap_sasl_bind_s(ld, NULL, LDAP_SASL_DIGEST_MD5,
683*0Sstevel@tonic-gate NULL, serverctrls, clientctrls, &challenge);
684*0Sstevel@tonic-gate
685*0Sstevel@tonic-gate if (errnum == LDAP_SASL_BIND_IN_PROGRESS) {
686*0Sstevel@tonic-gate if (challenge != NULL) {
687*0Sstevel@tonic-gate LDAPDebug(LDAP_DEBUG_TRACE,
688*0Sstevel@tonic-gate "SASL challenge: %s\n",
689*0Sstevel@tonic-gate challenge->bv_val, 0, 0);
690*0Sstevel@tonic-gate errnum = ldap_digest_md5_encode(challenge->bv_val,
691*0Sstevel@tonic-gate user_name, cred->bv_val, &digest);
692*0Sstevel@tonic-gate ber_bvfree(challenge);
693*0Sstevel@tonic-gate challenge = NULL;
694*0Sstevel@tonic-gate if (errnum == LDAP_SUCCESS) {
695*0Sstevel@tonic-gate resp.bv_val = digest;
696*0Sstevel@tonic-gate resp.bv_len = strlen(digest);
697*0Sstevel@tonic-gate LDAPDebug(LDAP_DEBUG_TRACE,
698*0Sstevel@tonic-gate "SASL reply: %s\n",
699*0Sstevel@tonic-gate digest, 0, 0);
700*0Sstevel@tonic-gate errnum = ldap_sasl_bind_s(ld, NULL,
701*0Sstevel@tonic-gate LDAP_SASL_DIGEST_MD5, &resp,
702*0Sstevel@tonic-gate serverctrls, clientctrls, &challenge);
703*0Sstevel@tonic-gate free(digest);
704*0Sstevel@tonic-gate }
705*0Sstevel@tonic-gate if (challenge != NULL)
706*0Sstevel@tonic-gate ber_bvfree(challenge);
707*0Sstevel@tonic-gate } else {
708*0Sstevel@tonic-gate errnum = LDAP_NO_MEMORY; /* TO DO: What val? */
709*0Sstevel@tonic-gate }
710*0Sstevel@tonic-gate }
711*0Sstevel@tonic-gate
712*0Sstevel@tonic-gate LDAP_MUTEX_LOCK(ld, LDAP_ERR_LOCK);
713*0Sstevel@tonic-gate ld->ld_errno = errnum;
714*0Sstevel@tonic-gate LDAP_MUTEX_UNLOCK(ld, LDAP_ERR_LOCK);
715*0Sstevel@tonic-gate return (errnum);
716*0Sstevel@tonic-gate }
717*0Sstevel@tonic-gate
718*0Sstevel@tonic-gate static int
sasl_digest_md5_bind_1(LDAP * ld,char * user_name,LDAPControl ** serverctrls,LDAPControl ** clientctrls,int * msgidp)719*0Sstevel@tonic-gate sasl_digest_md5_bind_1(
720*0Sstevel@tonic-gate LDAP *ld,
721*0Sstevel@tonic-gate char *user_name,
722*0Sstevel@tonic-gate LDAPControl **serverctrls,
723*0Sstevel@tonic-gate LDAPControl **clientctrls,
724*0Sstevel@tonic-gate int *msgidp)
725*0Sstevel@tonic-gate {
726*0Sstevel@tonic-gate if (ld == NULL || user_name == NULL || msgidp == NULL)
727*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
728*0Sstevel@tonic-gate
729*0Sstevel@tonic-gate if (ld->ld_version < LDAP_VERSION3)
730*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
731*0Sstevel@tonic-gate
732*0Sstevel@tonic-gate return (ldap_sasl_bind(ld, NULL, LDAP_SASL_DIGEST_MD5,
733*0Sstevel@tonic-gate NULL, serverctrls, clientctrls, msgidp));
734*0Sstevel@tonic-gate }
735*0Sstevel@tonic-gate
736*0Sstevel@tonic-gate static int
sasl_digest_md5_bind_2(LDAP * ld,char * user_name,struct berval * cred,LDAPControl ** serverctrls,LDAPControl ** clientctrls,LDAPMessage * result,int * msgidp)737*0Sstevel@tonic-gate sasl_digest_md5_bind_2(
738*0Sstevel@tonic-gate LDAP *ld,
739*0Sstevel@tonic-gate char *user_name,
740*0Sstevel@tonic-gate struct berval *cred,
741*0Sstevel@tonic-gate LDAPControl **serverctrls,
742*0Sstevel@tonic-gate LDAPControl **clientctrls,
743*0Sstevel@tonic-gate LDAPMessage *result,
744*0Sstevel@tonic-gate int *msgidp)
745*0Sstevel@tonic-gate {
746*0Sstevel@tonic-gate struct berval *challenge = NULL;
747*0Sstevel@tonic-gate struct berval resp;
748*0Sstevel@tonic-gate int errnum;
749*0Sstevel@tonic-gate char *digest = NULL;
750*0Sstevel@tonic-gate int err;
751*0Sstevel@tonic-gate
752*0Sstevel@tonic-gate if (ld == NULL || user_name == NULL || cred == NULL ||
753*0Sstevel@tonic-gate cred->bv_val == NULL || result == NULL || msgidp == NULL)
754*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
755*0Sstevel@tonic-gate
756*0Sstevel@tonic-gate if (ld->ld_version < LDAP_VERSION3)
757*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
758*0Sstevel@tonic-gate
759*0Sstevel@tonic-gate err = ldap_result2error(ld, result, 0);
760*0Sstevel@tonic-gate if (err != LDAP_SASL_BIND_IN_PROGRESS)
761*0Sstevel@tonic-gate return (err);
762*0Sstevel@tonic-gate
763*0Sstevel@tonic-gate if ((err = ldap_parse_sasl_bind_result(ld, result, &challenge, 0))
764*0Sstevel@tonic-gate != LDAP_SUCCESS)
765*0Sstevel@tonic-gate return (err);
766*0Sstevel@tonic-gate if (challenge == NULL)
767*0Sstevel@tonic-gate return (LDAP_NO_MEMORY);
768*0Sstevel@tonic-gate
769*0Sstevel@tonic-gate err = ldap_digest_md5_encode(challenge->bv_val,
770*0Sstevel@tonic-gate user_name, cred->bv_val, &digest);
771*0Sstevel@tonic-gate ber_bvfree(challenge);
772*0Sstevel@tonic-gate
773*0Sstevel@tonic-gate if (err == LDAP_SUCCESS) {
774*0Sstevel@tonic-gate resp.bv_val = digest;
775*0Sstevel@tonic-gate resp.bv_len = strlen(digest);
776*0Sstevel@tonic-gate LDAPDebug(LDAP_DEBUG_TRACE, "SASL reply: %s\n",
777*0Sstevel@tonic-gate digest, 0, 0);
778*0Sstevel@tonic-gate err = ldap_sasl_bind(ld, NULL, LDAP_SASL_DIGEST_MD5,
779*0Sstevel@tonic-gate &resp, serverctrls, clientctrls, msgidp);
780*0Sstevel@tonic-gate free(digest);
781*0Sstevel@tonic-gate }
782*0Sstevel@tonic-gate return (err);
783*0Sstevel@tonic-gate }
784*0Sstevel@tonic-gate
ldap_x_sasl_digest_md5_bind(LDAP * ld,char * user_name,struct berval * cred,LDAPControl ** serverctrls,LDAPControl ** clientctrls,struct timeval * timeout,LDAPMessage ** result)785*0Sstevel@tonic-gate int ldap_x_sasl_digest_md5_bind(
786*0Sstevel@tonic-gate LDAP *ld,
787*0Sstevel@tonic-gate char *user_name,
788*0Sstevel@tonic-gate struct berval *cred,
789*0Sstevel@tonic-gate LDAPControl **serverctrls,
790*0Sstevel@tonic-gate LDAPControl **clientctrls,
791*0Sstevel@tonic-gate struct timeval *timeout,
792*0Sstevel@tonic-gate LDAPMessage **result)
793*0Sstevel@tonic-gate {
794*0Sstevel@tonic-gate LDAPMessage *res = NULL;
795*0Sstevel@tonic-gate int msgid;
796*0Sstevel@tonic-gate int rc;
797*0Sstevel@tonic-gate
798*0Sstevel@tonic-gate if (ld == NULL || user_name == NULL || cred == NULL ||
799*0Sstevel@tonic-gate result == NULL)
800*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
801*0Sstevel@tonic-gate
802*0Sstevel@tonic-gate if (ld->ld_version < LDAP_VERSION3)
803*0Sstevel@tonic-gate return (LDAP_PARAM_ERROR);
804*0Sstevel@tonic-gate
805*0Sstevel@tonic-gate *result = NULL;
806*0Sstevel@tonic-gate
807*0Sstevel@tonic-gate rc = sasl_digest_md5_bind_1(ld, user_name,
808*0Sstevel@tonic-gate serverctrls, clientctrls, &msgid);
809*0Sstevel@tonic-gate if (rc != LDAP_SUCCESS)
810*0Sstevel@tonic-gate return (rc);
811*0Sstevel@tonic-gate
812*0Sstevel@tonic-gate rc = ldap_result(ld, msgid, 1, timeout, &res);
813*0Sstevel@tonic-gate if (rc == -1) {
814*0Sstevel@tonic-gate if (res != NULL)
815*0Sstevel@tonic-gate ldap_msgfree(res);
816*0Sstevel@tonic-gate return (ldap_get_lderrno(ld, NULL, NULL));
817*0Sstevel@tonic-gate }
818*0Sstevel@tonic-gate rc = ldap_result2error(ld, res, 0);
819*0Sstevel@tonic-gate if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
820*0Sstevel@tonic-gate *result = res;
821*0Sstevel@tonic-gate return (rc);
822*0Sstevel@tonic-gate }
823*0Sstevel@tonic-gate
824*0Sstevel@tonic-gate rc = sasl_digest_md5_bind_2(ld, user_name, cred,
825*0Sstevel@tonic-gate serverctrls, clientctrls, res, &msgid);
826*0Sstevel@tonic-gate ldap_msgfree(res);
827*0Sstevel@tonic-gate res = NULL;
828*0Sstevel@tonic-gate
829*0Sstevel@tonic-gate if (rc != LDAP_SUCCESS)
830*0Sstevel@tonic-gate return (rc);
831*0Sstevel@tonic-gate
832*0Sstevel@tonic-gate rc = ldap_result(ld, msgid, 1, timeout, &res);
833*0Sstevel@tonic-gate if (rc == -1) {
834*0Sstevel@tonic-gate if (res != NULL)
835*0Sstevel@tonic-gate ldap_msgfree(res);
836*0Sstevel@tonic-gate return (ldap_get_lderrno(ld, NULL, NULL));
837*0Sstevel@tonic-gate }
838*0Sstevel@tonic-gate *result = res;
839*0Sstevel@tonic-gate rc = ldap_result2error(ld, res, 0);
840*0Sstevel@tonic-gate return (rc);
841*0Sstevel@tonic-gate }
842