adutils.c revision 5232:a25c7db1e2c4
1279377Simp/* 2279377Simp * CDDL HEADER START 3279377Simp * 4279377Simp * The contents of this file are subject to the terms of the 5279377Simp * Common Development and Distribution License (the "License"). 6279377Simp * You may not use this file except in compliance with the License. 7279377Simp * 8279377Simp * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9279377Simp * or http://www.opensolaris.org/os/licensing. 10279377Simp * See the License for the specific language governing permissions 11279377Simp * and limitations under the License. 12279377Simp * 13279377Simp * When distributing Covered Code, include this CDDL HEADER in each 14279377Simp * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15279377Simp * If applicable, add the following below this CDDL HEADER, with the 16279377Simp * fields enclosed by brackets "[]" replaced with your own identifying 17279377Simp * information: Portions Copyright [yyyy] [name of copyright owner] 18279377Simp * 19279377Simp * CDDL HEADER END 20279377Simp */ 21279377Simp 22279377Simp/* 23279377Simp * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24279377Simp * Use is subject to license terms. 25279377Simp */ 26279377Simp 27279377Simp#pragma ident "%Z%%M% %I% %E% SMI" 28279377Simp 29279377Simp/* 30279377Simp * Processes name2sid & sid2name batched lookups for a given user or 31279377Simp * computer from an AD Directory server using GSSAPI authentication 32279377Simp */ 33279377Simp 34279377Simp#include <stdio.h> 35279377Simp#include <stdlib.h> 36279377Simp#include <alloca.h> 37279377Simp#include <string.h> 38279377Simp#include <strings.h> 39279377Simp#include <lber.h> 40279377Simp#include <ldap.h> 41279377Simp#include <sasl/sasl.h> 42279377Simp#include <string.h> 43279377Simp#include <ctype.h> 44279377Simp#include <pthread.h> 45279377Simp#include <synch.h> 46279377Simp#include <atomic.h> 47279377Simp#include <errno.h> 48279377Simp#include <assert.h> 49279377Simp#include <limits.h> 50279377Simp#include "idmapd.h" 51279377Simp 52279377Simp/* 53279377Simp * Internal data structures for this code 54279377Simp */ 55279377Simp 56279377Simp/* Attribute names and filter format strings */ 57279377Simp#define OBJECTSID "objectSid" 58279377Simp#define OBJECTSIDFILTER "(objectSid=%s)" 59279377Simp#define SAMACCOUNTNAME "sAMAccountName" 60279377Simp#define SANFILTER "(sAMAccountName=%.*s)" 61279377Simp#define OBJECTCLASS "objectClass" 62279377Simp 63279377Simp/* 64279377Simp * This should really be in some <sys/sid.h> file or so; we have a 65279377Simp * private version of sid_t, and so must other components of ON until we 66279377Simp * rationalize this. 67279377Simp */ 68279377Simptypedef struct sid { 69279377Simp uchar_t version; 70279377Simp uchar_t sub_authority_count; 71279377Simp uint64_t authority; /* really, 48-bits */ 72279377Simp rid_t sub_authorities[SID_MAX_SUB_AUTHORITIES]; 73279377Simp} sid_t; 74279377Simp 75279377Simp/* A single DS */ 76279377Simptypedef struct ad_host { 77279377Simp struct ad_host *next; 78279377Simp ad_t *owner; /* ad_t to which this belongs */ 79279377Simp pthread_mutex_t lock; 80279377Simp LDAP *ld; /* LDAP connection */ 81279377Simp uint32_t ref; /* ref count */ 82279377Simp time_t idletime; /* time since last activity */ 83279377Simp int dead; /* error on LDAP connection */ 84279377Simp /* 85279377Simp * Used to distinguish between different instances of LDAP 86279377Simp * connections to this same DS. We need this so we never mix up 87279377Simp * results for a given msgID from one connection with those of 88279377Simp * another earlier connection where two batch state structures 89279377Simp * share this ad_host object but used different LDAP connections 90279377Simp * to send their LDAP searches. 91279377Simp */ 92279377Simp uint64_t generation; 93279377Simp 94279377Simp /* LDAP DS info */ 95279377Simp char *host; 96279377Simp int port; 97279377Simp 98279377Simp /* hardwired to SASL GSSAPI only for now */ 99279377Simp char *saslmech; 100279377Simp unsigned saslflags; 101279377Simp} ad_host_t; 102279377Simp 103279377Simp/* A set of DSs for a given AD partition; ad_t typedef comes from adutil.h */ 104279377Simpstruct ad { 105279377Simp char *dflt_w2k_dom; /* used to qualify bare names */ 106279377Simp char *basedn; /* derived from dflt domain */ 107279377Simp pthread_mutex_t lock; 108279377Simp uint32_t ref; 109279377Simp idmap_ad_partition_t partition; /* Data or global catalog? */ 110279377Simp}; 111279377Simp 112279377Simp/* 113279377Simp * A place to put the results of a batched (async) query 114279377Simp * 115279377Simp * There is one of these for every query added to a batch object 116279377Simp * (idmap_query_state, see below). 117279377Simp */ 118279377Simptypedef struct idmap_q { 119279377Simp char **result; /* name or stringified SID */ 120279377Simp char **domain; /* name of domain of object */ 121279377Simp rid_t *rid; /* for n2s, if not NULL */ 122279377Simp int *sid_type; /* if not NULL */ 123279377Simp idmap_retcode *rc; 124279377Simp int msgid; /* LDAP message ID */ 125279377Simp /* 126279377Simp * Bitfield containing state needed to know when we're done 127279377Simp * processing search results related to this query's LDAP 128279377Simp * searches. Mostly self-explanatory. 129279377Simp */ 130279377Simp uint16_t n2s : 1; /* name->SID or SID->name? */ 131279377Simp uint16_t got_reply : 1; 132279377Simp uint16_t got_results : 1; 133279377Simp uint16_t got_objectSid : 1; 134279377Simp uint16_t got_objectClass : 1; 135279377Simp uint16_t got_samAcctName : 1; 136279377Simp} idmap_q_t; 137279377Simp 138279377Simp/* Batch context structure; typedef is in header file */ 139279377Simpstruct idmap_query_state { 140279377Simp idmap_query_state_t *next; 141279377Simp int qcount; /* how many queries */ 142279377Simp int ref_cnt; /* reference count */ 143279377Simp pthread_cond_t cv; /* Condition wait variable */ 144279377Simp uint32_t qlastsent; 145279377Simp uint32_t qinflight; /* how many queries in flight */ 146279377Simp uint16_t qdead; /* oops, lost LDAP connection */ 147279377Simp ad_host_t *qadh; /* LDAP connection */ 148279377Simp uint64_t qadh_gen; /* same as qadh->generation */ 149279377Simp idmap_q_t queries[1]; /* array of query results */ 150279377Simp}; 151279377Simp 152279377Simp/* 153279377Simp * List of query state structs -- needed so we can "route" LDAP results 154279377Simp * to the right context if multiple threads should be using the same 155279377Simp * connection concurrently 156279377Simp */ 157279377Simpstatic idmap_query_state_t *qstatehead = NULL; 158279377Simpstatic pthread_mutex_t qstatelock = PTHREAD_MUTEX_INITIALIZER; 159279377Simp 160279377Simp/* 161279377Simp * List of DSs, needed by the idle connection reaper thread 162279377Simp */ 163279377Simpstatic ad_host_t *host_head = NULL; 164279377Simpstatic pthread_t reaperid = 0; 165279377Simpstatic pthread_mutex_t adhostlock = PTHREAD_MUTEX_INITIALIZER; 166279377Simp 167279377Simp 168279377Simpstatic void 169279377Simpidmap_lookup_unlock_batch(idmap_query_state_t **state); 170279377Simp 171279377Simp 172279377Simp 173279377Simp/*ARGSUSED*/ 174279377Simpstatic int 175279377Simpidmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) { 176279377Simp sasl_interact_t *interact; 177279377Simp 178279377Simp if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE) 179279377Simp return (LDAP_PARAM_ERROR); 180279377Simp 181279377Simp /* There should be no extra arguemnts for SASL/GSSAPI authentication */ 182279377Simp for (interact = prompts; interact->id != SASL_CB_LIST_END; 183279377Simp interact++) { 184279377Simp interact->result = NULL; 185279377Simp interact->len = 0; 186279377Simp } 187279377Simp return (LDAP_SUCCESS); 188279377Simp} 189279377Simp 190279377Simp/* Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com" */ 191279377Simpstatic 192279377Simpchar * 193279377Simpdns2dn(const char *dns) 194279377Simp{ 195279377Simp int nameparts; 196279377Simp 197279377Simp /* Sigh, ldap_dns_to_dn()'s first arg should be a const char * */ 198279377Simp return (ldap_dns_to_dn((char *)dns, &nameparts)); 199279377Simp} 200279377Simp 201279377Simp/* 202279377Simp * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other 203279377Simp * attributes (CN, etc...) 204279377Simp */ 205279377Simpstatic 206279377Simpchar * 207279377Simpdn2dns(const char *dn) 208279377Simp{ 209279377Simp char **rdns = NULL; 210279377Simp char **attrs = NULL; 211279377Simp char **labels = NULL; 212279377Simp char *dns = NULL; 213279377Simp char **rdn, **attr, **label; 214279377Simp int maxlabels = 5; 215279377Simp int nlabels = 0; 216279377Simp int dnslen; 217279377Simp 218279377Simp /* 219279377Simp * There is no reverse of ldap_dns_to_dn() in our libldap, so we 220279377Simp * have to do the hard work here for now. 221279377Simp */ 222279377Simp 223279377Simp /* 224279377Simp * This code is much too liberal: it looks for "dc" attributes 225279377Simp * in all RDNs of the DN. In theory this could cause problems 226279377Simp * if people were to use "dc" in nodes other than the root of 227279377Simp * the tree, but in practice noone, least of all Active 228279377Simp * Directory, does that. 229279377Simp * 230279377Simp * On the other hand, this code is much too conservative: it 231279377Simp * does not make assumptions about ldap_explode_dn(), and _that_ 232279377Simp * is the true for looking at every attr of every RDN. 233279377Simp * 234279377Simp * Since we only ever look at dc and those must be DNS labels, 235279377Simp * at least until we get around to supporting IDN here we 236279377Simp * shouldn't see escaped labels from AD nor from libldap, though 237279377Simp * the spec (RFC2253) does allow libldap to escape things that 238279377Simp * don't need escaping -- if that should ever happen then 239279377Simp * libldap will need a spanking, and we can take care of that. 240279377Simp */ 241279377Simp 242279377Simp /* Explode a DN into RDNs */ 243279377Simp if ((rdns = ldap_explode_dn(dn, 0)) == NULL) 244279377Simp return (NULL); 245279377Simp 246279377Simp labels = calloc(maxlabels + 1, sizeof (char *)); 247279377Simp label = labels; 248279377Simp 249279377Simp for (rdn = rdns; *rdn != NULL; rdn++) { 250279377Simp if (attrs != NULL) 251279377Simp ldap_value_free(attrs); 252279377Simp 253279377Simp /* Explode each RDN, look for DC attr, save val as DNS label */ 254279377Simp if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL) 255279377Simp goto done; 256279377Simp 257279377Simp for (attr = attrs; *attr != NULL; attr++) { 258279377Simp if (strncasecmp(*attr, "dc=", 3) != 0) 259279377Simp continue; 260279377Simp 261279377Simp /* Found a DNS label */ 262279377Simp labels[nlabels++] = strdup((*attr) + 3); 263279377Simp 264279377Simp if (nlabels == maxlabels) { 265279377Simp char **tmp; 266279377Simp tmp = realloc(labels, 267279377Simp sizeof (char *) * (maxlabels + 1)); 268279377Simp 269279377Simp if (tmp == NULL) 270279377Simp goto done; 271279377Simp 272279377Simp labels = tmp; 273279377Simp labels[nlabels] = NULL; 274279377Simp } 275279377Simp 276279377Simp /* There should be just one DC= attr per-RDN */ 277279377Simp break; 278279377Simp } 279279377Simp } 280279377Simp 281279377Simp /* 282279377Simp * Got all the labels, now join with '.' 283279377Simp * 284279377Simp * We need room for nlabels - 1 periods ('.'), one nul 285279377Simp * terminator, and the strlen() of each label. 286279377Simp */ 287279377Simp dnslen = nlabels; 288279377Simp for (label = labels; *label != NULL; label++) 289279377Simp dnslen += strlen(*label); 290279377Simp 291279377Simp if ((dns = malloc(dnslen)) == NULL) 292279377Simp goto done; 293279377Simp 294279377Simp *dns = '\0'; 295279377Simp 296279377Simp for (label = labels; *label != NULL; label++) { 297279377Simp (void) strlcat(dns, *label, dnslen); 298279377Simp /* 299279377Simp * NOTE: the last '.' won't be appended -- there's no room 300279377Simp * for it! 301279377Simp */ 302279377Simp (void) strlcat(dns, ".", dnslen); 303279377Simp } 304279377Simp 305279377Simpdone: 306279377Simp if (labels != NULL) { 307279377Simp for (label = labels; *label != NULL; label++) 308279377Simp free(*label); 309279377Simp free(labels); 310279377Simp } 311279377Simp if (attrs != NULL) 312279377Simp ldap_value_free(attrs); 313279377Simp if (rdns != NULL) 314279377Simp ldap_value_free(rdns); 315279377Simp 316279377Simp return (dns); 317279377Simp} 318279377Simp 319279377Simp/* 320279377Simp * Keep connection management simple for now, extend or replace later 321279377Simp * with updated libsldap code. 322279377Simp */ 323279377Simp#define ADREAPERSLEEP 60 324279377Simp#define ADCONN_TIME 300 325279377Simp 326279377Simp/* 327279377Simp * Idle connection reaping side of connection management 328279377Simp * 329279377Simp * Every minute wake up and look for connections that have been idle for 330279377Simp * five minutes or more and close them. 331279377Simp */ 332279377Simp/*ARGSUSED*/ 333279377Simpstatic 334279377Simpvoid 335279377Simpadreaper(void *arg) 336279377Simp{ 337279377Simp ad_host_t *adh; 338279377Simp time_t now; 339279377Simp timespec_t ts; 340279377Simp 341279377Simp ts.tv_sec = ADREAPERSLEEP; 342279377Simp ts.tv_nsec = 0; 343279377Simp 344279377Simp for (;;) { 345279377Simp /* 346279377Simp * nanosleep(3RT) is thead-safe (no SIGALRM) and more 347279377Simp * portable than usleep(3C) 348279377Simp */ 349279377Simp (void) nanosleep(&ts, NULL); 350279377Simp (void) pthread_mutex_lock(&adhostlock); 351279377Simp now = time(NULL); 352279377Simp for (adh = host_head; adh != NULL; adh = adh->next) { 353279377Simp (void) pthread_mutex_lock(&adh->lock); 354279377Simp if (adh->ref == 0 && adh->idletime != 0 && 355279377Simp adh->idletime + ADCONN_TIME < now) { 356279377Simp if (adh->ld) { 357279377Simp (void) ldap_unbind(adh->ld); 358279377Simp adh->ld = NULL; 359279377Simp adh->idletime = 0; 360279377Simp adh->ref = 0; 361279377Simp } 362279377Simp } 363279377Simp (void) pthread_mutex_unlock(&adh->lock); 364279377Simp } 365279377Simp (void) pthread_mutex_unlock(&adhostlock); 366279377Simp } 367279377Simp} 368279377Simp 369279377Simpint 370279377Simpidmap_ad_alloc(ad_t **new_ad, const char *default_domain, 371279377Simp idmap_ad_partition_t part) 372279377Simp{ 373279377Simp ad_t *ad; 374279377Simp 375279377Simp *new_ad = NULL; 376279377Simp 377279377Simp if ((default_domain == NULL || *default_domain == '\0') && 378279377Simp part != IDMAP_AD_GLOBAL_CATALOG) 379279377Simp return (-1); 380279377Simp 381279377Simp if ((ad = calloc(1, sizeof (ad_t))) == NULL) 382279377Simp return (-1); 383279377Simp 384279377Simp ad->ref = 1; 385279377Simp ad->partition = part; 386279377Simp 387279377Simp /* 388279377Simp * If default_domain is NULL, deal, deferring errors until 389279377Simp * idmap_lookup_batch_start() -- this makes it easier on the 390279377Simp * caller, who can simply observe lookups failing as opposed to 391279377Simp * having to conditionalize calls to lookups according to 392279377Simp * whether it has a non-NULL ad_t *. 393279377Simp */ 394279377Simp if (default_domain == NULL) 395279377Simp default_domain = ""; 396279377Simp 397279377Simp if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL) 398279377Simp goto err; 399279377Simp 400279377Simp /* If default_domain is empty, deal; see above */ 401279377Simp if (*default_domain == '\0') { 402279377Simp if ((ad->basedn = strdup("")) == NULL) 403279377Simp goto err; 404279377Simp } else if ((ad->basedn = dns2dn(default_domain)) == NULL) { 405279377Simp goto err; 406279377Simp } 407279377Simp 408279377Simp if (pthread_mutex_init(&ad->lock, NULL) != 0) 409279377Simp goto err; 410279377Simp 411279377Simp *new_ad = ad; 412279377Simp 413279377Simp return (0); 414279377Simperr: 415279377Simp if (ad->dflt_w2k_dom != NULL) 416279377Simp free(ad->dflt_w2k_dom); 417279377Simp if (ad->basedn != NULL) 418279377Simp free(ad->basedn); 419279377Simp free(ad); 420279377Simp return (-1); 421279377Simp} 422279377Simp 423279377Simp 424279377Simpvoid 425279377Simpidmap_ad_free(ad_t **ad) 426279377Simp{ 427279377Simp ad_host_t *p; 428279377Simp 429279377Simp if (ad == NULL || *ad == NULL) 430279377Simp return; 431279377Simp 432279377Simp (void) pthread_mutex_lock(&(*ad)->lock); 433279377Simp 434279377Simp if (atomic_dec_32_nv(&(*ad)->ref) > 0) { 435279377Simp (void) pthread_mutex_unlock(&(*ad)->lock); 436279377Simp *ad = NULL; 437279377Simp return; 438279377Simp } 439279377Simp 440279377Simp for (p = host_head; p != NULL; p = p->next) { 441279377Simp if (p->owner != (*ad)) 442279377Simp continue; 443279377Simp idmap_delete_ds((*ad), p->host, p->port); 444279377Simp } 445279377Simp 446279377Simp free((*ad)->basedn); 447279377Simp 448279377Simp (void) pthread_mutex_unlock(&(*ad)->lock); 449279377Simp (void) pthread_mutex_destroy(&(*ad)->lock); 450279377Simp 451279377Simp free(*ad); 452279377Simp 453279377Simp *ad = NULL; 454279377Simp} 455279377Simp 456279377Simpstatic 457279377Simpint 458279377Simpidmap_open_conn(ad_host_t *adh) 459279377Simp{ 460279377Simp int rc, ldversion; 461279377Simp 462279377Simp if (adh->dead && adh->ld != NULL) { 463279377Simp (void) ldap_unbind(adh->ld); 464279377Simp adh->ld = NULL; 465279377Simp adh->dead = 0; 466279377Simp } 467279377Simp 468279377Simp if (adh->ld == NULL) { 469279377Simp int zero = 0; 470279377Simp int timeoutms = 30 * 1000; 471279377Simp 472279377Simp atomic_inc_64(&adh->generation); 473279377Simp adh->ld = ldap_init(adh->host, adh->port); 474279377Simp if (adh->ld == NULL) 475279377Simp return (-1); 476279377Simp 477279377Simp ldversion = LDAP_VERSION3; 478279377Simp (void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, 479279377Simp &ldversion); 480279377Simp 481279377Simp (void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, 482279377Simp LDAP_OPT_OFF); 483279377Simp (void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero); 484279377Simp (void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero); 485279377Simp /* setup TCP/IP connect timeout */ 486279377Simp (void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, 487279377Simp &timeoutms); 488279377Simp (void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON); 489279377Simp rc = ldap_sasl_interactive_bind_s(adh->ld, 490279377Simp "" /* binddn */, adh->saslmech, NULL, NULL, adh->saslflags, 491279377Simp &idmap_saslcallback, NULL /* defaults */); 492279377Simp 493279377Simp if (rc != LDAP_SUCCESS) { 494279377Simp idmapdlog(LOG_ERR, "ldap_sasl_interactive_bind_s() " 495279377Simp "to server %s:%d failed. (%s)", 496279377Simp adh->host, adh->port, ldap_err2string(rc)); 497279377Simp return (rc); 498279377Simp } 499279377Simp } 500279377Simp 501279377Simp adh->idletime = time(NULL); 502279377Simp 503279377Simp return (LDAP_SUCCESS); 504279377Simp} 505279377Simp 506279377Simp 507279377Simp/* 508279377Simp * Connection management: find an open connection or open one 509279377Simp */ 510279377Simpstatic 511279377Simpad_host_t * 512279377Simpidmap_get_conn(const ad_t *ad) 513279377Simp{ 514279377Simp ad_host_t *adh = NULL; 515279377Simp int rc; 516279377Simp 517279377Simp (void) pthread_mutex_lock(&adhostlock); 518279377Simp 519279377Simp /* 520279377Simp * Search for any ad_host_t, preferably one with an open 521279377Simp * connection 522279377Simp */ 523279377Simp for (adh = host_head; adh != NULL; adh = adh->next) { 524279377Simp if (adh->owner == ad) { 525279377Simp break; 526279377Simp } 527279377Simp } 528279377Simp 529279377Simp if (adh != NULL) 530279377Simp atomic_inc_32(&adh->ref); 531279377Simp 532279377Simp (void) pthread_mutex_unlock(&adhostlock); 533279377Simp 534279377Simp if (adh == NULL) 535279377Simp return (NULL); 536279377Simp 537279377Simp /* found connection, open it if not opened */ 538279377Simp (void) pthread_mutex_lock(&adh->lock); 539279377Simp rc = idmap_open_conn(adh); 540279377Simp (void) pthread_mutex_unlock(&adh->lock); 541279377Simp if (rc != LDAP_SUCCESS) 542279377Simp return (NULL); 543279377Simp 544279377Simp return (adh); 545279377Simp} 546279377Simp 547279377Simpstatic 548279377Simpvoid 549279377Simpidmap_release_conn(ad_host_t *adh) 550279377Simp{ 551279377Simp (void) pthread_mutex_lock(&adh->lock); 552279377Simp if (atomic_dec_32_nv(&adh->ref) == 0) 553279377Simp adh->idletime = time(NULL); 554279377Simp (void) pthread_mutex_unlock(&adh->lock); 555279377Simp} 556279377Simp 557279377Simp/* 558279377Simp * Take ad_host_config_t information, create a ad_host_t, 559279377Simp * populate it and add it to the list of hosts. 560279377Simp */ 561279377Simp 562279377Simpint 563279377Simpidmap_add_ds(ad_t *ad, const char *host, int port) 564279377Simp{ 565279377Simp ad_host_t *p; 566279377Simp ad_host_t *new = NULL; 567279377Simp int ret = -1; 568279377Simp 569279377Simp if (port == 0) 570279377Simp port = (int)ad->partition; 571279377Simp 572279377Simp (void) pthread_mutex_lock(&adhostlock); 573279377Simp for (p = host_head; p != NULL; p = p->next) { 574279377Simp if (p->owner != ad) 575279377Simp continue; 576279377Simp 577279377Simp if (strcmp(host, p->host) == 0 && p->port == port) { 578279377Simp /* already added */ 579279377Simp ret = -2; 580279377Simp goto err; 581279377Simp } 582279377Simp } 583279377Simp 584279377Simp /* add new entry */ 585279377Simp new = (ad_host_t *)calloc(1, sizeof (ad_host_t)); 586279377Simp if (new == NULL) 587279377Simp goto err; 588279377Simp new->owner = ad; 589279377Simp new->port = port; 590279377Simp new->dead = 0; 591279377Simp if ((new->host = strdup(host)) == NULL) 592279377Simp goto err; 593279377Simp 594279377Simp /* default to SASL GSSAPI only for now */ 595279377Simp new->saslflags = LDAP_SASL_INTERACTIVE; 596279377Simp new->saslmech = "GSSAPI"; 597279377Simp 598279377Simp if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) { 599279377Simp free(new->host); 600279377Simp new->host = NULL; 601279377Simp errno = ret; 602279377Simp ret = -1; 603279377Simp goto err; 604279377Simp } 605279377Simp 606279377Simp /* link in */ 607279377Simp new->next = host_head; 608279377Simp host_head = new; 609279377Simp 610279377Simp /* Start reaper if it doesn't exist */ 611279377Simp if (reaperid == 0) 612279377Simp (void) pthread_create(&reaperid, NULL, 613279377Simp (void *(*)(void *))adreaper, (void *)NULL); 614279377Simp 615279377Simperr: 616279377Simp (void) pthread_mutex_unlock(&adhostlock); 617279377Simp 618279377Simp if (ret != 0 && new != NULL) { 619279377Simp if (new->host != NULL) { 620279377Simp (void) pthread_mutex_destroy(&new->lock); 621279377Simp free(new->host); 622279377Simp } 623279377Simp free(new); 624279377Simp } 625279377Simp 626279377Simp return (ret); 627279377Simp} 628279377Simp 629279377Simp/* 630279377Simp * free a DS configuration 631279377Simp */ 632279377Simpvoid 633279377Simpidmap_delete_ds(ad_t *ad, const char *host, int port) 634279377Simp{ 635279377Simp ad_host_t **p, *q; 636279377Simp 637279377Simp (void) pthread_mutex_lock(&adhostlock); 638279377Simp for (p = &host_head; *p != NULL; p = &((*p)->next)) { 639279377Simp if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 || 640279377Simp (*p)->port != port) 641279377Simp continue; 642279377Simp /* found */ 643279377Simp if (atomic_dec_32_nv(&((*p)->ref)) > 0) 644279377Simp break; /* still in use */ 645279377Simp 646279377Simp q = *p; 647279377Simp *p = (*p)->next; 648279377Simp 649279377Simp (void) pthread_mutex_destroy(&q->lock); 650279377Simp 651279377Simp if (q->ld) 652279377Simp (void) ldap_unbind(q->ld); 653279377Simp if (q->host) 654279377Simp free(q->host); 655279377Simp free(q); 656279377Simp break; 657279377Simp } 658279377Simp (void) pthread_mutex_unlock(&adhostlock); 659279377Simp} 660279377Simp 661279377Simp/* 662279377Simp * Convert a binary SID in a BerValue to a sid_t 663279377Simp */ 664279377Simpstatic 665279377Simpint 666279377Simpidmap_getsid(BerValue *bval, sid_t *sidp) 667279377Simp{ 668279377Simp int i, j; 669279377Simp uchar_t *v; 670279377Simp uint32_t a; 671279377Simp 672279377Simp /* 673279377Simp * The binary format of a SID is as follows: 674279377Simp * 675279377Simp * byte #0: version, always 0x01 676279377Simp * byte #1: RID count, always <= 0x0f 677279377Simp * bytes #2-#7: SID authority, big-endian 48-bit unsigned int 678279377Simp * 679279377Simp * followed by RID count RIDs, each a little-endian, unsigned 680279377Simp * 32-bit int. 681279377Simp */ 682279377Simp /* 683279377Simp * Sanity checks: must have at least one RID, version must be 684279377Simp * 0x01, and the length must be 8 + rid count * 4 685279377Simp */ 686279377Simp if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 && 687279377Simp bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) { 688279377Simp v = (uchar_t *)bval->bv_val; 689279377Simp sidp->version = v[0]; 690279377Simp sidp->sub_authority_count = v[1]; 691279377Simp sidp->authority = 692279377Simp /* big endian -- so start from the left */ 693279377Simp ((u_longlong_t)v[2] << 40) | 694279377Simp ((u_longlong_t)v[3] << 32) | 695279377Simp ((u_longlong_t)v[4] << 24) | 696279377Simp ((u_longlong_t)v[5] << 16) | 697279377Simp ((u_longlong_t)v[6] << 8) | 698279377Simp (u_longlong_t)v[7]; 699279377Simp for (i = 0; i < sidp->sub_authority_count; i++) { 700279377Simp j = 8 + (i * 4); 701279377Simp /* little endian -- so start from the right */ 702279377Simp a = (v[j + 3] << 24) | (v[j + 2] << 16) | 703279377Simp (v[j + 1] << 8) | (v[j]); 704279377Simp sidp->sub_authorities[i] = a; 705279377Simp } 706279377Simp return (0); 707279377Simp } 708279377Simp return (-1); 709279377Simp} 710279377Simp 711279377Simp/* 712279377Simp * Convert a sid_t to S-1-... 713279377Simp */ 714279377Simpstatic 715279377Simpchar * 716279377Simpidmap_sid2txt(sid_t *sidp) 717279377Simp{ 718279377Simp int rlen, i, len; 719279377Simp char *str, *cp; 720279377Simp 721279377Simp if (sidp->version != 1) 722279377Simp return (NULL); 723279377Simp 724279377Simp len = sizeof ("S-1-") - 1; 725279377Simp 726279377Simp /* 727279377Simp * We could optimize like so, but, why? 728279377Simp * if (sidp->authority < 10) 729279377Simp * len += 2; 730279377Simp * else if (sidp->authority < 100) 731279377Simp * len += 3; 732279377Simp * else 733279377Simp * len += snprintf(NULL, 0"%llu", sidp->authority); 734279377Simp */ 735279377Simp len += snprintf(NULL, 0, "%llu", sidp->authority); 736279377Simp 737279377Simp /* Max length of a uint32_t printed out in ASCII is 10 bytes */ 738279377Simp len += 1 + (sidp->sub_authority_count + 1) * 10; 739279377Simp 740279377Simp if ((cp = str = malloc(len)) == NULL) 741279377Simp return (NULL); 742279377Simp 743279377Simp rlen = snprintf(str, len, "S-1-%llu", sidp->authority); 744279377Simp 745279377Simp cp += rlen; 746279377Simp len -= rlen; 747279377Simp 748279377Simp for (i = 0; i < sidp->sub_authority_count; i++) { 749279377Simp assert(len > 0); 750279377Simp rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]); 751279377Simp cp += rlen; 752279377Simp len -= rlen; 753279377Simp assert(len >= 0); 754279377Simp } 755279377Simp 756279377Simp return (str); 757279377Simp} 758279377Simp 759279377Simp/* 760279377Simp * Convert a sid_t to on-the-wire encoding 761279377Simp */ 762279377Simpstatic 763279377Simpint 764279377Simpidmap_sid2binsid(sid_t *sid, uchar_t *binsid, int binsidlen) 765279377Simp{ 766279377Simp uchar_t *p; 767279377Simp int i; 768279377Simp uint64_t a; 769279377Simp uint32_t r; 770279377Simp 771279377Simp if (sid->version != 1 || 772279377Simp binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4)) 773279377Simp return (-1); 774279377Simp 775279377Simp p = binsid; 776279377Simp *p++ = 0x01; /* version */ 777279377Simp /* sub authority count */ 778279377Simp *p++ = sid->sub_authority_count; 779279377Simp /* Authority */ 780279377Simp a = sid->authority; 781279377Simp /* big-endian -- start from left */ 782279377Simp *p++ = (a >> 40) & 0xFF; 783279377Simp *p++ = (a >> 32) & 0xFF; 784279377Simp *p++ = (a >> 24) & 0xFF; 785279377Simp *p++ = (a >> 16) & 0xFF; 786279377Simp *p++ = (a >> 8) & 0xFF; 787279377Simp *p++ = a & 0xFF; 788279377Simp 789279377Simp /* sub-authorities */ 790279377Simp for (i = 0; i < sid->sub_authority_count; i++) { 791279377Simp r = sid->sub_authorities[i]; 792279377Simp /* little-endian -- start from right */ 793279377Simp *p++ = (r & 0x000000FF); 794279377Simp *p++ = (r & 0x0000FF00) >> 8; 795279377Simp *p++ = (r & 0x00FF0000) >> 16; 796279377Simp *p++ = (r & 0xFF000000) >> 24; 797279377Simp } 798279377Simp 799279377Simp return (0); 800279377Simp} 801279377Simp 802279377Simp/* 803279377Simp * Convert a stringified SID (S-1-...) into a hex-encoded version of the 804279377Simp * on-the-wire encoding, but with each pair of hex digits pre-pended 805279377Simp * with a '\', so we can pass this to libldap. 806279377Simp */ 807279377Simpstatic 808279377Simpint 809279377Simpidmap_txtsid2hexbinsid(const char *txt, const rid_t *rid, 810279377Simp char *hexbinsid, int hexbinsidlen) 811279377Simp{ 812279377Simp sid_t sid = { 0 }; 813279377Simp int i, j; 814279377Simp const char *cp; 815279377Simp char *ecp; 816279377Simp u_longlong_t a; 817279377Simp unsigned long r; 818279377Simp uchar_t *binsid, b, hb; 819279377Simp 820279377Simp /* Only version 1 SIDs please */ 821279377Simp if (strncmp(txt, "S-1-", strlen("S-1-")) != 0) 822279377Simp return (-1); 823279377Simp 824279377Simp if (strlen(txt) < (strlen("S-1-") + 1)) 825279377Simp return (-1); 826279377Simp 827279377Simp /* count '-'s */ 828279377Simp for (j = 0, cp = strchr(txt, '-'); 829279377Simp cp != NULL && *cp != '\0'; 830279377Simp j++, cp = strchr(cp + 1, '-')) { 831279377Simp /* can't end on a '-' */ 832279377Simp if (*(cp + 1) == '\0') 833279377Simp return (-1); 834279377Simp } 835279377Simp 836279377Simp /* Adjust count for version and authority */ 837279377Simp j -= 2; 838279377Simp 839279377Simp /* we know the version number and RID count */ 840279377Simp sid.version = 1; 841279377Simp sid.sub_authority_count = (rid != NULL) ? j + 1 : j; 842279377Simp 843279377Simp /* must have at least one RID, but not too many */ 844279377Simp if (sid.sub_authority_count < 1 || 845279377Simp sid.sub_authority_count > SID_MAX_SUB_AUTHORITIES) 846279377Simp return (-1); 847279377Simp 848279377Simp /* check that we only have digits and '-' */ 849279377Simp if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1)) 850279377Simp return (-1); 851279377Simp 852279377Simp cp = txt + strlen("S-1-"); 853279377Simp 854279377Simp /* 64-bit safe parsing of unsigned 48-bit authority value */ 855279377Simp errno = 0; 856279377Simp a = strtoull(cp, &ecp, 10); 857279377Simp 858279377Simp /* errors parsing the authority or too many bits */ 859279377Simp if (cp == ecp || (a == 0 && errno == EINVAL) || 860279377Simp (a == ULLONG_MAX && errno == ERANGE) || 861279377Simp (a & 0x0000ffffffffffffULL) != a) 862279377Simp return (-1); 863279377Simp 864279377Simp cp = ecp; 865279377Simp 866279377Simp sid.authority = (uint64_t)a; 867279377Simp 868279377Simp for (i = 0; i < j; i++) { 869279377Simp if (*cp++ != '-') 870279377Simp return (-1); 871279377Simp /* 64-bit safe parsing of unsigned 32-bit RID */ 872279377Simp errno = 0; 873279377Simp r = strtoul(cp, &ecp, 10); 874279377Simp /* errors parsing the RID or too many bits */ 875279377Simp if (cp == ecp || (r == 0 && errno == EINVAL) || 876279377Simp (r == ULONG_MAX && errno == ERANGE) || 877279377Simp (r & 0xffffffffUL) != r) 878279377Simp return (-1); 879279377Simp sid.sub_authorities[i] = (uint32_t)r; 880279377Simp cp = ecp; 881279377Simp } 882279377Simp 883279377Simp /* check that all of the string SID has been consumed */ 884279377Simp if (*cp != '\0') 885279377Simp return (-1); 886279377Simp 887279377Simp if (rid != NULL) 888279377Simp sid.sub_authorities[j] = *rid; 889279377Simp 890279377Simp j = 1 + 1 + 6 + sid.sub_authority_count * 4; 891279377Simp 892279377Simp if (hexbinsidlen < (j * 3)) 893279377Simp return (-2); 894279377Simp 895279377Simp /* binary encode the SID */ 896279377Simp binsid = (uchar_t *)alloca(j); 897279377Simp (void) idmap_sid2binsid(&sid, binsid, j); 898279377Simp 899279377Simp /* hex encode, with a backslash before each byte */ 900279377Simp for (ecp = hexbinsid, i = 0; i < j; i++) { 901279377Simp b = binsid[i]; 902279377Simp *ecp++ = '\\'; 903279377Simp hb = (b >> 4) & 0xF; 904279377Simp *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); 905279377Simp hb = b & 0xF; 906279377Simp *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); 907279377Simp } 908279377Simp *ecp = '\0'; 909279377Simp 910279377Simp return (0); 911279377Simp} 912279377Simp 913279377Simpstatic 914279377Simpchar * 915279377Simpconvert_bval2sid(BerValue *bval, rid_t *rid) 916279377Simp{ 917279377Simp sid_t sid; 918279377Simp 919279377Simp if (idmap_getsid(bval, &sid) < 0) 920279377Simp return (NULL); 921279377Simp 922279377Simp /* 923279377Simp * If desired and if the SID is what should be a domain/computer 924279377Simp * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then 925279377Simp * save the last RID and truncate the SID 926279377Simp */ 927279377Simp if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5) 928279377Simp *rid = sid.sub_authorities[--sid.sub_authority_count]; 929279377Simp return (idmap_sid2txt(&sid)); 930279377Simp} 931279377Simp 932279377Simp 933279377Simpidmap_retcode 934279377Simpidmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state) 935279377Simp{ 936279377Simp idmap_query_state_t *new_state; 937279377Simp ad_host_t *adh = NULL; 938279377Simp 939279377Simp *state = NULL; 940279377Simp 941279377Simp if (*ad->dflt_w2k_dom == '\0') 942279377Simp return (-1); 943279377Simp 944279377Simp adh = idmap_get_conn(ad); 945279377Simp if (adh == NULL) 946279377Simp return (IDMAP_ERR_OTHER); 947279377Simp 948279377Simp new_state = calloc(1, sizeof (idmap_query_state_t) + 949279377Simp (nqueries - 1) * sizeof (idmap_q_t)); 950279377Simp 951279377Simp if (new_state == NULL) 952279377Simp return (IDMAP_ERR_MEMORY); 953279377Simp 954279377Simp new_state->ref_cnt = 1; 955279377Simp new_state->qadh = adh; 956279377Simp new_state->qcount = nqueries; 957279377Simp new_state->qadh_gen = adh->generation; 958279377Simp /* should be -1, but the atomic routines want unsigned */ 959279377Simp new_state->qlastsent = 0; 960279377Simp (void) pthread_cond_init(&new_state->cv, NULL); 961279377Simp 962279377Simp (void) pthread_mutex_lock(&qstatelock); 963279377Simp new_state->next = qstatehead; 964279377Simp qstatehead = new_state; 965279377Simp (void) pthread_mutex_unlock(&qstatelock); 966279377Simp 967279377Simp *state = new_state; 968279377Simp 969279377Simp return (IDMAP_SUCCESS); 970279377Simp} 971279377Simp 972279377Simp/* 973279377Simp * Find the idmap_query_state_t to which a given LDAP result msgid on a 974279377Simp * given connection belongs. This routine increaments the reference count 975279377Simp * so that the object can not be freed. idmap_lookup_unlock_batch() 976279377Simp * must be called to decreament the reference count. 977279377Simp */ 978279377Simpstatic 979279377Simpint 980279377Simpidmap_msgid2query(ad_host_t *adh, int msgid, 981279377Simp idmap_query_state_t **state, int *qid) 982279377Simp{ 983279377Simp idmap_query_state_t *p; 984279377Simp int i; 985279377Simp 986279377Simp (void) pthread_mutex_lock(&qstatelock); 987279377Simp for (p = qstatehead; p != NULL; p = p->next) { 988279377Simp if (p->qadh != adh || adh->generation != p->qadh_gen) 989279377Simp continue; 990279377Simp for (i = 0; i < p->qcount; i++) { 991279377Simp if ((p->queries[i]).msgid == msgid) { 992279377Simp p->ref_cnt++; 993279377Simp *state = p; 994279377Simp *qid = i; 995279377Simp (void) pthread_mutex_unlock(&qstatelock); 996279377Simp return (1); 997279377Simp } 998279377Simp } 999279377Simp } 1000279377Simp (void) pthread_mutex_unlock(&qstatelock); 1001279377Simp return (0); 1002279377Simp} 1003279377Simp 1004279377Simp/* 1005279377Simp * Handle an objectSid attr from a result 1006279377Simp */ 1007279377Simpstatic 1008279377Simpvoid 1009279377Simpidmap_bv_objsid2sidstr(BerValue **bvalues, idmap_q_t *q) 1010279377Simp{ 1011279377Simp if (bvalues == NULL) 1012279377Simp return; 1013279377Simp /* objectSid is single valued */ 1014279377Simp *(q->result) = convert_bval2sid(bvalues[0], q->rid); 1015279377Simp q->got_objectSid = 1; 1016279377Simp} 1017279377Simp 1018279377Simp/* 1019279377Simp * Handle a sAMAccountName attr from a result 1020279377Simp */ 1021279377Simpstatic 1022279377Simpvoid 1023279377Simpidmap_bv_samaccountname2name(BerValue **bvalues, idmap_q_t *q, const char *dn) 1024279377Simp{ 1025279377Simp char *result, *domain; 1026279377Simp int len; 1027279377Simp 1028279377Simp if (bvalues == NULL) 1029279377Simp return; 1030279377Simp 1031279377Simp if ((domain = dn2dns(dn)) == NULL) 1032279377Simp return; 1033279377Simp 1034279377Simp if (bvalues == NULL || bvalues[0] == NULL || 1035279377Simp bvalues[0]->bv_val == NULL) 1036279377Simp return; 1037279377Simp 1038279377Simp len = bvalues[0]->bv_len + 1; 1039279377Simp 1040279377Simp if (q->domain != NULL) 1041279377Simp *(q->domain) = domain; 1042279377Simp else 1043279377Simp len += strlen(domain) + 1; 1044279377Simp 1045279377Simp if ((result = malloc(len)) == NULL) { 1046279377Simp if (q->domain != NULL) 1047279377Simp *(q->domain) = NULL; 1048279377Simp free(domain); 1049279377Simp return; 1050279377Simp } 1051279377Simp 1052279377Simp (void) memcpy(result, bvalues[0]->bv_val, (size_t)bvalues[0]->bv_len); 1053279377Simp result[bvalues[0]->bv_len] = '\0'; 1054279377Simp 1055279377Simp if (q->domain == NULL) { 1056279377Simp (void) strlcat(result, "@", len); 1057279377Simp (void) strlcat(result, domain, len); 1058279377Simp free(domain); 1059279377Simp } 1060279377Simp 1061279377Simp *(q->result) = result; 1062279377Simp q->got_samAcctName = 1; 1063279377Simp} 1064279377Simp 1065279377Simp 1066279377Simp#define BVAL_CASEEQ(bv, str) \ 1067279377Simp (((*(bv))->bv_len == (sizeof (str) - 1)) && \ 1068279377Simp strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0) 1069279377Simp 1070279377Simp/* 1071279377Simp * Handle an objectClass attr from a result 1072279377Simp */ 1073279377Simpstatic 1074279377Simpvoid 1075279377Simpidmap_bv_objclass2sidtype(BerValue **bvalues, idmap_q_t *q) 1076279377Simp{ 1077279377Simp BerValue **cbval; 1078279377Simp 1079279377Simp if (bvalues == NULL) 1080279377Simp return; 1081279377Simp 1082279377Simp for (cbval = bvalues; *cbval != NULL; cbval++) { 1083279377Simp /* don't clobber sid_type */ 1084279377Simp if (*(q->sid_type) == _IDMAP_T_COMPUTER || 1085279377Simp *(q->sid_type) == _IDMAP_T_GROUP || 1086279377Simp *(q->sid_type) == _IDMAP_T_USER) 1087279377Simp continue; 1088279377Simp 1089279377Simp if (BVAL_CASEEQ(cbval, "Computer")) { 1090279377Simp *(q->sid_type) = _IDMAP_T_COMPUTER; 1091279377Simp return; 1092279377Simp } else if (BVAL_CASEEQ(cbval, "Group")) { 1093279377Simp *(q->sid_type) = _IDMAP_T_GROUP; 1094279377Simp } else if (BVAL_CASEEQ(cbval, "USER")) { 1095279377Simp *(q->sid_type) = _IDMAP_T_USER; 1096279377Simp } else 1097279377Simp *(q->sid_type) = _IDMAP_T_OTHER; 1098279377Simp q->got_objectClass = 1; 1099279377Simp } 1100279377Simp} 1101279377Simp 1102279377Simp/* 1103279377Simp * Handle a given search result entry 1104279377Simp */ 1105279377Simpstatic 1106279377Simpvoid 1107279377Simpidmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) 1108279377Simp{ 1109279377Simp char *dn, *attr; 1110279377Simp BerElement *ber = NULL; 1111279377Simp BerValue **bvalues; 1112279377Simp ad_host_t *adh; 1113279377Simp idmap_q_t *q; 1114279377Simp idmap_retcode orc; 1115279377Simp 1116279377Simp adh = state->qadh; 1117279377Simp 1118279377Simp (void) pthread_mutex_lock(&adh->lock); 1119279377Simp 1120279377Simp if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) { 1121279377Simp (void) pthread_mutex_unlock(&adh->lock); 1122279377Simp return; 1123279377Simp } 1124279377Simp 1125279377Simp q = &(state->queries[qid]); 1126279377Simp 1127279377Simp for (attr = ldap_first_attribute(adh->ld, res, &ber); attr != NULL; 1128279377Simp attr = ldap_next_attribute(adh->ld, res, ber)) { 1129279377Simp orc = *q->rc; 1130279377Simp bvalues = NULL; /* for memory management below */ 1131279377Simp 1132279377Simp /* 1133279377Simp * If this is an attribute we are looking for and 1134279377Simp * haven't seen it yet, parse it 1135279377Simp */ 1136279377Simp if (orc != IDMAP_SUCCESS && q->n2s && !q->got_objectSid && 1137279377Simp strcasecmp(attr, OBJECTSID) == 0) { 1138279377Simp bvalues = ldap_get_values_len(adh->ld, res, attr); 1139279377Simp idmap_bv_objsid2sidstr(bvalues, q); 1140279377Simp } else if (orc != IDMAP_SUCCESS && !q->n2s && 1141279377Simp !q->got_samAcctName && 1142279377Simp strcasecmp(attr, SAMACCOUNTNAME) == 0) { 1143279377Simp bvalues = ldap_get_values_len(adh->ld, res, attr); 1144279377Simp idmap_bv_samaccountname2name(bvalues, q, dn); 1145279377Simp } else if (orc != IDMAP_SUCCESS && !q->got_objectClass && 1146279377Simp strcasecmp(attr, OBJECTCLASS) == 0) { 1147279377Simp bvalues = ldap_get_values_len(adh->ld, res, attr); 1148279377Simp idmap_bv_objclass2sidtype(bvalues, q); 1149279377Simp } 1150279377Simp 1151279377Simp if (bvalues != NULL) 1152279377Simp ldap_value_free_len(bvalues); 1153279377Simp ldap_memfree(attr); 1154279377Simp 1155279377Simp if (q->n2s) 1156279377Simp *q->rc = (q->got_objectSid && 1157279377Simp q->got_objectClass) ? 1158279377Simp IDMAP_SUCCESS : IDMAP_ERR_NORESULT; 1159279377Simp else 1160279377Simp *q->rc = (q->got_samAcctName && 1161279377Simp q->got_objectClass) ? 1162279377Simp IDMAP_SUCCESS : IDMAP_ERR_NORESULT; 1163279377Simp 1164279377Simp if (*q->rc == IDMAP_SUCCESS && *q->result == NULL) 1165279377Simp *q->rc = IDMAP_ERR_NORESULT; 1166279377Simp } 1167279377Simp (void) pthread_mutex_unlock(&adh->lock); 1168279377Simp 1169279377Simp /* 1170279377Simp * If there should be multiple partial results for different 1171279377Simp * entities (there should not be, but, if it should happen) then 1172279377Simp * it's possible that they could get mixed up here and we could 1173279377Simp * get bogus results. We just mark the query's results as 1174279377Simp * toxic (IDMAP_ERR_INTERNAL). 1175279377Simp * 1176279377Simp * Between this and ignoring results when we've already filled 1177279377Simp * out a query's results we should be OK. The first full reply 1178279377Simp * wins. In practice we should never get multiple results. 1179279377Simp */ 1180279377Simp if (orc == IDMAP_ERR_INTERNAL) 1181279377Simp *q->rc = IDMAP_ERR_INTERNAL; 1182279377Simp else if (*q->rc != IDMAP_SUCCESS) 1183279377Simp *q->rc = IDMAP_ERR_INTERNAL; 1184279377Simp 1185279377Simp if (ber != NULL) 1186279377Simp ber_free(ber, 0); 1187279377Simp 1188279377Simp ldap_memfree(dn); 1189279377Simp} 1190279377Simp 1191279377Simp/* 1192279377Simp * Try to get a result; if there is one, find the corresponding 1193279377Simp * idmap_q_t and process the result. 1194279377Simp */ 1195279377Simpstatic 1196279377Simpint 1197279377Simpidmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout) 1198279377Simp{ 1199279377Simp idmap_query_state_t *query_state; 1200279377Simp LDAPMessage *res = NULL; 1201279377Simp int rc, ret, msgid, qid; 1202279377Simp 1203279377Simp (void) pthread_mutex_lock(&adh->lock); 1204279377Simp if (adh->dead) { 1205279377Simp (void) pthread_mutex_unlock(&adh->lock); 1206279377Simp return (-1); 1207279377Simp } 1208279377Simp 1209279377Simp /* Get one result */ 1210279377Simp rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, 1211279377Simp timeout, &res); 1212279377Simp if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM || 1213279377Simp rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN || 1214279377Simp rc == LDAP_BUSY) 1215279377Simp adh->dead = 1; 1216279377Simp (void) pthread_mutex_unlock(&adh->lock); 1217279377Simp 1218279377Simp if (adh->dead) 1219279377Simp return (-1); 1220279377Simp 1221279377Simp switch (rc) { 1222279377Simp case LDAP_RES_SEARCH_RESULT: 1223279377Simp /* We have all the LDAP replies for some search... */ 1224279377Simp msgid = ldap_msgid(res); 1225279377Simp if (idmap_msgid2query(adh, msgid, 1226279377Simp &query_state, &qid)) { 1227279377Simp /* ...so we can decrement qinflight */ 1228279377Simp atomic_dec_32(&query_state->qinflight); 1229279377Simp /* we saw at least one reply */ 1230279377Simp query_state->queries[qid].got_reply = 1; 1231279377Simp idmap_lookup_unlock_batch(&query_state); 1232279377Simp } 1233279377Simp (void) ldap_msgfree(res); 1234279377Simp ret = 0; 1235279377Simp break; 1236279377Simp case LDAP_RES_SEARCH_REFERENCE: 1237279377Simp /* 1238279377Simp * We have no need for these at the moment. Eventually, 1239279377Simp * when we query things that we can't expect to find in 1240279377Simp * the Global Catalog then we'll need to learn to follow 1241279377Simp * references. 1242279377Simp */ 1243279377Simp (void) ldap_msgfree(res); 1244279377Simp ret = 0; 1245279377Simp break; 1246279377Simp case LDAP_RES_SEARCH_ENTRY: 1247279377Simp /* Got a result */ 1248279377Simp msgid = ldap_msgid(res); 1249279377Simp if (idmap_msgid2query(adh, msgid, 1250279377Simp &query_state, &qid)) { 1251279377Simp idmap_extract_object(query_state, qid, res); 1252279377Simp /* we saw at least one result */ 1253279377Simp query_state->queries[qid].got_reply = 1; 1254279377Simp query_state->queries[qid].got_results = 1; 1255279377Simp idmap_lookup_unlock_batch(&query_state); 1256279377Simp } 1257279377Simp (void) ldap_msgfree(res); 1258279377Simp ret = 0; 1259279377Simp break; 1260279377Simp default: 1261279377Simp /* timeout or error; treat the same */ 1262279377Simp ret = -1; 1263279377Simp break; 1264279377Simp } 1265279377Simp 1266279377Simp return (ret); 1267279377Simp} 1268279377Simp 1269279377Simp/* 1270279377Simp * This routine decreament the reference count of the 1271279377Simp * idmap_query_state_t 1272279377Simp */ 1273279377Simpstatic void 1274279377Simpidmap_lookup_unlock_batch(idmap_query_state_t **state) 1275279377Simp{ 1276279377Simp /* 1277279377Simp * Decrement reference count with qstatelock locked 1278279377Simp */ 1279279377Simp (void) pthread_mutex_lock(&qstatelock); 1280279377Simp (*state)->ref_cnt--; 1281279377Simp /* 1282279377Simp * If there are no references wakup the allocating thread 1283279377Simp */ 1284279377Simp if ((*state)->ref_cnt == 0) 1285279377Simp (void) pthread_cond_signal(&(*state)->cv); 1286279377Simp (void) pthread_mutex_unlock(&qstatelock); 1287279377Simp *state = NULL; 1288279377Simp} 1289279377Simp 1290279377Simp/* 1291279377Simp * This routine frees the idmap_query_state_t structure 1292279377Simp * If the reference count is greater than 1 it waits 1293279377Simp * for the other threads to finish using it. 1294279377Simp */ 1295279377Simpvoid 1296279377Simpidmap_lookup_release_batch(idmap_query_state_t **state) 1297279377Simp{ 1298279377Simp idmap_query_state_t **p; 1299279377Simp 1300279377Simp /* 1301279377Simp * Decrement reference count with qstatelock locked 1302279377Simp * and wait for reference count to get to zero 1303279377Simp */ 1304279377Simp (void) pthread_mutex_lock(&qstatelock); 1305279377Simp (*state)->ref_cnt--; 1306279377Simp while ((*state)->ref_cnt > 0) { 1307279377Simp (void) pthread_cond_wait(&(*state)->cv, &qstatelock); 1308279377Simp } 1309279377Simp 1310279377Simp /* Remove this state struct from the list of state structs */ 1311279377Simp for (p = &qstatehead; *p != NULL; p = &(*p)->next) { 1312279377Simp if (*p == (*state)) { 1313279377Simp *p = (*state)->next; 1314279377Simp break; 1315279377Simp } 1316279377Simp } 1317279377Simp (void) pthread_mutex_unlock(&qstatelock); 1318279377Simp 1319279377Simp (void) pthread_cond_destroy(&(*state)->cv); 1320279377Simp 1321279377Simp idmap_release_conn((*state)->qadh); 1322279377Simp 1323279377Simp free(*state); 1324279377Simp *state = NULL; 1325279377Simp} 1326279377Simp 1327279377Simpidmap_retcode 1328279377Simpidmap_lookup_batch_end(idmap_query_state_t **state, 1329279377Simp struct timeval *timeout) 1330279377Simp{ 1331279377Simp idmap_q_t *q; 1332279377Simp int i; 1333279377Simp int rc = LDAP_SUCCESS; 1334279377Simp idmap_retcode retcode = IDMAP_SUCCESS; 1335279377Simp 1336279377Simp (*state)->qdead = 1; 1337279377Simp 1338279377Simp /* Process results until done or until timeout, if given */ 1339279377Simp while ((*state)->qinflight > 0) { 1340279377Simp if ((rc = idmap_get_adobject_batch((*state)->qadh, 1341279377Simp timeout)) != 0) 1342279377Simp break; 1343279377Simp } 1344279377Simp 1345279377Simp if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM || 1346279377Simp rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN || 1347279377Simp rc == LDAP_BUSY) { 1348279377Simp retcode = IDMAP_ERR_RETRIABLE_NET_ERR; 1349279377Simp (*state)->qadh->dead = 1; 1350279377Simp } 1351279377Simp 1352279377Simp for (i = 0; i < (*state)->qcount; i++) { 1353279377Simp q = &((*state)->queries[i]); 1354279377Simp if (q->got_reply && !q->got_results) { 1355279377Simp if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) 1356279377Simp *q->rc = IDMAP_ERR_RETRIABLE_NET_ERR; 1357279377Simp else 1358279377Simp *q->rc = IDMAP_ERR_NOTFOUND; 1359279377Simp } 1360279377Simp } 1361279377Simp 1362279377Simp idmap_lookup_release_batch(state); 1363279377Simp 1364279377Simp return (retcode); 1365279377Simp} 1366279377Simp 1367279377Simp/* 1368279377Simp * Send one prepared search, queue up msgid, process what results are 1369279377Simp * available 1370279377Simp */ 1371279377Simpstatic 1372279377Simpidmap_retcode 1373279377Simpidmap_batch_add1(idmap_query_state_t *state, int n2s, 1374279377Simp const char *filter, const char *basedn, 1375279377Simp char **result, char **dname, rid_t *rid, int *sid_type, 1376279377Simp idmap_retcode *rc) 1377279377Simp{ 1378279377Simp idmap_retcode retcode = IDMAP_SUCCESS; 1379279377Simp int lrc, qid; 1380279377Simp struct timeval tv; 1381279377Simp idmap_q_t *q; 1382279377Simp 1383279377Simp if (state->qdead) { 1384279377Simp *rc = IDMAP_ERR_NORESULT; 1385279377Simp return (IDMAP_ERR_RETRIABLE_NET_ERR); 1386279377Simp } 1387279377Simp 1388279377Simp qid = atomic_inc_32_nv(&state->qlastsent) - 1; 1389279377Simp 1390279377Simp q = &(state->queries[qid]); 1391279377Simp 1392279377Simp /* Remember where to put the results */ 1393279377Simp q->result = result; 1394279377Simp q->domain = dname; 1395279377Simp q->rid = rid; 1396279377Simp q->sid_type = sid_type; 1397279377Simp q->rc = rc; 1398279377Simp q->n2s = n2s ? 1 : 0; 1399279377Simp q->got_objectSid = 0; 1400279377Simp q->got_objectClass = 0; 1401279377Simp q->got_samAcctName = 0; 1402279377Simp 1403279377Simp /* 1404279377Simp * Provide sane defaults for the results in case we never hear 1405279377Simp * back from the DS before closing the connection. 1406279377Simp */ 1407279377Simp *rc = IDMAP_ERR_RETRIABLE_NET_ERR; 1408279377Simp *sid_type = _IDMAP_T_OTHER; 1409279377Simp *result = NULL; 1410279377Simp if (dname != NULL) 1411279377Simp *dname = NULL; 1412279377Simp if (rid != NULL) 1413279377Simp *rid = 0; 1414279377Simp 1415279377Simp /* Send this lookup, don't wait for a result here */ 1416279377Simp (void) pthread_mutex_lock(&state->qadh->lock); 1417279377Simp 1418279377Simp if (!state->qadh->dead) { 1419279377Simp state->qadh->idletime = time(NULL); 1420279377Simp lrc = ldap_search_ext(state->qadh->ld, basedn, 1421279377Simp LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, 1422279377Simp NULL, -1, &q->msgid); 1423279377Simp if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE || 1424279377Simp lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN || 1425279377Simp lrc == LDAP_UNWILLING_TO_PERFORM) { 1426279377Simp retcode = IDMAP_ERR_RETRIABLE_NET_ERR; 1427279377Simp state->qadh->dead = 1; 1428279377Simp } else if (lrc != LDAP_SUCCESS) { 1429279377Simp retcode = IDMAP_ERR_OTHER; 1430279377Simp state->qadh->dead = 1; 1431279377Simp } 1432279377Simp } 1433279377Simp (void) pthread_mutex_unlock(&state->qadh->lock); 1434279377Simp 1435279377Simp if (state->qadh->dead) 1436279377Simp return (retcode); 1437279377Simp 1438279377Simp atomic_inc_32(&state->qinflight); 1439279377Simp 1440279377Simp /* 1441279377Simp * Reap as many requests as we can _without_ waiting 1442279377Simp * 1443279377Simp * We do this to prevent any possible TCP socket buffer 1444279377Simp * starvation deadlocks. 1445279377Simp */ 1446279377Simp (void) memset(&tv, 0, sizeof (tv)); 1447279377Simp while (idmap_get_adobject_batch(state->qadh, &tv) == 0) 1448279377Simp ; 1449279377Simp 1450279377Simp return (IDMAP_SUCCESS); 1451279377Simp} 1452279377Simp 1453279377Simpidmap_retcode 1454279377Simpidmap_name2sid_batch_add1(idmap_query_state_t *state, 1455279377Simp const char *name, const char *dname, 1456279377Simp char **sid, rid_t *rid, int *sid_type, idmap_retcode *rc) 1457279377Simp{ 1458279377Simp idmap_retcode retcode; 1459279377Simp int flen, samAcctNameLen; 1460279377Simp char *filter = NULL; 1461279377Simp char *basedn = NULL; 1462279377Simp char *cp; 1463279377Simp 1464279377Simp /* 1465279377Simp * Strategy: search [the global catalog] for user/group by 1466279377Simp * sAMAccountName = user/groupname with base DN derived from the 1467279377Simp * domain name. The objectSid and objectClass of the result are 1468279377Simp * all we need to figure out the SID of the user/group and 1469279377Simp * whether it is a user or a group. 1470279377Simp */ 1471279377Simp 1472279377Simp /* 1473279377Simp * Handle optional domain parameter and default domain 1474279377Simp * semantics. The get a basedn from the domainname. 1475279377Simp */ 1476279377Simp samAcctNameLen = strlen(name); 1477279377Simp if (dname == NULL || *dname == '\0') { 1478279377Simp /* domain name not given separately */ 1479279377Simp if ((cp = strchr(name, '@')) == NULL) { 1480279377Simp /* nor is the name qualified */ 1481279377Simp dname = state->qadh->owner->dflt_w2k_dom; 1482279377Simp basedn = state->qadh->owner->basedn; 1483279377Simp } else { 1484279377Simp /* the name is qualified */ 1485279377Simp samAcctNameLen -= strlen(cp); 1486279377Simp dname = cp + 1; 1487279377Simp } 1488279377Simp } 1489279377Simp 1490279377Simp if (basedn == NULL) 1491279377Simp basedn = dns2dn(dname); 1492279377Simp 1493279377Simp /* Assemble filter */ 1494279377Simp flen = snprintf(NULL, 0, SANFILTER, samAcctNameLen, name) + 1; 1495279377Simp if ((filter = (char *)malloc(flen)) == NULL) { 1496279377Simp if (basedn != state->qadh->owner->basedn) 1497279377Simp free(basedn); 1498279377Simp return (IDMAP_ERR_MEMORY); 1499279377Simp } 1500279377Simp (void) snprintf(filter, flen, SANFILTER, samAcctNameLen, name); 1501279377Simp 1502279377Simp retcode = idmap_batch_add1(state, 1, filter, basedn, 1503279377Simp sid, NULL, rid, sid_type, rc); 1504279377Simp 1505279377Simp if (basedn != state->qadh->owner->basedn) 1506279377Simp free(basedn); 1507279377Simp free(filter); 1508279377Simp 1509279377Simp return (retcode); 1510279377Simp} 1511279377Simp 1512279377Simpidmap_retcode 1513279377Simpidmap_sid2name_batch_add1(idmap_query_state_t *state, 1514279377Simp const char *sid, const rid_t *rid, 1515279377Simp char **name, char **dname, int *sid_type, idmap_retcode *rc) 1516279377Simp{ 1517279377Simp idmap_retcode retcode; 1518279377Simp int flen, ret; 1519279377Simp char *filter = NULL; 1520279377Simp char cbinsid[MAXHEXBINSID + 1]; 1521279377Simp 1522279377Simp /* 1523279377Simp * Strategy: search [the global catalog] for user/group by 1524279377Simp * objectSid = SID with empty base DN. The DN, sAMAccountName 1525279377Simp * and objectClass of the result are all we need to figure out 1526279377Simp * the name of the SID and whether it is a user, a group or a 1527279377Simp * computer. 1528279377Simp */ 1529279377Simp 1530279377Simp ret = idmap_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); 1531279377Simp if (ret != 0) 1532279377Simp return (IDMAP_ERR_SID); 1533279377Simp 1534279377Simp /* Assemble filter */ 1535279377Simp flen = snprintf(NULL, 0, OBJECTSIDFILTER, cbinsid) + 1; 1536279377Simp if ((filter = (char *)malloc(flen)) == NULL) 1537279377Simp return (IDMAP_ERR_MEMORY); 1538279377Simp (void) snprintf(filter, flen, OBJECTSIDFILTER, cbinsid); 1539279377Simp 1540279377Simp retcode = idmap_batch_add1(state, 0, filter, NULL, name, dname, 1541279377Simp NULL, sid_type, rc); 1542279377Simp 1543279377Simp free(filter); 1544279377Simp 1545279377Simp return (retcode); 1546279377Simp} 1547279377Simp