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