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