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