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