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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2023 RackTop Systems, Inc. 24 */ 25 26 #include <alloca.h> 27 #include <string.h> 28 #include <strings.h> 29 #include <lber.h> 30 #include <sasl/sasl.h> 31 #include <string.h> 32 #include <ctype.h> 33 #include <synch.h> 34 #include <atomic.h> 35 #include <errno.h> 36 #include <assert.h> 37 #include <limits.h> 38 #include <syslog.h> 39 #include <sys/u8_textprep.h> 40 #include <sys/varargs.h> 41 #include "libadutils.h" 42 #include "adutils_impl.h" 43 44 /* List of DSs, needed by the idle connection reaper thread */ 45 static pthread_mutex_t adhostlock = PTHREAD_MUTEX_INITIALIZER; 46 static adutils_host_t *host_head = NULL; 47 48 /* 49 * List of query state structs -- needed so we can "route" LDAP results 50 * to the right context if multiple threads should be using the same 51 * connection concurrently 52 */ 53 static pthread_mutex_t qstatelock = PTHREAD_MUTEX_INITIALIZER; 54 static adutils_query_state_t *qstatehead = NULL; 55 56 static char *adutils_sid_ber2str(BerValue *bvalues); 57 static void adutils_lookup_batch_unlock(adutils_query_state_t **state); 58 static void delete_ds(adutils_ad_t *ad, const char *host, int port); 59 60 int ad_debug[AD_DEBUG_MAX+1] = {0}; 61 62 typedef struct binary_attrs { 63 const char *name; 64 char *(*ber2str)(BerValue *bvalues); 65 } binary_attrs_t; 66 67 static binary_attrs_t binattrs[] = { 68 {"objectSID", adutils_sid_ber2str}, 69 {NULL, NULL} 70 }; 71 72 73 adutils_logger logger = syslog; 74 75 76 void 77 adutils_set_logger(adutils_logger funct) 78 { 79 logger = funct; 80 } 81 82 83 /* 84 * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com" 85 */ 86 static 87 char * 88 adutils_dns2dn(const char *dns) 89 { 90 int num_parts; 91 92 return (ldap_dns_to_dn((char *)dns, &num_parts)); 93 } 94 95 96 /* 97 * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other 98 * attributes (CN, etc...). 99 */ 100 char * 101 adutils_dn2dns(const char *dn) 102 { 103 return (DN_to_DNS(dn)); 104 } 105 106 107 /* 108 * Convert a binary SID in a BerValue to a adutils_sid_t 109 */ 110 int 111 adutils_getsid(BerValue *bval, adutils_sid_t *sidp) 112 { 113 int i, j; 114 uchar_t *v; 115 uint32_t a; 116 117 /* 118 * The binary format of a SID is as follows: 119 * 120 * byte #0: version, always 0x01 121 * byte #1: RID count, always <= 0x0f 122 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int 123 * 124 * followed by RID count RIDs, each a little-endian, unsigned 125 * 32-bit int. 126 */ 127 /* 128 * Sanity checks: must have at least one RID, version must be 129 * 0x01, and the length must be 8 + rid count * 4 130 */ 131 if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 && 132 bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) { 133 v = (uchar_t *)bval->bv_val; 134 sidp->version = v[0]; 135 sidp->sub_authority_count = v[1]; 136 sidp->authority = 137 /* big endian -- so start from the left */ 138 ((u_longlong_t)v[2] << 40) | 139 ((u_longlong_t)v[3] << 32) | 140 ((u_longlong_t)v[4] << 24) | 141 ((u_longlong_t)v[5] << 16) | 142 ((u_longlong_t)v[6] << 8) | 143 (u_longlong_t)v[7]; 144 for (i = 0; i < sidp->sub_authority_count; i++) { 145 j = 8 + (i * 4); 146 /* little endian -- so start from the right */ 147 a = (v[j + 3] << 24) | (v[j + 2] << 16) | 148 (v[j + 1] << 8) | (v[j]); 149 sidp->sub_authorities[i] = a; 150 } 151 return (0); 152 } 153 return (-1); 154 } 155 156 /* 157 * Convert a adutils_sid_t to S-1-... 158 */ 159 char * 160 adutils_sid2txt(adutils_sid_t *sidp) 161 { 162 int rlen, i, len; 163 char *str, *cp; 164 165 if (sidp->version != 1) 166 return (NULL); 167 168 len = sizeof ("S-1-") - 1; 169 170 /* 171 * We could optimize like so, but, why? 172 * if (sidp->authority < 10) 173 * len += 2; 174 * else if (sidp->authority < 100) 175 * len += 3; 176 * else 177 * len += snprintf(NULL, 0"%llu", sidp->authority); 178 */ 179 len += snprintf(NULL, 0, "%llu", sidp->authority); 180 181 /* Max length of a uint32_t printed out in ASCII is 10 bytes */ 182 len += 1 + (sidp->sub_authority_count + 1) * 10; 183 184 if ((cp = str = malloc(len)) == NULL) 185 return (NULL); 186 187 rlen = snprintf(str, len, "S-1-%llu", sidp->authority); 188 189 cp += rlen; 190 len -= rlen; 191 192 for (i = 0; i < sidp->sub_authority_count; i++) { 193 assert(len > 0); 194 rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]); 195 cp += rlen; 196 len -= rlen; 197 assert(len >= 0); 198 } 199 200 return (str); 201 } 202 203 /* 204 * Convert a adutils_sid_t to on-the-wire encoding 205 */ 206 static 207 int 208 sid2binsid(adutils_sid_t *sid, uchar_t *binsid, int binsidlen) 209 { 210 uchar_t *p; 211 int i; 212 uint64_t a; 213 uint32_t r; 214 215 if (sid->version != 1 || 216 binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4)) 217 return (-1); 218 219 p = binsid; 220 *p++ = 0x01; /* version */ 221 /* sub authority count */ 222 *p++ = sid->sub_authority_count; 223 /* Authority */ 224 a = sid->authority; 225 /* big-endian -- start from left */ 226 *p++ = (a >> 40) & 0xFF; 227 *p++ = (a >> 32) & 0xFF; 228 *p++ = (a >> 24) & 0xFF; 229 *p++ = (a >> 16) & 0xFF; 230 *p++ = (a >> 8) & 0xFF; 231 *p++ = a & 0xFF; 232 233 /* sub-authorities */ 234 for (i = 0; i < sid->sub_authority_count; i++) { 235 r = sid->sub_authorities[i]; 236 /* little-endian -- start from right */ 237 *p++ = (r & 0x000000FF); 238 *p++ = (r & 0x0000FF00) >> 8; 239 *p++ = (r & 0x00FF0000) >> 16; 240 *p++ = (r & 0xFF000000) >> 24; 241 } 242 243 return (0); 244 } 245 246 /* 247 * Convert a stringified SID (S-1-...) into a hex-encoded version of the 248 * on-the-wire encoding, but with each pair of hex digits pre-pended 249 * with a '\', so we can pass this to libldap. 250 */ 251 int 252 adutils_txtsid2hexbinsid(const char *txt, const uint32_t *rid, 253 char *hexbinsid, int hexbinsidlen) 254 { 255 adutils_sid_t sid = { 0 }; 256 int i, j; 257 const char *cp; 258 char *ecp; 259 u_longlong_t a; 260 unsigned long r; 261 uchar_t *binsid, b, hb; 262 263 /* Only version 1 SIDs please */ 264 if (strncmp(txt, "S-1-", strlen("S-1-")) != 0) 265 return (-1); 266 267 if (strlen(txt) < (strlen("S-1-") + 1)) 268 return (-1); 269 270 /* count '-'s */ 271 for (j = 0, cp = strchr(txt, '-'); 272 cp != NULL && *cp != '\0'; 273 j++, cp = strchr(cp + 1, '-')) { 274 /* can't end on a '-' */ 275 if (*(cp + 1) == '\0') 276 return (-1); 277 } 278 279 /* Adjust count for version and authority */ 280 j -= 2; 281 282 /* we know the version number and RID count */ 283 sid.version = 1; 284 sid.sub_authority_count = (rid != NULL) ? j + 1 : j; 285 286 /* must have at least one RID, but not too many */ 287 if (sid.sub_authority_count < 1 || 288 sid.sub_authority_count > ADUTILS_SID_MAX_SUB_AUTHORITIES) 289 return (-1); 290 291 /* check that we only have digits and '-' */ 292 if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1)) 293 return (-1); 294 295 cp = txt + strlen("S-1-"); 296 297 /* 64-bit safe parsing of unsigned 48-bit authority value */ 298 errno = 0; 299 a = strtoull(cp, &ecp, 10); 300 301 /* errors parsing the authority or too many bits */ 302 if (cp == ecp || (a == 0 && errno == EINVAL) || 303 (a == ULLONG_MAX && errno == ERANGE) || 304 (a & 0x0000ffffffffffffULL) != a) 305 return (-1); 306 307 cp = ecp; 308 309 sid.authority = (uint64_t)a; 310 311 for (i = 0; i < j; i++) { 312 if (*cp++ != '-') 313 return (-1); 314 /* 64-bit safe parsing of unsigned 32-bit RID */ 315 errno = 0; 316 r = strtoul(cp, &ecp, 10); 317 /* errors parsing the RID or too many bits */ 318 if (cp == ecp || (r == 0 && errno == EINVAL) || 319 (r == ULONG_MAX && errno == ERANGE) || 320 (r & 0xffffffffUL) != r) 321 return (-1); 322 sid.sub_authorities[i] = (uint32_t)r; 323 cp = ecp; 324 } 325 326 /* check that all of the string SID has been consumed */ 327 if (*cp != '\0') 328 return (-1); 329 330 if (rid != NULL) 331 sid.sub_authorities[j] = *rid; 332 333 j = 1 + 1 + 6 + sid.sub_authority_count * 4; 334 335 if (hexbinsidlen < (j * 3)) 336 return (-2); 337 338 /* binary encode the SID */ 339 binsid = (uchar_t *)alloca(j); 340 (void) sid2binsid(&sid, binsid, j); 341 342 /* hex encode, with a backslash before each byte */ 343 for (ecp = hexbinsid, i = 0; i < j; i++) { 344 b = binsid[i]; 345 *ecp++ = '\\'; 346 hb = (b >> 4) & 0xF; 347 *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); 348 hb = b & 0xF; 349 *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); 350 } 351 *ecp = '\0'; 352 353 return (0); 354 } 355 356 static 357 char * 358 convert_bval2sid(BerValue *bval, uint32_t *rid) 359 { 360 adutils_sid_t sid; 361 362 if (adutils_getsid(bval, &sid) < 0) 363 return (NULL); 364 365 /* 366 * If desired and if the SID is what should be a domain/computer 367 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then 368 * save the last RID and truncate the SID 369 */ 370 if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5) 371 *rid = sid.sub_authorities[--sid.sub_authority_count]; 372 return (adutils_sid2txt(&sid)); 373 } 374 375 376 /* 377 * Return a NUL-terminated stringified SID from the value of an 378 * objectSid attribute and put the last RID in *rid. 379 */ 380 char * 381 adutils_bv_objsid2sidstr(BerValue *bval, uint32_t *rid) 382 { 383 char *sid; 384 385 if (bval == NULL) 386 return (NULL); 387 /* objectSid is single valued */ 388 if ((sid = convert_bval2sid(bval, rid)) == NULL) 389 return (NULL); 390 return (sid); 391 } 392 393 static 394 char * 395 adutils_sid_ber2str(BerValue *bval) 396 { 397 return (adutils_bv_objsid2sidstr(bval, NULL)); 398 } 399 400 401 /* 402 * Extract an int from the Ber value 403 * Return B_TRUE if a valid integer was found, B_FALSE if not. 404 */ 405 boolean_t 406 adutils_bv_uint(BerValue *bval, unsigned int *result) 407 { 408 char buf[40]; /* big enough for any int */ 409 unsigned int tmp; 410 char *p; 411 412 *result = 0; /* for error cases */ 413 414 if (bval == NULL || bval->bv_val == NULL) 415 return (B_FALSE); 416 if (bval->bv_len >= sizeof (buf)) 417 return (B_FALSE); 418 419 (void) memcpy(buf, bval->bv_val, bval->bv_len); 420 buf[bval->bv_len] = '\0'; 421 422 tmp = strtoul(buf, &p, 10); 423 424 /* Junk after the number? */ 425 if (*p != '\0') 426 return (B_FALSE); 427 428 *result = tmp; 429 430 return (B_TRUE); 431 } 432 433 /* Return a NUL-terminated string from the Ber value */ 434 char * 435 adutils_bv_str(BerValue *bval) 436 { 437 char *s; 438 439 if (bval == NULL || bval->bv_val == NULL) 440 return (NULL); 441 if ((s = malloc(bval->bv_len + 1)) == NULL) 442 return (NULL); 443 (void) snprintf(s, bval->bv_len + 1, "%.*s", bval->bv_len, 444 bval->bv_val); 445 return (s); 446 } 447 448 /*ARGSUSED*/ 449 int 450 saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) 451 { 452 sasl_interact_t *interact; 453 454 if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE) 455 return (LDAP_PARAM_ERROR); 456 457 /* There should be no extra arguemnts for SASL/GSSAPI authentication */ 458 for (interact = prompts; interact->id != SASL_CB_LIST_END; 459 interact++) { 460 interact->result = NULL; 461 interact->len = 0; 462 } 463 return (LDAP_SUCCESS); 464 } 465 466 467 #define ADCONN_TIME 300 468 469 /* 470 * Idle connection reaping side of connection management 471 */ 472 void 473 adutils_reap_idle_connections() 474 { 475 adutils_host_t *adh; 476 time_t now; 477 478 (void) pthread_mutex_lock(&adhostlock); 479 now = time(NULL); 480 for (adh = host_head; adh != NULL; adh = adh->next) { 481 (void) pthread_mutex_lock(&adh->lock); 482 if (adh->ref == 0 && adh->idletime != 0 && 483 adh->idletime + ADCONN_TIME < now) { 484 if (adh->ld) { 485 (void) ldap_unbind(adh->ld); 486 adh->ld = NULL; 487 adh->idletime = 0; 488 adh->ref = 0; 489 } 490 } 491 (void) pthread_mutex_unlock(&adh->lock); 492 } 493 (void) pthread_mutex_unlock(&adhostlock); 494 } 495 496 497 adutils_rc 498 adutils_ad_alloc(adutils_ad_t **new_ad, const char *domain_name, 499 adutils_ad_partition_t part) 500 { 501 adutils_ad_t *ad; 502 503 *new_ad = NULL; 504 505 if ((ad = calloc(1, sizeof (*ad))) == NULL) 506 return (ADUTILS_ERR_MEMORY); 507 ad->ref = 1; 508 ad->partition = part; 509 510 /* domain_name is required iff we are talking directly to a DC */ 511 if (part == ADUTILS_AD_DATA) { 512 assert(domain_name != NULL); 513 assert(*domain_name != '\0'); 514 515 ad->basedn = adutils_dns2dn(domain_name); 516 } else { 517 assert(domain_name == NULL); 518 ad->basedn = strdup(""); 519 } 520 if (ad->basedn == NULL) 521 goto err; 522 523 if (pthread_mutex_init(&ad->lock, NULL) != 0) 524 goto err; 525 *new_ad = ad; 526 return (ADUTILS_SUCCESS); 527 528 err: 529 free(ad->basedn); 530 free(ad); 531 return (ADUTILS_ERR_MEMORY); 532 } 533 534 void 535 adutils_ad_free(adutils_ad_t **ad) 536 { 537 adutils_host_t *p; 538 adutils_host_t *prev; 539 540 if (ad == NULL || *ad == NULL) 541 return; 542 543 (void) pthread_mutex_lock(&(*ad)->lock); 544 545 if (atomic_dec_32_nv(&(*ad)->ref) > 0) { 546 (void) pthread_mutex_unlock(&(*ad)->lock); 547 *ad = NULL; 548 return; 549 } 550 551 (void) pthread_mutex_lock(&adhostlock); 552 prev = NULL; 553 p = host_head; 554 while (p != NULL) { 555 if (p->owner != (*ad)) { 556 prev = p; 557 p = p->next; 558 continue; 559 } else { 560 delete_ds((*ad), p->host, p->port); 561 if (prev == NULL) 562 p = host_head; 563 else 564 p = prev->next; 565 } 566 } 567 (void) pthread_mutex_unlock(&adhostlock); 568 569 (void) pthread_mutex_unlock(&(*ad)->lock); 570 (void) pthread_mutex_destroy(&(*ad)->lock); 571 572 if ((*ad)->known_domains) 573 free((*ad)->known_domains); 574 free((*ad)->basedn); 575 free(*ad); 576 577 *ad = NULL; 578 } 579 580 static 581 int 582 open_conn(adutils_host_t *adh, int timeoutsecs) 583 { 584 int zero = 0; 585 int ldversion, rc; 586 int timeoutms = timeoutsecs * 1000; 587 588 if (adh == NULL) 589 return (0); 590 591 (void) pthread_mutex_lock(&adh->lock); 592 593 if (!adh->dead && adh->ld != NULL) 594 /* done! */ 595 goto out; 596 597 if (adh->ld != NULL) { 598 (void) ldap_unbind(adh->ld); 599 adh->ld = NULL; 600 } 601 adh->num_requests = 0; 602 603 atomic_inc_64(&adh->generation); 604 605 /* Open and bind an LDAP connection */ 606 adh->ld = ldap_init(adh->host, adh->port); 607 if (adh->ld == NULL) { 608 logger(LOG_INFO, "ldap_init() to server " 609 "%s port %d failed. (%s)", adh->host, 610 adh->port, strerror(errno)); 611 goto out; 612 } 613 ldversion = LDAP_VERSION3; 614 (void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion); 615 (void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 616 (void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero); 617 (void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero); 618 (void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms); 619 (void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 620 621 rc = adutils_set_thread_functions(adh->ld); 622 if (rc != LDAP_SUCCESS) { 623 /* Error has already been logged */ 624 (void) ldap_unbind(adh->ld); 625 adh->ld = NULL; 626 goto out; 627 } 628 629 rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */, 630 adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback, 631 NULL); 632 633 if (rc != LDAP_SUCCESS) { 634 logger(LOG_INFO, "ldap_sasl_interactive_bind_s() to server " 635 "%s port %d failed. (%s)", adh->host, adh->port, 636 ldap_err2string(rc)); 637 ldap_perror(adh->ld, adh->host); 638 (void) ldap_unbind(adh->ld); 639 adh->ld = NULL; 640 goto out; 641 } 642 643 logger(LOG_DEBUG, "Using server %s:%d", 644 adh->host, adh->port); 645 646 out: 647 if (adh->ld != NULL) { 648 atomic_inc_32(&adh->ref); 649 adh->idletime = time(NULL); 650 adh->dead = 0; 651 (void) pthread_mutex_unlock(&adh->lock); 652 return (1); 653 } 654 655 (void) pthread_mutex_unlock(&adh->lock); 656 return (0); 657 } 658 659 660 /* 661 * Connection management: find an open connection or open one 662 */ 663 static 664 adutils_host_t * 665 get_conn(adutils_ad_t *ad) 666 { 667 adutils_host_t *adh = NULL; 668 int tries; 669 int dscount = 0; 670 int timeoutsecs = ADUTILS_LDAP_OPEN_TIMEOUT; 671 672 retry: 673 (void) pthread_mutex_lock(&adhostlock); 674 675 if (host_head == NULL) { 676 (void) pthread_mutex_unlock(&adhostlock); 677 goto out; 678 } 679 680 if (dscount == 0) { 681 /* 682 * First try: count the number of DSes. 683 * 684 * Integer overflow is not an issue -- we can't have so many 685 * DSes because they won't fit even DNS over TCP, and SMF 686 * shouldn't let you set so many. 687 */ 688 for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) { 689 if (adh->owner == ad) 690 dscount++; 691 } 692 693 if (dscount == 0) { 694 (void) pthread_mutex_unlock(&adhostlock); 695 goto out; 696 } 697 698 tries = dscount * 3; /* three tries per-ds */ 699 700 /* 701 * Begin round-robin at the next DS in the list after the last 702 * one that we had a connection to, else start with the first 703 * DS in the list. 704 */ 705 adh = ad->last_adh; 706 } 707 708 /* 709 * Round-robin -- pick the next one on the list; if the list 710 * changes on us, no big deal, we'll just potentially go 711 * around the wrong number of times. 712 */ 713 for (;;) { 714 if (adh != NULL && adh->owner == ad && adh->ld != NULL && 715 !adh->dead) 716 break; 717 if (adh == NULL || (adh = adh->next) == NULL) 718 adh = host_head; 719 if (adh->owner == ad) 720 break; 721 } 722 723 ad->last_adh = adh; 724 (void) pthread_mutex_unlock(&adhostlock); 725 726 /* Found suitable DS, open it if not already opened */ 727 if (open_conn(adh, timeoutsecs)) 728 return (adh); 729 730 tries--; 731 if ((tries % dscount) == 0) 732 timeoutsecs *= 2; 733 if (tries > 0) 734 goto retry; 735 736 out: 737 logger(LOG_NOTICE, "Couldn't open an LDAP connection to any global " 738 "catalog server!"); 739 return (NULL); 740 } 741 742 static 743 void 744 release_conn(adutils_host_t *adh) 745 { 746 int delete = 0; 747 748 (void) pthread_mutex_lock(&adh->lock); 749 if (atomic_dec_32_nv(&adh->ref) == 0) { 750 if (adh->owner == NULL) 751 delete = 1; 752 adh->idletime = time(NULL); 753 } 754 (void) pthread_mutex_unlock(&adh->lock); 755 756 /* Free this host if its owner no longer exists. */ 757 if (delete) { 758 (void) pthread_mutex_lock(&adhostlock); 759 delete_ds(NULL, adh->host, adh->port); 760 (void) pthread_mutex_unlock(&adhostlock); 761 } 762 } 763 764 /* 765 * Create a adutils_host_t, populate it and add it to the list of hosts. 766 */ 767 adutils_rc 768 adutils_add_ds(adutils_ad_t *ad, const char *host, int port) 769 { 770 adutils_host_t *p; 771 adutils_host_t *new = NULL; 772 int ret; 773 adutils_rc rc; 774 775 (void) pthread_mutex_lock(&adhostlock); 776 for (p = host_head; p != NULL; p = p->next) { 777 if (p->owner != ad) 778 continue; 779 780 if (strcmp(host, p->host) == 0 && p->port == port) { 781 /* already added */ 782 rc = ADUTILS_SUCCESS; 783 goto err; 784 } 785 } 786 787 rc = ADUTILS_ERR_MEMORY; 788 789 /* add new entry */ 790 new = (adutils_host_t *)calloc(1, sizeof (*new)); 791 if (new == NULL) 792 goto err; 793 new->owner = ad; 794 new->port = port; 795 new->dead = 0; 796 new->max_requests = 80; 797 new->num_requests = 0; 798 if ((new->host = strdup(host)) == NULL) 799 goto err; 800 new->saslflags = LDAP_SASL_INTERACTIVE; 801 new->saslmech = "GSSAPI"; 802 803 if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) { 804 free(new->host); 805 new->host = NULL; 806 errno = ret; 807 rc = ADUTILS_ERR_INTERNAL; 808 goto err; 809 } 810 811 /* link in */ 812 rc = ADUTILS_SUCCESS; 813 new->next = host_head; 814 host_head = new; 815 816 err: 817 (void) pthread_mutex_unlock(&adhostlock); 818 819 if (rc != 0 && new != NULL) { 820 if (new->host != NULL) { 821 (void) pthread_mutex_destroy(&new->lock); 822 free(new->host); 823 } 824 free(new); 825 } 826 827 return (rc); 828 } 829 830 /* 831 * Free a DS configuration. 832 * Caller must lock the adhostlock mutex 833 */ 834 static 835 void 836 delete_ds(adutils_ad_t *ad, const char *host, int port) 837 { 838 adutils_host_t **p, *q; 839 840 for (p = &host_head; *p != NULL; p = &((*p)->next)) { 841 if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 || 842 (*p)->port != port) 843 continue; 844 /* found */ 845 846 (void) pthread_mutex_lock(&((*p)->lock)); 847 if ((*p)->ref > 0) { 848 /* 849 * Still in use. Set its owner to NULL so 850 * that it can be freed when its ref count 851 * becomes 0. 852 */ 853 (*p)->owner = NULL; 854 (void) pthread_mutex_unlock(&((*p)->lock)); 855 break; 856 } 857 (void) pthread_mutex_unlock(&((*p)->lock)); 858 859 q = *p; 860 *p = (*p)->next; 861 862 (void) pthread_mutex_destroy(&q->lock); 863 864 if (q->ld) 865 (void) ldap_unbind(q->ld); 866 if (q->host) 867 free(q->host); 868 free(q); 869 break; 870 } 871 872 } 873 /* 874 * Add known domain name and domain SID to AD configuration. 875 */ 876 877 adutils_rc 878 adutils_add_domain(adutils_ad_t *ad, const char *domain, const char *sid) 879 { 880 struct known_domain *new; 881 int num = ad->num_known_domains; 882 883 ad->num_known_domains++; 884 new = realloc(ad->known_domains, 885 sizeof (struct known_domain) * ad->num_known_domains); 886 if (new != NULL) { 887 ad->known_domains = new; 888 (void) strlcpy(ad->known_domains[num].name, domain, 889 sizeof (ad->known_domains[num].name)); 890 (void) strlcpy(ad->known_domains[num].sid, sid, 891 sizeof (ad->known_domains[num].sid)); 892 return (ADUTILS_SUCCESS); 893 } else { 894 if (ad->known_domains != NULL) { 895 free(ad->known_domains); 896 ad->known_domains = NULL; 897 } 898 ad->num_known_domains = 0; 899 return (ADUTILS_ERR_MEMORY); 900 } 901 } 902 903 904 /* 905 * Check that this AD supports this domain. 906 * If there are no known domains assume that the 907 * domain is supported by this AD. 908 * 909 * Returns 1 if this domain is supported by this AD 910 * else returns 0; 911 */ 912 913 int 914 adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain) 915 { 916 adutils_ad_t *ad = qs->qadh->owner; 917 int i; 918 919 for (i = 0; i < ad->num_known_domains; i++) { 920 if (domain_eq(domain, ad->known_domains[i].name)) 921 return (1); 922 } 923 924 return ((i == 0) ? 1 : 0); 925 } 926 927 928 /* 929 * Check that this AD supports the SID prefix. 930 * The SID prefix should match the domain SID. 931 * If there are no known domains assume that the 932 * SID prefix is supported by this AD. 933 * 934 * Returns 1 if this sid prefix is supported by this AD 935 * else returns 0; 936 */ 937 938 int 939 adutils_lookup_check_sid_prefix(adutils_query_state_t *qs, const char *sid) 940 { 941 adutils_ad_t *ad = qs->qadh->owner; 942 int i; 943 944 945 for (i = 0; i < ad->num_known_domains; i++) { 946 if (strcmp(sid, ad->known_domains[i].sid) == 0) 947 return (1); 948 } 949 950 return ((i == 0) ? 1 : 0); 951 } 952 953 954 adutils_rc 955 adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries, 956 adutils_ldap_res_search_cb ldap_res_search_cb, 957 void *ldap_res_search_argp, 958 adutils_query_state_t **state) 959 { 960 adutils_query_state_t *new_state; 961 adutils_host_t *adh = NULL; 962 963 if (ad == NULL) 964 return (ADUTILS_ERR_INTERNAL); 965 966 *state = NULL; 967 adh = get_conn(ad); 968 if (adh == NULL) 969 return (ADUTILS_ERR_RETRIABLE_NET_ERR); 970 971 new_state = calloc(1, sizeof (adutils_query_state_t) + 972 (nqueries - 1) * sizeof (adutils_q_t)); 973 if (new_state == NULL) 974 return (ADUTILS_ERR_MEMORY); 975 976 new_state->ref_cnt = 1; 977 new_state->qadh = adh; 978 new_state->qsize = nqueries; 979 new_state->qadh_gen = adh->generation; 980 new_state->qcount = 0; 981 new_state->ldap_res_search_cb = ldap_res_search_cb; 982 new_state->ldap_res_search_argp = ldap_res_search_argp; 983 (void) pthread_cond_init(&new_state->cv, NULL); 984 985 (void) pthread_mutex_lock(&qstatelock); 986 new_state->next = qstatehead; 987 qstatehead = new_state; 988 (void) pthread_mutex_unlock(&qstatelock); 989 *state = new_state; 990 991 return (ADUTILS_SUCCESS); 992 } 993 994 /* 995 * Find the adutils_query_state_t to which a given LDAP result msgid on a 996 * given connection belongs. This routine increaments the reference count 997 * so that the object can not be freed. adutils_lookup_batch_unlock() 998 * must be called to decreament the reference count. 999 */ 1000 static 1001 int 1002 msgid2query(adutils_host_t *adh, int msgid, 1003 adutils_query_state_t **state, int *qid) 1004 { 1005 adutils_query_state_t *p; 1006 int i; 1007 int ret; 1008 1009 (void) pthread_mutex_lock(&qstatelock); 1010 for (p = qstatehead; p != NULL; p = p->next) { 1011 if (p->qadh != adh || adh->generation != p->qadh_gen) 1012 continue; 1013 for (i = 0; i < p->qcount; i++) { 1014 if ((p->queries[i]).msgid == msgid) { 1015 if (!p->qdead) { 1016 p->ref_cnt++; 1017 *state = p; 1018 *qid = i; 1019 ret = 1; 1020 } else 1021 ret = 0; 1022 (void) pthread_mutex_unlock(&qstatelock); 1023 return (ret); 1024 } 1025 } 1026 } 1027 (void) pthread_mutex_unlock(&qstatelock); 1028 return (0); 1029 } 1030 1031 static 1032 int 1033 check_for_binary_attrs(const char *attr) 1034 { 1035 int i; 1036 for (i = 0; binattrs[i].name != NULL; i++) { 1037 if (strcasecmp(binattrs[i].name, attr) == 0) 1038 return (i); 1039 } 1040 return (-1); 1041 } 1042 1043 static 1044 void 1045 free_entry(adutils_entry_t *entry) 1046 { 1047 int i, j; 1048 adutils_attr_t *ap; 1049 1050 if (entry == NULL) 1051 return; 1052 if (entry->attr_nvpairs == NULL) { 1053 free(entry); 1054 return; 1055 } 1056 for (i = 0; i < entry->num_nvpairs; i++) { 1057 ap = &entry->attr_nvpairs[i]; 1058 if (ap->attr_name == NULL) { 1059 ldap_value_free(ap->attr_values); 1060 continue; 1061 } 1062 if (check_for_binary_attrs(ap->attr_name) >= 0) { 1063 free(ap->attr_name); 1064 if (ap->attr_values == NULL) 1065 continue; 1066 for (j = 0; j < ap->num_values; j++) 1067 free(ap->attr_values[j]); 1068 free(ap->attr_values); 1069 } else if (strcasecmp(ap->attr_name, "dn") == 0) { 1070 free(ap->attr_name); 1071 ldap_memfree(ap->attr_values[0]); 1072 free(ap->attr_values); 1073 } else { 1074 free(ap->attr_name); 1075 ldap_value_free(ap->attr_values); 1076 } 1077 } 1078 free(entry->attr_nvpairs); 1079 free(entry); 1080 } 1081 1082 void 1083 adutils_freeresult(adutils_result_t **result) 1084 { 1085 adutils_entry_t *e, *next; 1086 1087 if (result == NULL || *result == NULL) 1088 return; 1089 if ((*result)->entries == NULL) { 1090 free(*result); 1091 *result = NULL; 1092 return; 1093 } 1094 for (e = (*result)->entries; e != NULL; e = next) { 1095 next = e->next; 1096 free_entry(e); 1097 } 1098 free(*result); 1099 *result = NULL; 1100 } 1101 1102 const adutils_entry_t * 1103 adutils_getfirstentry(adutils_result_t *result) 1104 { 1105 if (result != NULL) 1106 return (result->entries); 1107 return (NULL); 1108 } 1109 1110 1111 char ** 1112 adutils_getattr(const adutils_entry_t *entry, const char *attrname) 1113 { 1114 int i; 1115 adutils_attr_t *ap; 1116 1117 if (entry == NULL || entry->attr_nvpairs == NULL) 1118 return (NULL); 1119 for (i = 0; i < entry->num_nvpairs; i++) { 1120 ap = &entry->attr_nvpairs[i]; 1121 if (ap->attr_name != NULL && 1122 strcasecmp(ap->attr_name, attrname) == 0) 1123 return (ap->attr_values); 1124 } 1125 return (NULL); 1126 } 1127 1128 1129 /* 1130 * Queue LDAP result for the given query. 1131 * 1132 * Return values: 1133 * 0 success 1134 * -1 ignore result 1135 * -2 error 1136 */ 1137 static 1138 int 1139 make_entry(adutils_q_t *q, adutils_host_t *adh, LDAPMessage *search_res, 1140 adutils_entry_t **entry) 1141 { 1142 BerElement *ber = NULL; 1143 BerValue **bvalues = NULL; 1144 char **strvalues; 1145 char *attr = NULL, *dn = NULL, *domain = NULL; 1146 adutils_entry_t *ep; 1147 adutils_attr_t *ap; 1148 int i, j, b, ret = -2; 1149 1150 *entry = NULL; 1151 1152 /* Check that this is the domain that we were looking for */ 1153 if ((dn = ldap_get_dn(adh->ld, search_res)) == NULL) 1154 return (-2); 1155 if ((domain = adutils_dn2dns(dn)) == NULL) { 1156 ldap_memfree(dn); 1157 return (-2); 1158 } 1159 if (q->edomain != NULL) { 1160 if (!domain_eq(q->edomain, domain)) { 1161 ldap_memfree(dn); 1162 free(domain); 1163 return (-1); 1164 } 1165 } 1166 free(domain); 1167 1168 /* Allocate memory for the entry */ 1169 if ((ep = calloc(1, sizeof (*ep))) == NULL) 1170 goto out; 1171 1172 /* For 'dn' */ 1173 ep->num_nvpairs = 1; 1174 1175 /* Count the number of name-value pairs for this entry */ 1176 for (attr = ldap_first_attribute(adh->ld, search_res, &ber); 1177 attr != NULL; 1178 attr = ldap_next_attribute(adh->ld, search_res, ber)) { 1179 ep->num_nvpairs++; 1180 ldap_memfree(attr); 1181 } 1182 ber_free(ber, 0); 1183 ber = NULL; 1184 1185 /* Allocate array for the attribute name-value pairs */ 1186 ep->attr_nvpairs = calloc(ep->num_nvpairs, sizeof (*ep->attr_nvpairs)); 1187 if (ep->attr_nvpairs == NULL) { 1188 ep->num_nvpairs = 0; 1189 goto out; 1190 } 1191 1192 /* For dn */ 1193 ap = &ep->attr_nvpairs[0]; 1194 if ((ap->attr_name = strdup("dn")) == NULL) 1195 goto out; 1196 ap->num_values = 1; 1197 ap->attr_values = calloc(ap->num_values, sizeof (*ap->attr_values)); 1198 if (ap->attr_values == NULL) { 1199 ap->num_values = 0; 1200 goto out; 1201 } 1202 ap->attr_values[0] = dn; 1203 dn = NULL; 1204 1205 for (attr = ldap_first_attribute(adh->ld, search_res, &ber), i = 1; 1206 attr != NULL; 1207 ldap_memfree(attr), i++, 1208 attr = ldap_next_attribute(adh->ld, search_res, ber)) { 1209 ap = &ep->attr_nvpairs[i]; 1210 if ((ap->attr_name = strdup(attr)) == NULL) 1211 goto out; 1212 1213 if ((b = check_for_binary_attrs(attr)) >= 0) { 1214 bvalues = 1215 ldap_get_values_len(adh->ld, search_res, attr); 1216 if (bvalues == NULL) 1217 continue; 1218 ap->num_values = ldap_count_values_len(bvalues); 1219 if (ap->num_values == 0) { 1220 ldap_value_free_len(bvalues); 1221 bvalues = NULL; 1222 continue; 1223 } 1224 ap->attr_values = calloc(ap->num_values, 1225 sizeof (*ap->attr_values)); 1226 if (ap->attr_values == NULL) { 1227 ap->num_values = 0; 1228 goto out; 1229 } 1230 for (j = 0; j < ap->num_values; j++) { 1231 ap->attr_values[j] = 1232 binattrs[b].ber2str(bvalues[j]); 1233 if (ap->attr_values[j] == NULL) 1234 goto out; 1235 } 1236 ldap_value_free_len(bvalues); 1237 bvalues = NULL; 1238 continue; 1239 } 1240 1241 strvalues = ldap_get_values(adh->ld, search_res, attr); 1242 if (strvalues == NULL) 1243 continue; 1244 ap->num_values = ldap_count_values(strvalues); 1245 if (ap->num_values == 0) { 1246 ldap_value_free(strvalues); 1247 continue; 1248 } 1249 ap->attr_values = strvalues; 1250 } 1251 1252 ret = 0; 1253 out: 1254 ldap_memfree(attr); 1255 ldap_memfree(dn); 1256 ber_free(ber, 0); 1257 ldap_value_free_len(bvalues); 1258 if (ret < 0) 1259 free_entry(ep); 1260 else 1261 *entry = ep; 1262 return (ret); 1263 } 1264 1265 /* 1266 * Put the search result onto the given adutils_q_t. 1267 * Returns: 0 success 1268 * < 0 error 1269 */ 1270 static 1271 int 1272 add_entry(adutils_host_t *adh, adutils_q_t *q, LDAPMessage *search_res) 1273 { 1274 int ret = -1; 1275 adutils_entry_t *entry = NULL; 1276 adutils_result_t *res; 1277 1278 ret = make_entry(q, adh, search_res, &entry); 1279 if (ret < -1) { 1280 *q->rc = ADUTILS_ERR_MEMORY; 1281 goto out; 1282 } else if (ret == -1) { 1283 /* ignore result */ 1284 goto out; 1285 } 1286 if (*q->result == NULL) { 1287 res = calloc(1, sizeof (*res)); 1288 if (res == NULL) { 1289 *q->rc = ADUTILS_ERR_MEMORY; 1290 goto out; 1291 } 1292 res->num_entries = 1; 1293 res->entries = entry; 1294 *q->result = res; 1295 } else { 1296 res = *q->result; 1297 entry->next = res->entries; 1298 res->entries = entry; 1299 res->num_entries++; 1300 } 1301 *q->rc = ADUTILS_SUCCESS; 1302 entry = NULL; 1303 ret = 0; 1304 1305 out: 1306 free_entry(entry); 1307 return (ret); 1308 } 1309 1310 /* 1311 * Try to get a result; if there is one, find the corresponding 1312 * adutils_q_t and process the result. 1313 * 1314 * Returns: 0 success 1315 * -1 error 1316 */ 1317 static 1318 int 1319 get_adobject_batch(adutils_host_t *adh, struct timeval *timeout) 1320 { 1321 adutils_query_state_t *query_state; 1322 LDAPMessage *res = NULL; 1323 int rc, ret, msgid, qid; 1324 adutils_q_t *que; 1325 int num; 1326 1327 (void) pthread_mutex_lock(&adh->lock); 1328 if (adh->dead || adh->num_requests == 0) { 1329 ret = (adh->dead) ? -1 : -2; 1330 (void) pthread_mutex_unlock(&adh->lock); 1331 return (ret); 1332 } 1333 1334 /* Get one result */ 1335 rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res); 1336 if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) || 1337 rc < 0) 1338 adh->dead = 1; 1339 1340 if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0) 1341 adh->num_requests--; 1342 if (adh->dead) { 1343 num = adh->num_requests; 1344 (void) pthread_mutex_unlock(&adh->lock); 1345 logger(LOG_DEBUG, 1346 "AD ldap_result error - %d queued requests", num); 1347 return (-1); 1348 } 1349 1350 switch (rc) { 1351 case LDAP_RES_SEARCH_RESULT: 1352 msgid = ldap_msgid(res); 1353 if (msgid2query(adh, msgid, &query_state, &qid)) { 1354 if (query_state->ldap_res_search_cb != NULL) { 1355 /* 1356 * We use the caller-provided callback 1357 * to process the result. 1358 */ 1359 query_state->ldap_res_search_cb( 1360 adh->ld, &res, rc, qid, 1361 query_state->ldap_res_search_argp); 1362 (void) pthread_mutex_unlock(&adh->lock); 1363 } else { 1364 /* 1365 * No callback. We fallback to our 1366 * default behaviour. All the entries 1367 * gotten from this search have been 1368 * added to the result list during 1369 * LDAP_RES_SEARCH_ENTRY (see below). 1370 * Here we set the return status to 1371 * notfound if the result is still empty. 1372 */ 1373 (void) pthread_mutex_unlock(&adh->lock); 1374 que = &(query_state->queries[qid]); 1375 if (*que->result == NULL) 1376 *que->rc = ADUTILS_ERR_NOTFOUND; 1377 } 1378 atomic_dec_32(&query_state->qinflight); 1379 adutils_lookup_batch_unlock(&query_state); 1380 } else { 1381 num = adh->num_requests; 1382 (void) pthread_mutex_unlock(&adh->lock); 1383 logger(LOG_DEBUG, 1384 "AD cannot find message ID (%d) " 1385 "- %d queued requests", 1386 msgid, num); 1387 } 1388 (void) ldap_msgfree(res); 1389 ret = 0; 1390 break; 1391 1392 case LDAP_RES_SEARCH_ENTRY: 1393 msgid = ldap_msgid(res); 1394 if (msgid2query(adh, msgid, &query_state, &qid)) { 1395 if (query_state->ldap_res_search_cb != NULL) { 1396 /* 1397 * We use the caller-provided callback 1398 * to process the entry. 1399 */ 1400 query_state->ldap_res_search_cb( 1401 adh->ld, &res, rc, qid, 1402 query_state->ldap_res_search_argp); 1403 (void) pthread_mutex_unlock(&adh->lock); 1404 } else { 1405 /* 1406 * No callback. We fallback to our 1407 * default behaviour. This entry 1408 * will be added to the result list. 1409 */ 1410 que = &(query_state->queries[qid]); 1411 rc = add_entry(adh, que, res); 1412 (void) pthread_mutex_unlock(&adh->lock); 1413 if (rc < 0) { 1414 logger(LOG_DEBUG, 1415 "Failed to queue entry by " 1416 "message ID (%d) " 1417 "- %d queued requests", 1418 msgid, num); 1419 } 1420 } 1421 adutils_lookup_batch_unlock(&query_state); 1422 } else { 1423 num = adh->num_requests; 1424 (void) pthread_mutex_unlock(&adh->lock); 1425 logger(LOG_DEBUG, 1426 "AD cannot find message ID (%d) " 1427 "- %d queued requests", 1428 msgid, num); 1429 } 1430 (void) ldap_msgfree(res); 1431 ret = 0; 1432 break; 1433 1434 case LDAP_RES_SEARCH_REFERENCE: 1435 /* 1436 * We have no need for these at the moment. Eventually, 1437 * when we query things that we can't expect to find in 1438 * the Global Catalog then we'll need to learn to follow 1439 * references. 1440 */ 1441 (void) pthread_mutex_unlock(&adh->lock); 1442 (void) ldap_msgfree(res); 1443 ret = 0; 1444 break; 1445 1446 default: 1447 /* timeout or error; treat the same */ 1448 (void) pthread_mutex_unlock(&adh->lock); 1449 ret = -1; 1450 break; 1451 } 1452 1453 return (ret); 1454 } 1455 1456 /* 1457 * This routine decreament the reference count of the 1458 * adutils_query_state_t 1459 */ 1460 static void 1461 adutils_lookup_batch_unlock(adutils_query_state_t **state) 1462 { 1463 /* 1464 * Decrement reference count with qstatelock locked 1465 */ 1466 (void) pthread_mutex_lock(&qstatelock); 1467 (*state)->ref_cnt--; 1468 /* 1469 * If there are no references wakup the allocating thread 1470 */ 1471 if ((*state)->ref_cnt <= 1) 1472 (void) pthread_cond_signal(&(*state)->cv); 1473 (void) pthread_mutex_unlock(&qstatelock); 1474 *state = NULL; 1475 } 1476 1477 /* 1478 * This routine frees the adutils_query_state_t structure 1479 * If the reference count is greater than 1 it waits 1480 * for the other threads to finish using it. 1481 */ 1482 void 1483 adutils_lookup_batch_release(adutils_query_state_t **state) 1484 { 1485 adutils_query_state_t **p; 1486 int i; 1487 1488 if (state == NULL || *state == NULL) 1489 return; 1490 1491 /* 1492 * Set state to dead to stop further operations. 1493 * Wait for reference count with qstatelock locked 1494 * to get to one. 1495 */ 1496 (void) pthread_mutex_lock(&qstatelock); 1497 (*state)->qdead = 1; 1498 while ((*state)->ref_cnt > 1) { 1499 (void) pthread_cond_wait(&(*state)->cv, &qstatelock); 1500 } 1501 1502 /* Remove this state struct from the list of state structs */ 1503 for (p = &qstatehead; *p != NULL; p = &(*p)->next) { 1504 if (*p == (*state)) { 1505 *p = (*state)->next; 1506 break; 1507 } 1508 } 1509 (void) pthread_mutex_unlock(&qstatelock); 1510 (void) pthread_cond_destroy(&(*state)->cv); 1511 release_conn((*state)->qadh); 1512 1513 /* Clear results for queries that failed */ 1514 for (i = 0; i < (*state)->qcount; i++) { 1515 if (*(*state)->queries[i].rc != ADUTILS_SUCCESS) { 1516 adutils_freeresult((*state)->queries[i].result); 1517 } 1518 } 1519 free(*state); 1520 *state = NULL; 1521 } 1522 1523 1524 /* 1525 * This routine waits for other threads using the 1526 * adutils_query_state_t structure to finish. 1527 * If the reference count is greater than 1 it waits 1528 * for the other threads to finish using it. 1529 */ 1530 static 1531 void 1532 adutils_lookup_batch_wait(adutils_query_state_t *state) 1533 { 1534 /* 1535 * Set state to dead to stop further operation. 1536 * stating. 1537 * Wait for reference count to get to one 1538 * with qstatelock locked. 1539 */ 1540 (void) pthread_mutex_lock(&qstatelock); 1541 state->qdead = 1; 1542 while (state->ref_cnt > 1) { 1543 (void) pthread_cond_wait(&state->cv, &qstatelock); 1544 } 1545 (void) pthread_mutex_unlock(&qstatelock); 1546 } 1547 1548 /* 1549 * Process active queries in the AD lookup batch and then finalize the 1550 * result. 1551 */ 1552 adutils_rc 1553 adutils_lookup_batch_end(adutils_query_state_t **state) 1554 { 1555 int rc = LDAP_SUCCESS; 1556 adutils_rc ad_rc = ADUTILS_SUCCESS; 1557 struct timeval tv; 1558 1559 tv.tv_sec = ADUTILS_SEARCH_TIMEOUT; 1560 tv.tv_usec = 0; 1561 1562 /* Process results until done or until timeout, if given */ 1563 while ((*state)->qinflight > 0) { 1564 if ((rc = get_adobject_batch((*state)->qadh, 1565 &tv)) != 0) 1566 break; 1567 } 1568 (*state)->qdead = 1; 1569 /* Wait for other threads processing search result to finish */ 1570 adutils_lookup_batch_wait(*state); 1571 if (rc == -1 || (*state)->qinflight != 0) 1572 ad_rc = ADUTILS_ERR_RETRIABLE_NET_ERR; 1573 adutils_lookup_batch_release(state); 1574 return (ad_rc); 1575 } 1576 1577 /* 1578 * Send one prepared search, queue up msgid, process what results are 1579 * available 1580 */ 1581 adutils_rc 1582 adutils_lookup_batch_add(adutils_query_state_t *state, 1583 const char *filter, const char * const *attrs, const char *edomain, 1584 adutils_result_t **result, adutils_rc *rc) 1585 { 1586 adutils_rc retcode = ADUTILS_SUCCESS; 1587 int lrc, qid; 1588 int num; 1589 int dead; 1590 struct timeval tv; 1591 adutils_q_t *q; 1592 1593 qid = atomic_inc_32_nv(&state->qcount) - 1; 1594 q = &(state->queries[qid]); 1595 1596 assert(qid < state->qsize); 1597 1598 /* 1599 * Remember the expected domain so we can check the results 1600 * against it 1601 */ 1602 q->edomain = edomain; 1603 1604 /* Remember where to put the results */ 1605 q->result = result; 1606 q->rc = rc; 1607 1608 /* 1609 * Provide sane defaults for the results in case we never hear 1610 * back from the DS before closing the connection. 1611 */ 1612 *rc = ADUTILS_ERR_RETRIABLE_NET_ERR; 1613 if (result != NULL) 1614 *result = NULL; 1615 1616 /* Check the number of queued requests first */ 1617 tv.tv_sec = ADUTILS_SEARCH_TIMEOUT; 1618 tv.tv_usec = 0; 1619 while (!state->qadh->dead && 1620 state->qadh->num_requests > state->qadh->max_requests) { 1621 if (get_adobject_batch(state->qadh, &tv) != 0) 1622 break; 1623 } 1624 1625 /* Send this lookup, don't wait for a result here */ 1626 lrc = LDAP_SUCCESS; 1627 (void) pthread_mutex_lock(&state->qadh->lock); 1628 1629 if (!state->qadh->dead) { 1630 state->qadh->idletime = time(NULL); 1631 1632 lrc = ldap_search_ext(state->qadh->ld, 1633 state->qadh->owner->basedn, 1634 LDAP_SCOPE_SUBTREE, filter, (char **)attrs, 1635 0, NULL, NULL, NULL, -1, &q->msgid); 1636 1637 if (lrc == LDAP_SUCCESS) { 1638 state->qadh->num_requests++; 1639 } else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE || 1640 lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN || 1641 lrc == LDAP_UNWILLING_TO_PERFORM) { 1642 retcode = ADUTILS_ERR_RETRIABLE_NET_ERR; 1643 state->qadh->dead = 1; 1644 } else { 1645 retcode = ADUTILS_ERR_OTHER; 1646 state->qadh->dead = 1; 1647 } 1648 } 1649 dead = state->qadh->dead; 1650 num = state->qadh->num_requests; 1651 (void) pthread_mutex_unlock(&state->qadh->lock); 1652 1653 if (dead) { 1654 if (lrc != LDAP_SUCCESS) 1655 logger(LOG_DEBUG, 1656 "AD ldap_search_ext error (%s) " 1657 "- %d queued requests", 1658 ldap_err2string(lrc), num); 1659 return (retcode); 1660 } 1661 1662 atomic_inc_32(&state->qinflight); 1663 1664 /* 1665 * Reap as many requests as we can _without_ waiting to prevent 1666 * any possible TCP socket buffer starvation deadlocks. 1667 */ 1668 (void) memset(&tv, 0, sizeof (tv)); 1669 while (get_adobject_batch(state->qadh, &tv) == 0) 1670 ; 1671 1672 return (ADUTILS_SUCCESS); 1673 } 1674 1675 /* 1676 * Single AD lookup request implemented on top of the batch API. 1677 */ 1678 adutils_rc 1679 adutils_lookup(adutils_ad_t *ad, const char *filter, const char **attrs, 1680 const char *domain, adutils_result_t **result) 1681 { 1682 adutils_rc rc, brc; 1683 adutils_query_state_t *qs; 1684 1685 rc = adutils_lookup_batch_start(ad, 1, NULL, NULL, &qs); 1686 if (rc != ADUTILS_SUCCESS) 1687 return (rc); 1688 1689 rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc); 1690 if (rc != ADUTILS_SUCCESS) { 1691 adutils_lookup_batch_release(&qs); 1692 return (rc); 1693 } 1694 1695 rc = adutils_lookup_batch_end(&qs); 1696 if (rc != ADUTILS_SUCCESS) 1697 return (rc); 1698 return (brc); 1699 } 1700 1701 boolean_t 1702 domain_eq(const char *a, const char *b) 1703 { 1704 int err; 1705 1706 return (u8_strcmp(a, b, 0, U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) 1707 == 0 && err == 0); 1708 } 1709 1710 void 1711 adutils_set_debug(enum ad_debug item, int value) 1712 { 1713 if (item >= 0 && item <= AD_DEBUG_MAX) 1714 ad_debug[item] = value; 1715 } 1716