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