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