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