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