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