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 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 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 * 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 * 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 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 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 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 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 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 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 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 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