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