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