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