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