1*1fcced4cSJordan Brown /*
2*1fcced4cSJordan Brown  * CDDL HEADER START
3*1fcced4cSJordan Brown  *
4*1fcced4cSJordan Brown  * The contents of this file are subject to the terms of the
5*1fcced4cSJordan Brown  * Common Development and Distribution License (the "License").
6*1fcced4cSJordan Brown  * You may not use this file except in compliance with the License.
7*1fcced4cSJordan Brown  *
8*1fcced4cSJordan Brown  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1fcced4cSJordan Brown  * or http://www.opensolaris.org/os/licensing.
10*1fcced4cSJordan Brown  * See the License for the specific language governing permissions
11*1fcced4cSJordan Brown  * and limitations under the License.
12*1fcced4cSJordan Brown  *
13*1fcced4cSJordan Brown  * When distributing Covered Code, include this CDDL HEADER in each
14*1fcced4cSJordan Brown  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1fcced4cSJordan Brown  * If applicable, add the following below this CDDL HEADER, with the
16*1fcced4cSJordan Brown  * fields enclosed by brackets "[]" replaced with your own identifying
17*1fcced4cSJordan Brown  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1fcced4cSJordan Brown  *
19*1fcced4cSJordan Brown  * CDDL HEADER END
20*1fcced4cSJordan Brown  */
21*1fcced4cSJordan Brown 
22*1fcced4cSJordan Brown /*
23*1fcced4cSJordan Brown  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*1fcced4cSJordan Brown  * Use is subject to license terms.
25*1fcced4cSJordan Brown  */
26*1fcced4cSJordan Brown 
27*1fcced4cSJordan Brown /*
28*1fcced4cSJordan Brown  * Retrieve directory information for Active Directory users.
29*1fcced4cSJordan Brown  */
30*1fcced4cSJordan Brown 
31*1fcced4cSJordan Brown #include <ldap.h>
32*1fcced4cSJordan Brown #include <lber.h>
33*1fcced4cSJordan Brown #include <pwd.h>
34*1fcced4cSJordan Brown #include <malloc.h>
35*1fcced4cSJordan Brown #include <string.h>
36*1fcced4cSJordan Brown #include <stdlib.h>
37*1fcced4cSJordan Brown #include <netdb.h>
38*1fcced4cSJordan Brown #include <libadutils.h>
39*1fcced4cSJordan Brown #include <note.h>
40*1fcced4cSJordan Brown #include <assert.h>
41*1fcced4cSJordan Brown #include "directory.h"
42*1fcced4cSJordan Brown #include "directory_private.h"
43*1fcced4cSJordan Brown #include "idmapd.h"
44*1fcced4cSJordan Brown #include <rpcsvc/idmap_prot.h>
45*1fcced4cSJordan Brown #include "directory_server_impl.h"
46*1fcced4cSJordan Brown #include "miscutils.h"
47*1fcced4cSJordan Brown 
48*1fcced4cSJordan Brown /*
49*1fcced4cSJordan Brown  * Information required by the function that handles the callback from LDAP
50*1fcced4cSJordan Brown  * when responses are received.
51*1fcced4cSJordan Brown  */
52*1fcced4cSJordan Brown struct cbinfo {
53*1fcced4cSJordan Brown 	const char * const *attrs;
54*1fcced4cSJordan Brown 	int nattrs;
55*1fcced4cSJordan Brown 	directory_entry_rpc *entry;
56*1fcced4cSJordan Brown 	const char *domain;
57*1fcced4cSJordan Brown };
58*1fcced4cSJordan Brown 
59*1fcced4cSJordan Brown static void directory_provider_ad_cb(LDAP *ld, LDAPMessage **ldapres, int rc,
60*1fcced4cSJordan Brown     int qid, void *argp);
61*1fcced4cSJordan Brown static void directory_provider_ad_cb1(LDAP *ld, LDAPMessage *msg,
62*1fcced4cSJordan Brown     struct cbinfo *cbinfo);
63*1fcced4cSJordan Brown static directory_error_t bv_list_dav(directory_values_rpc *lvals,
64*1fcced4cSJordan Brown     struct berval **bv);
65*1fcced4cSJordan Brown static directory_error_t directory_provider_ad_lookup(
66*1fcced4cSJordan Brown     directory_entry_rpc *pent, const char * const * attrs, int nattrs,
67*1fcced4cSJordan Brown     const char *domain, const char *filter);
68*1fcced4cSJordan Brown static directory_error_t get_domain(LDAP *ld, LDAPMessage *ldapres,
69*1fcced4cSJordan Brown     char **domain);
70*1fcced4cSJordan Brown static directory_error_t directory_provider_ad_utils_error(char *func, int rc);
71*1fcced4cSJordan Brown 
72*1fcced4cSJordan Brown #if	defined(DUMP_VALUES)
73*1fcced4cSJordan Brown static void dump_bv_list(const char *attr, struct berval **bv);
74*1fcced4cSJordan Brown #endif
75*1fcced4cSJordan Brown 
76*1fcced4cSJordan Brown #define	MAX_EXTRA_ATTRS	1	/* sAMAccountName */
77*1fcced4cSJordan Brown 
78*1fcced4cSJordan Brown /*
79*1fcced4cSJordan Brown  * Add an entry to a NULL-terminated list, if it's not already there.
80*1fcced4cSJordan Brown  * Assumes that the list has been allocated large enough for all additions,
81*1fcced4cSJordan Brown  * and prefilled with NULL.
82*1fcced4cSJordan Brown  */
83*1fcced4cSJordan Brown static
84*1fcced4cSJordan Brown void
85*1fcced4cSJordan Brown maybe_add_to_list(const char **list, const char *s)
86*1fcced4cSJordan Brown {
87*1fcced4cSJordan Brown 	for (; *list != NULL; list++) {
88*1fcced4cSJordan Brown 		if (strcaseeq(*list, s))
89*1fcced4cSJordan Brown 			return;
90*1fcced4cSJordan Brown 	}
91*1fcced4cSJordan Brown 	*list = s;
92*1fcced4cSJordan Brown }
93*1fcced4cSJordan Brown 
94*1fcced4cSJordan Brown /*
95*1fcced4cSJordan Brown  * Copy a counted attribute list to a NULL-terminated one.
96*1fcced4cSJordan Brown  * In the process, examine the requested attributes and augment
97*1fcced4cSJordan Brown  * the list as required to support any synthesized attributes
98*1fcced4cSJordan Brown  * requested.
99*1fcced4cSJordan Brown  */
100*1fcced4cSJordan Brown static
101*1fcced4cSJordan Brown const char **
102*1fcced4cSJordan Brown copy_and_augment_attr_list(char **req_list, int req_list_len)
103*1fcced4cSJordan Brown {
104*1fcced4cSJordan Brown 	const char **new_list;
105*1fcced4cSJordan Brown 	int i;
106*1fcced4cSJordan Brown 
107*1fcced4cSJordan Brown 	new_list =
108*1fcced4cSJordan Brown 	    calloc(req_list_len + MAX_EXTRA_ATTRS + 1, sizeof (*new_list));
109*1fcced4cSJordan Brown 	if (new_list == NULL)
110*1fcced4cSJordan Brown 		return (NULL);
111*1fcced4cSJordan Brown 
112*1fcced4cSJordan Brown 	(void) memcpy(new_list, req_list, req_list_len * sizeof (char *));
113*1fcced4cSJordan Brown 
114*1fcced4cSJordan Brown 	for (i = 0; i < req_list_len; i++) {
115*1fcced4cSJordan Brown 		const char *a = req_list[i];
116*1fcced4cSJordan Brown 		/*
117*1fcced4cSJordan Brown 		 * Note that you must update MAX_EXTRA_ATTRS above if you
118*1fcced4cSJordan Brown 		 * add to this list.
119*1fcced4cSJordan Brown 		 */
120*1fcced4cSJordan Brown 		if (strcaseeq(a, "x-sun-canonicalName")) {
121*1fcced4cSJordan Brown 			maybe_add_to_list(new_list, "sAMAccountName");
122*1fcced4cSJordan Brown 			continue;
123*1fcced4cSJordan Brown 		}
124*1fcced4cSJordan Brown 		/* None needed for x-sun-provider */
125*1fcced4cSJordan Brown 	}
126*1fcced4cSJordan Brown 
127*1fcced4cSJordan Brown 	return (new_list);
128*1fcced4cSJordan Brown }
129*1fcced4cSJordan Brown 
130*1fcced4cSJordan Brown /*
131*1fcced4cSJordan Brown  * Retrieve information by name.
132*1fcced4cSJordan Brown  * Called indirectly through the Directory_provider_static structure.
133*1fcced4cSJordan Brown  */
134*1fcced4cSJordan Brown static
135*1fcced4cSJordan Brown directory_error_t
136*1fcced4cSJordan Brown directory_provider_ad_get(
137*1fcced4cSJordan Brown     directory_entry_rpc *del,
138*1fcced4cSJordan Brown     idmap_utf8str_list *ids,
139*1fcced4cSJordan Brown     char *types,
140*1fcced4cSJordan Brown     idmap_utf8str_list *attrs)
141*1fcced4cSJordan Brown {
142*1fcced4cSJordan Brown 	int i;
143*1fcced4cSJordan Brown 	const char **attrs2;
144*1fcced4cSJordan Brown 	directory_error_t de = NULL;
145*1fcced4cSJordan Brown 
146*1fcced4cSJordan Brown 	/*
147*1fcced4cSJordan Brown 	 * If we don't have any AD servers handy, we can't find anything.
148*1fcced4cSJordan Brown 	 */
149*1fcced4cSJordan Brown 	if (_idmapdstate.num_ads < 1) {
150*1fcced4cSJordan Brown 		return (NULL);
151*1fcced4cSJordan Brown 	}
152*1fcced4cSJordan Brown 
153*1fcced4cSJordan Brown 	RDLOCK_CONFIG()
154*1fcced4cSJordan Brown 
155*1fcced4cSJordan Brown 	/* 6835280 spurious lint error if the strlen is in the declaration */
156*1fcced4cSJordan Brown 	int len = strlen(_idmapdstate.cfg->pgcfg.default_domain);
157*1fcced4cSJordan Brown 	char default_domain[len + 1];
158*1fcced4cSJordan Brown 	(void) strcpy(default_domain, _idmapdstate.cfg->pgcfg.default_domain);
159*1fcced4cSJordan Brown 
160*1fcced4cSJordan Brown 	UNLOCK_CONFIG();
161*1fcced4cSJordan Brown 
162*1fcced4cSJordan Brown 	/*
163*1fcced4cSJordan Brown 	 * Turn our counted-array argument into a NULL-terminated array.
164*1fcced4cSJordan Brown 	 * At the same time, add in any attributes that we need to support
165*1fcced4cSJordan Brown 	 * any requested synthesized attributes.
166*1fcced4cSJordan Brown 	 */
167*1fcced4cSJordan Brown 	attrs2 = copy_and_augment_attr_list(attrs->idmap_utf8str_list_val,
168*1fcced4cSJordan Brown 	    attrs->idmap_utf8str_list_len);
169*1fcced4cSJordan Brown 	if (attrs2 == NULL)
170*1fcced4cSJordan Brown 		goto nomem;
171*1fcced4cSJordan Brown 
172*1fcced4cSJordan Brown 	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
173*1fcced4cSJordan Brown 		char *vw[3];
174*1fcced4cSJordan Brown 		int type;
175*1fcced4cSJordan Brown 
176*1fcced4cSJordan Brown 		/*
177*1fcced4cSJordan Brown 		 * Extract the type for this particular ID.
178*1fcced4cSJordan Brown 		 * Advance to the next type, if it's there, else keep
179*1fcced4cSJordan Brown 		 * using this type until we run out of IDs.
180*1fcced4cSJordan Brown 		 */
181*1fcced4cSJordan Brown 		type = *types;
182*1fcced4cSJordan Brown 		if (*(types+1) != '\0')
183*1fcced4cSJordan Brown 			types++;
184*1fcced4cSJordan Brown 
185*1fcced4cSJordan Brown 		/*
186*1fcced4cSJordan Brown 		 * If this entry has already been handled, one way or another,
187*1fcced4cSJordan Brown 		 * skip it.
188*1fcced4cSJordan Brown 		 */
189*1fcced4cSJordan Brown 		if (del[i].status != DIRECTORY_NOT_FOUND)
190*1fcced4cSJordan Brown 			continue;
191*1fcced4cSJordan Brown 
192*1fcced4cSJordan Brown 		char *id = ids->idmap_utf8str_list_val[i];
193*1fcced4cSJordan Brown 
194*1fcced4cSJordan Brown 		/*
195*1fcced4cSJordan Brown 		 * Allow for expanding every character to \xx, plus some
196*1fcced4cSJordan Brown 		 * space for the query syntax.
197*1fcced4cSJordan Brown 		 */
198*1fcced4cSJordan Brown 		int id_len = strlen(id);
199*1fcced4cSJordan Brown 		char filter[1000 + id_len*3];
200*1fcced4cSJordan Brown 
201*1fcced4cSJordan Brown 		if (type == DIRECTORY_ID_SID[0]) {
202*1fcced4cSJordan Brown 			/*
203*1fcced4cSJordan Brown 			 * Mildly surprisingly, AD appears to allow searching
204*1fcced4cSJordan Brown 			 * based on text SIDs.  Must be a special case on the
205*1fcced4cSJordan Brown 			 * server end.
206*1fcced4cSJordan Brown 			 */
207*1fcced4cSJordan Brown 			ldap_build_filter(filter, sizeof (filter),
208*1fcced4cSJordan Brown 			    "(objectSid=%v)", NULL, NULL, NULL, id, NULL);
209*1fcced4cSJordan Brown 
210*1fcced4cSJordan Brown 			de = directory_provider_ad_lookup(&del[i], attrs2,
211*1fcced4cSJordan Brown 			    attrs->idmap_utf8str_list_len, NULL, filter);
212*1fcced4cSJordan Brown 			if (de != NULL) {
213*1fcced4cSJordan Brown 				directory_entry_set_error(&del[i], de);
214*1fcced4cSJordan Brown 				de = NULL;
215*1fcced4cSJordan Brown 			}
216*1fcced4cSJordan Brown 		} else {
217*1fcced4cSJordan Brown 			int id_len = strlen(id);
218*1fcced4cSJordan Brown 			char name[id_len + 1];
219*1fcced4cSJordan Brown 			char domain[id_len + 1];
220*1fcced4cSJordan Brown 
221*1fcced4cSJordan Brown 			split_name(name, domain, id);
222*1fcced4cSJordan Brown 
223*1fcced4cSJordan Brown 			vw[0] = name;
224*1fcced4cSJordan Brown 
225*1fcced4cSJordan Brown 			if (streq(domain, "")) {
226*1fcced4cSJordan Brown 				vw[1] = default_domain;
227*1fcced4cSJordan Brown 			} else {
228*1fcced4cSJordan Brown 				vw[1] = domain;
229*1fcced4cSJordan Brown 			}
230*1fcced4cSJordan Brown 
231*1fcced4cSJordan Brown 			if (type == DIRECTORY_ID_USER[0])
232*1fcced4cSJordan Brown 				vw[2] = "user";
233*1fcced4cSJordan Brown 			else if (type == DIRECTORY_ID_GROUP[0])
234*1fcced4cSJordan Brown 				vw[2] = "group";
235*1fcced4cSJordan Brown 			else
236*1fcced4cSJordan Brown 				vw[2] = "*";
237*1fcced4cSJordan Brown 
238*1fcced4cSJordan Brown 			/*
239*1fcced4cSJordan Brown 			 * Try samAccountName.
240*1fcced4cSJordan Brown 			 * Note that here we rely on checking the returned
241*1fcced4cSJordan Brown 			 * distinguishedName to make sure that we found an
242*1fcced4cSJordan Brown 			 * entry from the right domain, because there's no
243*1fcced4cSJordan Brown 			 * attribute we can straightforwardly filter for to
244*1fcced4cSJordan Brown 			 * match domain.
245*1fcced4cSJordan Brown 			 *
246*1fcced4cSJordan Brown 			 * Eventually we should perhaps also try
247*1fcced4cSJordan Brown 			 * userPrincipalName.
248*1fcced4cSJordan Brown 			 */
249*1fcced4cSJordan Brown 			ldap_build_filter(filter, sizeof (filter),
250*1fcced4cSJordan Brown 			    "(&(samAccountName=%v1)(objectClass=%v3))",
251*1fcced4cSJordan Brown 			    NULL, NULL, NULL, NULL, vw);
252*1fcced4cSJordan Brown 
253*1fcced4cSJordan Brown 			de = directory_provider_ad_lookup(&del[i], attrs2,
254*1fcced4cSJordan Brown 			    attrs->idmap_utf8str_list_len, vw[1], filter);
255*1fcced4cSJordan Brown 			if (de != NULL) {
256*1fcced4cSJordan Brown 				directory_entry_set_error(&del[i], de);
257*1fcced4cSJordan Brown 				de = NULL;
258*1fcced4cSJordan Brown 			}
259*1fcced4cSJordan Brown 		}
260*1fcced4cSJordan Brown 	}
261*1fcced4cSJordan Brown 
262*1fcced4cSJordan Brown 	de = NULL;
263*1fcced4cSJordan Brown 
264*1fcced4cSJordan Brown 	goto out;
265*1fcced4cSJordan Brown 
266*1fcced4cSJordan Brown nomem:
267*1fcced4cSJordan Brown 	de = directory_error("ENOMEM.AD",
268*1fcced4cSJordan Brown 	    "Out of memory during AD lookup", NULL);
269*1fcced4cSJordan Brown out:
270*1fcced4cSJordan Brown 	free(attrs2);
271*1fcced4cSJordan Brown 	return (de);
272*1fcced4cSJordan Brown }
273*1fcced4cSJordan Brown 
274*1fcced4cSJordan Brown /*
275*1fcced4cSJordan Brown  * Note that attrs is NULL terminated, and that nattrs is the number
276*1fcced4cSJordan Brown  * of attributes requested by the user... which might be fewer than are
277*1fcced4cSJordan Brown  * in attrs because of attributes that we need for our own processing.
278*1fcced4cSJordan Brown  */
279*1fcced4cSJordan Brown static
280*1fcced4cSJordan Brown directory_error_t
281*1fcced4cSJordan Brown directory_provider_ad_lookup(
282*1fcced4cSJordan Brown     directory_entry_rpc *pent,
283*1fcced4cSJordan Brown     const char * const * attrs,
284*1fcced4cSJordan Brown     int nattrs,
285*1fcced4cSJordan Brown     const char *domain,
286*1fcced4cSJordan Brown     const char *filter)
287*1fcced4cSJordan Brown {
288*1fcced4cSJordan Brown 	adutils_ad_t *ad;
289*1fcced4cSJordan Brown 	adutils_rc batchrc;
290*1fcced4cSJordan Brown 	struct cbinfo cbinfo;
291*1fcced4cSJordan Brown 	adutils_query_state_t *qs;
292*1fcced4cSJordan Brown 	int rc;
293*1fcced4cSJordan Brown 
294*1fcced4cSJordan Brown 	/*
295*1fcced4cSJordan Brown 	 * NEEDSWORK:  Should eventually handle other forests.
296*1fcced4cSJordan Brown 	 * NEEDSWORK:  Should eventually handle non-GC attributes.
297*1fcced4cSJordan Brown 	 */
298*1fcced4cSJordan Brown 	ad = _idmapdstate.ads[0];
299*1fcced4cSJordan Brown 
300*1fcced4cSJordan Brown 	/* Stash away information for the callback function. */
301*1fcced4cSJordan Brown 	cbinfo.attrs = attrs;
302*1fcced4cSJordan Brown 	cbinfo.nattrs = nattrs;
303*1fcced4cSJordan Brown 	cbinfo.entry = pent;
304*1fcced4cSJordan Brown 	cbinfo.domain = domain;
305*1fcced4cSJordan Brown 
306*1fcced4cSJordan Brown 	rc = adutils_lookup_batch_start(ad, 1, directory_provider_ad_cb,
307*1fcced4cSJordan Brown 	    &cbinfo, &qs);
308*1fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
309*1fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
310*1fcced4cSJordan Brown 		    "adutils_lookup_batch_start", rc));
311*1fcced4cSJordan Brown 	}
312*1fcced4cSJordan Brown 
313*1fcced4cSJordan Brown 	rc = adutils_lookup_batch_add(qs, filter, attrs, domain,
314*1fcced4cSJordan Brown 	    NULL, &batchrc);
315*1fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
316*1fcced4cSJordan Brown 		adutils_lookup_batch_release(&qs);
317*1fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
318*1fcced4cSJordan Brown 		    "adutils_lookup_batch_add", rc));
319*1fcced4cSJordan Brown 	}
320*1fcced4cSJordan Brown 
321*1fcced4cSJordan Brown 	rc = adutils_lookup_batch_end(&qs);
322*1fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
323*1fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
324*1fcced4cSJordan Brown 		    "adutils_lookup_batch_end", rc));
325*1fcced4cSJordan Brown 	}
326*1fcced4cSJordan Brown 
327*1fcced4cSJordan Brown 	if (batchrc != ADUTILS_SUCCESS) {
328*1fcced4cSJordan Brown 		/*
329*1fcced4cSJordan Brown 		 * NEEDSWORK:  We're consistently getting -9997 here.
330*1fcced4cSJordan Brown 		 * What does it mean?
331*1fcced4cSJordan Brown 		 */
332*1fcced4cSJordan Brown 		return (NULL);
333*1fcced4cSJordan Brown 	}
334*1fcced4cSJordan Brown 
335*1fcced4cSJordan Brown 	return (NULL);
336*1fcced4cSJordan Brown }
337*1fcced4cSJordan Brown 
338*1fcced4cSJordan Brown /*
339*1fcced4cSJordan Brown  * Callback from the LDAP functions when they get responses.
340*1fcced4cSJordan Brown  * We don't really need (nor want) asynchronous handling, but it's
341*1fcced4cSJordan Brown  * what libadutils gives us.
342*1fcced4cSJordan Brown  */
343*1fcced4cSJordan Brown static
344*1fcced4cSJordan Brown void
345*1fcced4cSJordan Brown directory_provider_ad_cb(
346*1fcced4cSJordan Brown     LDAP *ld,
347*1fcced4cSJordan Brown     LDAPMessage **ldapres,
348*1fcced4cSJordan Brown     int rc,
349*1fcced4cSJordan Brown     int qid,
350*1fcced4cSJordan Brown     void *argp)
351*1fcced4cSJordan Brown {
352*1fcced4cSJordan Brown 	NOTE(ARGUNUSED(rc, qid))
353*1fcced4cSJordan Brown 	struct cbinfo *cbinfo = (struct cbinfo *)argp;
354*1fcced4cSJordan Brown 	LDAPMessage *msg = *ldapres;
355*1fcced4cSJordan Brown 
356*1fcced4cSJordan Brown 	for (msg = ldap_first_entry(ld, msg);
357*1fcced4cSJordan Brown 	    msg != NULL;
358*1fcced4cSJordan Brown 	    msg = ldap_next_entry(ld, msg)) {
359*1fcced4cSJordan Brown 		directory_provider_ad_cb1(ld, msg, cbinfo);
360*1fcced4cSJordan Brown 	}
361*1fcced4cSJordan Brown }
362*1fcced4cSJordan Brown 
363*1fcced4cSJordan Brown /*
364*1fcced4cSJordan Brown  * Process a single entry returned by an LDAP callback.
365*1fcced4cSJordan Brown  * Note that this performs a function roughly equivalent to the
366*1fcced4cSJordan Brown  * directory*Populate() functions in the other providers.
367*1fcced4cSJordan Brown  * Given an LDAP response, populate the directory entry for return to
368*1fcced4cSJordan Brown  * the caller.  This one differs primarily in that we're working directly
369*1fcced4cSJordan Brown  * with LDAP, so we don't have to do any attribute translation.
370*1fcced4cSJordan Brown  */
371*1fcced4cSJordan Brown static
372*1fcced4cSJordan Brown void
373*1fcced4cSJordan Brown directory_provider_ad_cb1(
374*1fcced4cSJordan Brown     LDAP *ld,
375*1fcced4cSJordan Brown     LDAPMessage *msg,
376*1fcced4cSJordan Brown     struct cbinfo *cbinfo)
377*1fcced4cSJordan Brown {
378*1fcced4cSJordan Brown 	int nattrs = cbinfo->nattrs;
379*1fcced4cSJordan Brown 	const char * const *attrs = cbinfo->attrs;
380*1fcced4cSJordan Brown 	directory_entry_rpc *pent = cbinfo->entry;
381*1fcced4cSJordan Brown 
382*1fcced4cSJordan Brown 	int i;
383*1fcced4cSJordan Brown 	directory_values_rpc *llvals;
384*1fcced4cSJordan Brown 	directory_error_t de;
385*1fcced4cSJordan Brown 	char *domain = NULL;
386*1fcced4cSJordan Brown 
387*1fcced4cSJordan Brown 	/*
388*1fcced4cSJordan Brown 	 * We don't have a way to filter for entries from the right domain
389*1fcced4cSJordan Brown 	 * in the LDAP query, so we check for it here.  Searches based on
390*1fcced4cSJordan Brown 	 * samAccountName might yield results from the wrong domain.
391*1fcced4cSJordan Brown 	 */
392*1fcced4cSJordan Brown 	de = get_domain(ld, msg, &domain);
393*1fcced4cSJordan Brown 	if (de != NULL)
394*1fcced4cSJordan Brown 		goto err;
395*1fcced4cSJordan Brown 
396*1fcced4cSJordan Brown 	if (cbinfo->domain != NULL && !domain_eq(cbinfo->domain, domain))
397*1fcced4cSJordan Brown 		goto out;
398*1fcced4cSJordan Brown 
399*1fcced4cSJordan Brown 	/*
400*1fcced4cSJordan Brown 	 * If we've already found a match, error.
401*1fcced4cSJordan Brown 	 */
402*1fcced4cSJordan Brown 	if (pent->status != DIRECTORY_NOT_FOUND) {
403*1fcced4cSJordan Brown 		de = directory_error("Duplicate.AD",
404*1fcced4cSJordan Brown 		    "Multiple matching entries found", NULL);
405*1fcced4cSJordan Brown 		goto err;
406*1fcced4cSJordan Brown 	}
407*1fcced4cSJordan Brown 
408*1fcced4cSJordan Brown 	llvals = calloc(nattrs, sizeof (directory_values_rpc));
409*1fcced4cSJordan Brown 	if (llvals == NULL)
410*1fcced4cSJordan Brown 		goto nomem;
411*1fcced4cSJordan Brown 
412*1fcced4cSJordan Brown 	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
413*1fcced4cSJordan Brown 	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
414*1fcced4cSJordan Brown 	pent->status = DIRECTORY_FOUND;
415*1fcced4cSJordan Brown 
416*1fcced4cSJordan Brown 	for (i = 0; i < nattrs; i++) {
417*1fcced4cSJordan Brown 		struct berval **bv;
418*1fcced4cSJordan Brown 		const char *a = attrs[i];
419*1fcced4cSJordan Brown 		directory_values_rpc *val = &llvals[i];
420*1fcced4cSJordan Brown 
421*1fcced4cSJordan Brown 		bv = ldap_get_values_len(ld, msg, a);
422*1fcced4cSJordan Brown #if	defined(DUMP_VALUES)
423*1fcced4cSJordan Brown 		dump_bv_list(attrs[i], bv);
424*1fcced4cSJordan Brown #endif
425*1fcced4cSJordan Brown 		if (bv != NULL) {
426*1fcced4cSJordan Brown 			de = bv_list_dav(val, bv);
427*1fcced4cSJordan Brown 			ldap_value_free_len(bv);
428*1fcced4cSJordan Brown 			if (de != NULL)
429*1fcced4cSJordan Brown 				goto err;
430*1fcced4cSJordan Brown 		} else if (strcaseeq(a, "x-sun-canonicalName")) {
431*1fcced4cSJordan Brown 			bv = ldap_get_values_len(ld, msg, "sAMAccountName");
432*1fcced4cSJordan Brown 			if (bv != NULL) {
433*1fcced4cSJordan Brown 				int n = ldap_count_values_len(bv);
434*1fcced4cSJordan Brown 				if (n > 0) {
435*1fcced4cSJordan Brown 					char *tmp;
436*1fcced4cSJordan Brown 					(void) asprintf(&tmp, "%.*s@%s",
437*1fcced4cSJordan Brown 					    bv[0]->bv_len, bv[0]->bv_val,
438*1fcced4cSJordan Brown 					    domain);
439*1fcced4cSJordan Brown 					if (tmp == NULL)
440*1fcced4cSJordan Brown 						goto nomem;
441*1fcced4cSJordan Brown 					const char *ctmp = tmp;
442*1fcced4cSJordan Brown 					de = str_list_dav(val, &ctmp, 1);
443*1fcced4cSJordan Brown 					free(tmp);
444*1fcced4cSJordan Brown 					if (de != NULL)
445*1fcced4cSJordan Brown 						goto err;
446*1fcced4cSJordan Brown 				}
447*1fcced4cSJordan Brown 			}
448*1fcced4cSJordan Brown 		} else if (strcaseeq(a, "x-sun-provider")) {
449*1fcced4cSJordan Brown 			const char *provider = "LDAP-AD";
450*1fcced4cSJordan Brown 			de = str_list_dav(val, &provider, 1);
451*1fcced4cSJordan Brown 		}
452*1fcced4cSJordan Brown 	}
453*1fcced4cSJordan Brown 
454*1fcced4cSJordan Brown 	goto out;
455*1fcced4cSJordan Brown 
456*1fcced4cSJordan Brown nomem:
457*1fcced4cSJordan Brown 	de = directory_error("ENOMEM.users",
458*1fcced4cSJordan Brown 	    "No memory allocating return value for user lookup", NULL);
459*1fcced4cSJordan Brown 
460*1fcced4cSJordan Brown err:
461*1fcced4cSJordan Brown 	directory_entry_set_error(pent, de);
462*1fcced4cSJordan Brown 	de = NULL;
463*1fcced4cSJordan Brown 
464*1fcced4cSJordan Brown out:
465*1fcced4cSJordan Brown 	free(domain);
466*1fcced4cSJordan Brown }
467*1fcced4cSJordan Brown 
468*1fcced4cSJordan Brown /*
469*1fcced4cSJordan Brown  * Given a struct berval, populate a directory attribute value (which is a
470*1fcced4cSJordan Brown  * list of values).
471*1fcced4cSJordan Brown  * Note that here we populate the DAV with the exact bytes that LDAP returns.
472*1fcced4cSJordan Brown  * Back over in the client it appends a \0 so that strings are null
473*1fcced4cSJordan Brown  * terminated.
474*1fcced4cSJordan Brown  */
475*1fcced4cSJordan Brown static
476*1fcced4cSJordan Brown directory_error_t
477*1fcced4cSJordan Brown bv_list_dav(directory_values_rpc *lvals, struct berval **bv)
478*1fcced4cSJordan Brown {
479*1fcced4cSJordan Brown 	directory_value_rpc *dav;
480*1fcced4cSJordan Brown 	int n;
481*1fcced4cSJordan Brown 	int i;
482*1fcced4cSJordan Brown 
483*1fcced4cSJordan Brown 	n = ldap_count_values_len(bv);
484*1fcced4cSJordan Brown 
485*1fcced4cSJordan Brown 	dav = calloc(n, sizeof (directory_value_rpc));
486*1fcced4cSJordan Brown 	if (dav == NULL)
487*1fcced4cSJordan Brown 		goto nomem;
488*1fcced4cSJordan Brown 
489*1fcced4cSJordan Brown 	lvals->directory_values_rpc_u.values.values_val = dav;
490*1fcced4cSJordan Brown 	lvals->directory_values_rpc_u.values.values_len = n;
491*1fcced4cSJordan Brown 	lvals->found = TRUE;
492*1fcced4cSJordan Brown 
493*1fcced4cSJordan Brown 	for (i = 0; i < n; i++) {
494*1fcced4cSJordan Brown 		dav[i].directory_value_rpc_val =
495*1fcced4cSJordan Brown 		    memdup(bv[i]->bv_val, bv[i]->bv_len);
496*1fcced4cSJordan Brown 		if (dav[i].directory_value_rpc_val == NULL)
497*1fcced4cSJordan Brown 			goto nomem;
498*1fcced4cSJordan Brown 		dav[i].directory_value_rpc_len = bv[i]->bv_len;
499*1fcced4cSJordan Brown 	}
500*1fcced4cSJordan Brown 
501*1fcced4cSJordan Brown 	return (NULL);
502*1fcced4cSJordan Brown 
503*1fcced4cSJordan Brown nomem:
504*1fcced4cSJordan Brown 	return (directory_error("ENOMEM.bv_list_dav",
505*1fcced4cSJordan Brown 	    "Insufficient memory copying values"));
506*1fcced4cSJordan Brown }
507*1fcced4cSJordan Brown 
508*1fcced4cSJordan Brown #if	defined(DUMP_VALUES)
509*1fcced4cSJordan Brown static
510*1fcced4cSJordan Brown void
511*1fcced4cSJordan Brown dump_bv_list(const char *attr, struct berval **bv)
512*1fcced4cSJordan Brown {
513*1fcced4cSJordan Brown 	int i;
514*1fcced4cSJordan Brown 
515*1fcced4cSJordan Brown 	if (bv == NULL) {
516*1fcced4cSJordan Brown 		(void) fprintf(stderr, "%s:  (empty)\n", attr);
517*1fcced4cSJordan Brown 		return;
518*1fcced4cSJordan Brown 	}
519*1fcced4cSJordan Brown 	for (i = 0; bv[i] != NULL; i++) {
520*1fcced4cSJordan Brown 		(void) fprintf(stderr, "%s[%d] =\n", attr, i);
521*1fcced4cSJordan Brown 		dump(stderr, "    ", bv[i]->bv_val, bv[i]->bv_len);
522*1fcced4cSJordan Brown 	}
523*1fcced4cSJordan Brown }
524*1fcced4cSJordan Brown #endif	/* DUMP_VALUES */
525*1fcced4cSJordan Brown 
526*1fcced4cSJordan Brown /*
527*1fcced4cSJordan Brown  * Return the domain associated with the specified entry.
528*1fcced4cSJordan Brown  */
529*1fcced4cSJordan Brown static
530*1fcced4cSJordan Brown directory_error_t
531*1fcced4cSJordan Brown get_domain(
532*1fcced4cSJordan Brown     LDAP *ld,
533*1fcced4cSJordan Brown     LDAPMessage *msg,
534*1fcced4cSJordan Brown     char **domain)
535*1fcced4cSJordan Brown {
536*1fcced4cSJordan Brown 	*domain = NULL;
537*1fcced4cSJordan Brown 
538*1fcced4cSJordan Brown 	char *dn = ldap_get_dn(ld, msg);
539*1fcced4cSJordan Brown 	if (dn == NULL) {
540*1fcced4cSJordan Brown 		char buf[100];	/* big enough for any int */
541*1fcced4cSJordan Brown 		char *m;
542*1fcced4cSJordan Brown 		char *s;
543*1fcced4cSJordan Brown 		int err = ldap_get_lderrno(ld, &m, &s);
544*1fcced4cSJordan Brown 		(void) snprintf(buf, sizeof (buf), "%d", err);
545*1fcced4cSJordan Brown 
546*1fcced4cSJordan Brown 		return directory_error("AD.get_domain.ldap_get_dn",
547*1fcced4cSJordan Brown 		    "ldap_get_dn: %1 (%2)\n"
548*1fcced4cSJordan Brown 		    "matched: %3\n"
549*1fcced4cSJordan Brown 		    "error:   %4",
550*1fcced4cSJordan Brown 		    ldap_err2string(err), buf,
551*1fcced4cSJordan Brown 		    m == NULL ? "(null)" : m,
552*1fcced4cSJordan Brown 		    s == NULL ? "(null)" : s,
553*1fcced4cSJordan Brown 		    NULL);
554*1fcced4cSJordan Brown 	}
555*1fcced4cSJordan Brown 
556*1fcced4cSJordan Brown 	*domain = adutils_dn2dns(dn);
557*1fcced4cSJordan Brown 	if (*domain == NULL) {
558*1fcced4cSJordan Brown 		directory_error_t de;
559*1fcced4cSJordan Brown 
560*1fcced4cSJordan Brown 		de = directory_error("Unknown.get_domain.adutils_dn2dns",
561*1fcced4cSJordan Brown 		    "get_domain:  Unexpected error from adutils_dn2dns(%1)",
562*1fcced4cSJordan Brown 		    dn, NULL);
563*1fcced4cSJordan Brown 		free(dn);
564*1fcced4cSJordan Brown 		return (de);
565*1fcced4cSJordan Brown 	}
566*1fcced4cSJordan Brown 	free(dn);
567*1fcced4cSJordan Brown 
568*1fcced4cSJordan Brown 	return (NULL);
569*1fcced4cSJordan Brown }
570*1fcced4cSJordan Brown 
571*1fcced4cSJordan Brown /*
572*1fcced4cSJordan Brown  * Given an error report from libadutils, generate a directory_error_t.
573*1fcced4cSJordan Brown  */
574*1fcced4cSJordan Brown static
575*1fcced4cSJordan Brown directory_error_t
576*1fcced4cSJordan Brown directory_provider_ad_utils_error(char *func, int rc)
577*1fcced4cSJordan Brown {
578*1fcced4cSJordan Brown 	char rcstr[100];	/* plenty for any int */
579*1fcced4cSJordan Brown 	char code[100];		/* plenty for any int */
580*1fcced4cSJordan Brown 	(void) snprintf(rcstr, sizeof (rcstr), "%d", rc);
581*1fcced4cSJordan Brown 	(void) snprintf(code, sizeof (code), "ADUTILS.%d", rc);
582*1fcced4cSJordan Brown 
583*1fcced4cSJordan Brown 	return (directory_error(code,
584*1fcced4cSJordan Brown 	    "Error %2 from adutils function %1", func, rcstr, NULL));
585*1fcced4cSJordan Brown }
586*1fcced4cSJordan Brown 
587*1fcced4cSJordan Brown struct directory_provider_static directory_provider_ad = {
588*1fcced4cSJordan Brown 	"AD",
589*1fcced4cSJordan Brown 	directory_provider_ad_get,
590*1fcced4cSJordan Brown };
591