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