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