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