xref: /onnv-gate/usr/src/cmd/idmap/idmapd/directory_provider_ad.c (revision 12890:16985853e3aa)
110122SJordan.Brown@Sun.COM /*
210122SJordan.Brown@Sun.COM  * CDDL HEADER START
310122SJordan.Brown@Sun.COM  *
410122SJordan.Brown@Sun.COM  * The contents of this file are subject to the terms of the
510122SJordan.Brown@Sun.COM  * Common Development and Distribution License (the "License").
610122SJordan.Brown@Sun.COM  * You may not use this file except in compliance with the License.
710122SJordan.Brown@Sun.COM  *
810122SJordan.Brown@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910122SJordan.Brown@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010122SJordan.Brown@Sun.COM  * See the License for the specific language governing permissions
1110122SJordan.Brown@Sun.COM  * and limitations under the License.
1210122SJordan.Brown@Sun.COM  *
1310122SJordan.Brown@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410122SJordan.Brown@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510122SJordan.Brown@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610122SJordan.Brown@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710122SJordan.Brown@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810122SJordan.Brown@Sun.COM  *
1910122SJordan.Brown@Sun.COM  * CDDL HEADER END
2010122SJordan.Brown@Sun.COM  */
2110122SJordan.Brown@Sun.COM 
2210122SJordan.Brown@Sun.COM /*
23*12890SJoyce.McIntosh@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410122SJordan.Brown@Sun.COM  */
2510122SJordan.Brown@Sun.COM 
2610122SJordan.Brown@Sun.COM /*
2710122SJordan.Brown@Sun.COM  * Retrieve directory information for Active Directory users.
2810122SJordan.Brown@Sun.COM  */
2910122SJordan.Brown@Sun.COM 
3010122SJordan.Brown@Sun.COM #include <ldap.h>
3110122SJordan.Brown@Sun.COM #include <lber.h>
3210122SJordan.Brown@Sun.COM #include <pwd.h>
3310122SJordan.Brown@Sun.COM #include <malloc.h>
3410122SJordan.Brown@Sun.COM #include <string.h>
3510122SJordan.Brown@Sun.COM #include <stdlib.h>
3610122SJordan.Brown@Sun.COM #include <netdb.h>
3710122SJordan.Brown@Sun.COM #include <libadutils.h>
38*12890SJoyce.McIntosh@Sun.COM #include <libuutil.h>
3910122SJordan.Brown@Sun.COM #include <note.h>
4010122SJordan.Brown@Sun.COM #include <assert.h>
4110122SJordan.Brown@Sun.COM #include "directory.h"
4210122SJordan.Brown@Sun.COM #include "directory_private.h"
4310122SJordan.Brown@Sun.COM #include "idmapd.h"
4410122SJordan.Brown@Sun.COM #include <rpcsvc/idmap_prot.h>
4510122SJordan.Brown@Sun.COM #include "directory_server_impl.h"
4610122SJordan.Brown@Sun.COM 
4710122SJordan.Brown@Sun.COM /*
4810122SJordan.Brown@Sun.COM  * Information required by the function that handles the callback from LDAP
4910122SJordan.Brown@Sun.COM  * when responses are received.
5010122SJordan.Brown@Sun.COM  */
5110122SJordan.Brown@Sun.COM struct cbinfo {
5210122SJordan.Brown@Sun.COM 	const char * const *attrs;
5310122SJordan.Brown@Sun.COM 	int nattrs;
5410122SJordan.Brown@Sun.COM 	directory_entry_rpc *entry;
5510122SJordan.Brown@Sun.COM 	const char *domain;
5610122SJordan.Brown@Sun.COM };
5710122SJordan.Brown@Sun.COM 
5810122SJordan.Brown@Sun.COM static void directory_provider_ad_cb(LDAP *ld, LDAPMessage **ldapres, int rc,
5910122SJordan.Brown@Sun.COM     int qid, void *argp);
6010122SJordan.Brown@Sun.COM static void directory_provider_ad_cb1(LDAP *ld, LDAPMessage *msg,
6110122SJordan.Brown@Sun.COM     struct cbinfo *cbinfo);
6210122SJordan.Brown@Sun.COM static directory_error_t bv_list_dav(directory_values_rpc *lvals,
6310122SJordan.Brown@Sun.COM     struct berval **bv);
6410122SJordan.Brown@Sun.COM static directory_error_t directory_provider_ad_lookup(
6510122SJordan.Brown@Sun.COM     directory_entry_rpc *pent, const char * const * attrs, int nattrs,
6610122SJordan.Brown@Sun.COM     const char *domain, const char *filter);
6710122SJordan.Brown@Sun.COM static directory_error_t get_domain(LDAP *ld, LDAPMessage *ldapres,
6810122SJordan.Brown@Sun.COM     char **domain);
6910122SJordan.Brown@Sun.COM static directory_error_t directory_provider_ad_utils_error(char *func, int rc);
7010122SJordan.Brown@Sun.COM 
7110122SJordan.Brown@Sun.COM #if	defined(DUMP_VALUES)
7210122SJordan.Brown@Sun.COM static void dump_bv_list(const char *attr, struct berval **bv);
7310122SJordan.Brown@Sun.COM #endif
7410122SJordan.Brown@Sun.COM 
7510122SJordan.Brown@Sun.COM #define	MAX_EXTRA_ATTRS	1	/* sAMAccountName */
7610122SJordan.Brown@Sun.COM 
7710122SJordan.Brown@Sun.COM /*
7810122SJordan.Brown@Sun.COM  * Add an entry to a NULL-terminated list, if it's not already there.
7910122SJordan.Brown@Sun.COM  * Assumes that the list has been allocated large enough for all additions,
8010122SJordan.Brown@Sun.COM  * and prefilled with NULL.
8110122SJordan.Brown@Sun.COM  */
8210122SJordan.Brown@Sun.COM static
8310122SJordan.Brown@Sun.COM void
maybe_add_to_list(const char ** list,const char * s)8410122SJordan.Brown@Sun.COM maybe_add_to_list(const char **list, const char *s)
8510122SJordan.Brown@Sun.COM {
8610122SJordan.Brown@Sun.COM 	for (; *list != NULL; list++) {
87*12890SJoyce.McIntosh@Sun.COM 		if (uu_strcaseeq(*list, s))
8810122SJordan.Brown@Sun.COM 			return;
8910122SJordan.Brown@Sun.COM 	}
9010122SJordan.Brown@Sun.COM 	*list = s;
9110122SJordan.Brown@Sun.COM }
9210122SJordan.Brown@Sun.COM 
9310122SJordan.Brown@Sun.COM /*
9410122SJordan.Brown@Sun.COM  * Copy a counted attribute list to a NULL-terminated one.
9510122SJordan.Brown@Sun.COM  * In the process, examine the requested attributes and augment
9610122SJordan.Brown@Sun.COM  * the list as required to support any synthesized attributes
9710122SJordan.Brown@Sun.COM  * requested.
9810122SJordan.Brown@Sun.COM  */
9910122SJordan.Brown@Sun.COM static
10010122SJordan.Brown@Sun.COM const char **
copy_and_augment_attr_list(char ** req_list,int req_list_len)10110122SJordan.Brown@Sun.COM copy_and_augment_attr_list(char **req_list, int req_list_len)
10210122SJordan.Brown@Sun.COM {
10310122SJordan.Brown@Sun.COM 	const char **new_list;
10410122SJordan.Brown@Sun.COM 	int i;
10510122SJordan.Brown@Sun.COM 
10610122SJordan.Brown@Sun.COM 	new_list =
10710122SJordan.Brown@Sun.COM 	    calloc(req_list_len + MAX_EXTRA_ATTRS + 1, sizeof (*new_list));
10810122SJordan.Brown@Sun.COM 	if (new_list == NULL)
10910122SJordan.Brown@Sun.COM 		return (NULL);
11010122SJordan.Brown@Sun.COM 
11110122SJordan.Brown@Sun.COM 	(void) memcpy(new_list, req_list, req_list_len * sizeof (char *));
11210122SJordan.Brown@Sun.COM 
11310122SJordan.Brown@Sun.COM 	for (i = 0; i < req_list_len; i++) {
11410122SJordan.Brown@Sun.COM 		const char *a = req_list[i];
11510122SJordan.Brown@Sun.COM 		/*
11610122SJordan.Brown@Sun.COM 		 * Note that you must update MAX_EXTRA_ATTRS above if you
11710122SJordan.Brown@Sun.COM 		 * add to this list.
11810122SJordan.Brown@Sun.COM 		 */
119*12890SJoyce.McIntosh@Sun.COM 		if (uu_strcaseeq(a, "x-sun-canonicalName")) {
12010122SJordan.Brown@Sun.COM 			maybe_add_to_list(new_list, "sAMAccountName");
12110122SJordan.Brown@Sun.COM 			continue;
12210122SJordan.Brown@Sun.COM 		}
12310122SJordan.Brown@Sun.COM 		/* None needed for x-sun-provider */
12410122SJordan.Brown@Sun.COM 	}
12510122SJordan.Brown@Sun.COM 
12610122SJordan.Brown@Sun.COM 	return (new_list);
12710122SJordan.Brown@Sun.COM }
12810122SJordan.Brown@Sun.COM 
12910122SJordan.Brown@Sun.COM /*
13010122SJordan.Brown@Sun.COM  * Retrieve information by name.
13110122SJordan.Brown@Sun.COM  * Called indirectly through the Directory_provider_static structure.
13210122SJordan.Brown@Sun.COM  */
13310122SJordan.Brown@Sun.COM static
13410122SJordan.Brown@Sun.COM directory_error_t
directory_provider_ad_get(directory_entry_rpc * del,idmap_utf8str_list * ids,char * types,idmap_utf8str_list * attrs)13510122SJordan.Brown@Sun.COM directory_provider_ad_get(
13610122SJordan.Brown@Sun.COM     directory_entry_rpc *del,
13710122SJordan.Brown@Sun.COM     idmap_utf8str_list *ids,
13810122SJordan.Brown@Sun.COM     char *types,
13910122SJordan.Brown@Sun.COM     idmap_utf8str_list *attrs)
14010122SJordan.Brown@Sun.COM {
14110122SJordan.Brown@Sun.COM 	int i;
14210122SJordan.Brown@Sun.COM 	const char **attrs2;
14310122SJordan.Brown@Sun.COM 	directory_error_t de = NULL;
14410122SJordan.Brown@Sun.COM 
14510122SJordan.Brown@Sun.COM 	/*
14610122SJordan.Brown@Sun.COM 	 * If we don't have any AD servers handy, we can't find anything.
14710122SJordan.Brown@Sun.COM 	 */
14810504SKeyur.Desai@Sun.COM 	if (_idmapdstate.num_gcs < 1) {
14910122SJordan.Brown@Sun.COM 		return (NULL);
15010122SJordan.Brown@Sun.COM 	}
15110122SJordan.Brown@Sun.COM 
15210122SJordan.Brown@Sun.COM 	RDLOCK_CONFIG()
15310122SJordan.Brown@Sun.COM 
15410122SJordan.Brown@Sun.COM 	/* 6835280 spurious lint error if the strlen is in the declaration */
15510122SJordan.Brown@Sun.COM 	int len = strlen(_idmapdstate.cfg->pgcfg.default_domain);
15610122SJordan.Brown@Sun.COM 	char default_domain[len + 1];
15710122SJordan.Brown@Sun.COM 	(void) strcpy(default_domain, _idmapdstate.cfg->pgcfg.default_domain);
15810122SJordan.Brown@Sun.COM 
15910122SJordan.Brown@Sun.COM 	UNLOCK_CONFIG();
16010122SJordan.Brown@Sun.COM 
16110122SJordan.Brown@Sun.COM 	/*
16210122SJordan.Brown@Sun.COM 	 * Turn our counted-array argument into a NULL-terminated array.
16310122SJordan.Brown@Sun.COM 	 * At the same time, add in any attributes that we need to support
16410122SJordan.Brown@Sun.COM 	 * any requested synthesized attributes.
16510122SJordan.Brown@Sun.COM 	 */
16610122SJordan.Brown@Sun.COM 	attrs2 = copy_and_augment_attr_list(attrs->idmap_utf8str_list_val,
16710122SJordan.Brown@Sun.COM 	    attrs->idmap_utf8str_list_len);
16810122SJordan.Brown@Sun.COM 	if (attrs2 == NULL)
16910122SJordan.Brown@Sun.COM 		goto nomem;
17010122SJordan.Brown@Sun.COM 
17110122SJordan.Brown@Sun.COM 	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
17210122SJordan.Brown@Sun.COM 		char *vw[3];
17310122SJordan.Brown@Sun.COM 		int type;
17410122SJordan.Brown@Sun.COM 
17510122SJordan.Brown@Sun.COM 		/*
17610122SJordan.Brown@Sun.COM 		 * Extract the type for this particular ID.
17710122SJordan.Brown@Sun.COM 		 * Advance to the next type, if it's there, else keep
17810122SJordan.Brown@Sun.COM 		 * using this type until we run out of IDs.
17910122SJordan.Brown@Sun.COM 		 */
18010122SJordan.Brown@Sun.COM 		type = *types;
18110122SJordan.Brown@Sun.COM 		if (*(types+1) != '\0')
18210122SJordan.Brown@Sun.COM 			types++;
18310122SJordan.Brown@Sun.COM 
18410122SJordan.Brown@Sun.COM 		/*
18510122SJordan.Brown@Sun.COM 		 * If this entry has already been handled, one way or another,
18610122SJordan.Brown@Sun.COM 		 * skip it.
18710122SJordan.Brown@Sun.COM 		 */
18810122SJordan.Brown@Sun.COM 		if (del[i].status != DIRECTORY_NOT_FOUND)
18910122SJordan.Brown@Sun.COM 			continue;
19010122SJordan.Brown@Sun.COM 
19110122SJordan.Brown@Sun.COM 		char *id = ids->idmap_utf8str_list_val[i];
19210122SJordan.Brown@Sun.COM 
19310122SJordan.Brown@Sun.COM 		/*
19410122SJordan.Brown@Sun.COM 		 * Allow for expanding every character to \xx, plus some
19510122SJordan.Brown@Sun.COM 		 * space for the query syntax.
19610122SJordan.Brown@Sun.COM 		 */
19710122SJordan.Brown@Sun.COM 		int id_len = strlen(id);
19810122SJordan.Brown@Sun.COM 		char filter[1000 + id_len*3];
19910122SJordan.Brown@Sun.COM 
20010122SJordan.Brown@Sun.COM 		if (type == DIRECTORY_ID_SID[0]) {
20110122SJordan.Brown@Sun.COM 			/*
20210122SJordan.Brown@Sun.COM 			 * Mildly surprisingly, AD appears to allow searching
20310122SJordan.Brown@Sun.COM 			 * based on text SIDs.  Must be a special case on the
20410122SJordan.Brown@Sun.COM 			 * server end.
20510122SJordan.Brown@Sun.COM 			 */
20610122SJordan.Brown@Sun.COM 			ldap_build_filter(filter, sizeof (filter),
20710122SJordan.Brown@Sun.COM 			    "(objectSid=%v)", NULL, NULL, NULL, id, NULL);
20810122SJordan.Brown@Sun.COM 
20910122SJordan.Brown@Sun.COM 			de = directory_provider_ad_lookup(&del[i], attrs2,
21010122SJordan.Brown@Sun.COM 			    attrs->idmap_utf8str_list_len, NULL, filter);
21110122SJordan.Brown@Sun.COM 			if (de != NULL) {
21210122SJordan.Brown@Sun.COM 				directory_entry_set_error(&del[i], de);
21310122SJordan.Brown@Sun.COM 				de = NULL;
21410122SJordan.Brown@Sun.COM 			}
21510122SJordan.Brown@Sun.COM 		} else {
21610122SJordan.Brown@Sun.COM 			int id_len = strlen(id);
21710122SJordan.Brown@Sun.COM 			char name[id_len + 1];
21810122SJordan.Brown@Sun.COM 			char domain[id_len + 1];
21910122SJordan.Brown@Sun.COM 
22010122SJordan.Brown@Sun.COM 			split_name(name, domain, id);
22110122SJordan.Brown@Sun.COM 
22210122SJordan.Brown@Sun.COM 			vw[0] = name;
22310122SJordan.Brown@Sun.COM 
224*12890SJoyce.McIntosh@Sun.COM 			if (uu_streq(domain, "")) {
22510122SJordan.Brown@Sun.COM 				vw[1] = default_domain;
22610122SJordan.Brown@Sun.COM 			} else {
22710122SJordan.Brown@Sun.COM 				vw[1] = domain;
22810122SJordan.Brown@Sun.COM 			}
22910122SJordan.Brown@Sun.COM 
23010122SJordan.Brown@Sun.COM 			if (type == DIRECTORY_ID_USER[0])
23110122SJordan.Brown@Sun.COM 				vw[2] = "user";
23210122SJordan.Brown@Sun.COM 			else if (type == DIRECTORY_ID_GROUP[0])
23310122SJordan.Brown@Sun.COM 				vw[2] = "group";
23410122SJordan.Brown@Sun.COM 			else
23510122SJordan.Brown@Sun.COM 				vw[2] = "*";
23610122SJordan.Brown@Sun.COM 
23710122SJordan.Brown@Sun.COM 			/*
23810122SJordan.Brown@Sun.COM 			 * Try samAccountName.
23910122SJordan.Brown@Sun.COM 			 * Note that here we rely on checking the returned
24010122SJordan.Brown@Sun.COM 			 * distinguishedName to make sure that we found an
24110122SJordan.Brown@Sun.COM 			 * entry from the right domain, because there's no
24210122SJordan.Brown@Sun.COM 			 * attribute we can straightforwardly filter for to
24310122SJordan.Brown@Sun.COM 			 * match domain.
24410122SJordan.Brown@Sun.COM 			 *
24510122SJordan.Brown@Sun.COM 			 * Eventually we should perhaps also try
24610122SJordan.Brown@Sun.COM 			 * userPrincipalName.
24710122SJordan.Brown@Sun.COM 			 */
24810122SJordan.Brown@Sun.COM 			ldap_build_filter(filter, sizeof (filter),
24910122SJordan.Brown@Sun.COM 			    "(&(samAccountName=%v1)(objectClass=%v3))",
25010122SJordan.Brown@Sun.COM 			    NULL, NULL, NULL, NULL, vw);
25110122SJordan.Brown@Sun.COM 
25210122SJordan.Brown@Sun.COM 			de = directory_provider_ad_lookup(&del[i], attrs2,
25310122SJordan.Brown@Sun.COM 			    attrs->idmap_utf8str_list_len, vw[1], filter);
25410122SJordan.Brown@Sun.COM 			if (de != NULL) {
25510122SJordan.Brown@Sun.COM 				directory_entry_set_error(&del[i], de);
25610122SJordan.Brown@Sun.COM 				de = NULL;
25710122SJordan.Brown@Sun.COM 			}
25810122SJordan.Brown@Sun.COM 		}
25910122SJordan.Brown@Sun.COM 	}
26010122SJordan.Brown@Sun.COM 
26110122SJordan.Brown@Sun.COM 	de = NULL;
26210122SJordan.Brown@Sun.COM 
26310122SJordan.Brown@Sun.COM 	goto out;
26410122SJordan.Brown@Sun.COM 
26510122SJordan.Brown@Sun.COM nomem:
26610122SJordan.Brown@Sun.COM 	de = directory_error("ENOMEM.AD",
26710122SJordan.Brown@Sun.COM 	    "Out of memory during AD lookup", NULL);
26810122SJordan.Brown@Sun.COM out:
26910122SJordan.Brown@Sun.COM 	free(attrs2);
27010122SJordan.Brown@Sun.COM 	return (de);
27110122SJordan.Brown@Sun.COM }
27210122SJordan.Brown@Sun.COM 
27310122SJordan.Brown@Sun.COM /*
27410122SJordan.Brown@Sun.COM  * Note that attrs is NULL terminated, and that nattrs is the number
27510122SJordan.Brown@Sun.COM  * of attributes requested by the user... which might be fewer than are
27610122SJordan.Brown@Sun.COM  * in attrs because of attributes that we need for our own processing.
27710122SJordan.Brown@Sun.COM  */
27810122SJordan.Brown@Sun.COM static
27910122SJordan.Brown@Sun.COM directory_error_t
directory_provider_ad_lookup(directory_entry_rpc * pent,const char * const * attrs,int nattrs,const char * domain,const char * filter)28010122SJordan.Brown@Sun.COM directory_provider_ad_lookup(
28110122SJordan.Brown@Sun.COM     directory_entry_rpc *pent,
28210122SJordan.Brown@Sun.COM     const char * const * attrs,
28310122SJordan.Brown@Sun.COM     int nattrs,
28410122SJordan.Brown@Sun.COM     const char *domain,
28510122SJordan.Brown@Sun.COM     const char *filter)
28610122SJordan.Brown@Sun.COM {
28710122SJordan.Brown@Sun.COM 	adutils_ad_t *ad;
28810122SJordan.Brown@Sun.COM 	adutils_rc batchrc;
28910122SJordan.Brown@Sun.COM 	struct cbinfo cbinfo;
29010122SJordan.Brown@Sun.COM 	adutils_query_state_t *qs;
29110122SJordan.Brown@Sun.COM 	int rc;
29210122SJordan.Brown@Sun.COM 
29310122SJordan.Brown@Sun.COM 	/*
29410122SJordan.Brown@Sun.COM 	 * NEEDSWORK:  Should eventually handle other forests.
29510122SJordan.Brown@Sun.COM 	 * NEEDSWORK:  Should eventually handle non-GC attributes.
29610122SJordan.Brown@Sun.COM 	 */
29710504SKeyur.Desai@Sun.COM 	ad = _idmapdstate.gcs[0];
29810122SJordan.Brown@Sun.COM 
29910122SJordan.Brown@Sun.COM 	/* Stash away information for the callback function. */
30010122SJordan.Brown@Sun.COM 	cbinfo.attrs = attrs;
30110122SJordan.Brown@Sun.COM 	cbinfo.nattrs = nattrs;
30210122SJordan.Brown@Sun.COM 	cbinfo.entry = pent;
30310122SJordan.Brown@Sun.COM 	cbinfo.domain = domain;
30410122SJordan.Brown@Sun.COM 
30510122SJordan.Brown@Sun.COM 	rc = adutils_lookup_batch_start(ad, 1, directory_provider_ad_cb,
30610122SJordan.Brown@Sun.COM 	    &cbinfo, &qs);
30710122SJordan.Brown@Sun.COM 	if (rc != ADUTILS_SUCCESS) {
30810122SJordan.Brown@Sun.COM 		return (directory_provider_ad_utils_error(
30910122SJordan.Brown@Sun.COM 		    "adutils_lookup_batch_start", rc));
31010122SJordan.Brown@Sun.COM 	}
31110122SJordan.Brown@Sun.COM 
31210122SJordan.Brown@Sun.COM 	rc = adutils_lookup_batch_add(qs, filter, attrs, domain,
31310122SJordan.Brown@Sun.COM 	    NULL, &batchrc);
31410122SJordan.Brown@Sun.COM 	if (rc != ADUTILS_SUCCESS) {
31510122SJordan.Brown@Sun.COM 		adutils_lookup_batch_release(&qs);
31610122SJordan.Brown@Sun.COM 		return (directory_provider_ad_utils_error(
31710122SJordan.Brown@Sun.COM 		    "adutils_lookup_batch_add", rc));
31810122SJordan.Brown@Sun.COM 	}
31910122SJordan.Brown@Sun.COM 
32010122SJordan.Brown@Sun.COM 	rc = adutils_lookup_batch_end(&qs);
32110122SJordan.Brown@Sun.COM 	if (rc != ADUTILS_SUCCESS) {
32210122SJordan.Brown@Sun.COM 		return (directory_provider_ad_utils_error(
32310122SJordan.Brown@Sun.COM 		    "adutils_lookup_batch_end", rc));
32410122SJordan.Brown@Sun.COM 	}
32510122SJordan.Brown@Sun.COM 
32610122SJordan.Brown@Sun.COM 	if (batchrc != ADUTILS_SUCCESS) {
32710122SJordan.Brown@Sun.COM 		/*
32810122SJordan.Brown@Sun.COM 		 * NEEDSWORK:  We're consistently getting -9997 here.
32910122SJordan.Brown@Sun.COM 		 * What does it mean?
33010122SJordan.Brown@Sun.COM 		 */
33110122SJordan.Brown@Sun.COM 		return (NULL);
33210122SJordan.Brown@Sun.COM 	}
33310122SJordan.Brown@Sun.COM 
33410122SJordan.Brown@Sun.COM 	return (NULL);
33510122SJordan.Brown@Sun.COM }
33610122SJordan.Brown@Sun.COM 
33710122SJordan.Brown@Sun.COM /*
33810122SJordan.Brown@Sun.COM  * Callback from the LDAP functions when they get responses.
33910122SJordan.Brown@Sun.COM  * We don't really need (nor want) asynchronous handling, but it's
34010122SJordan.Brown@Sun.COM  * what libadutils gives us.
34110122SJordan.Brown@Sun.COM  */
34210122SJordan.Brown@Sun.COM static
34310122SJordan.Brown@Sun.COM void
directory_provider_ad_cb(LDAP * ld,LDAPMessage ** ldapres,int rc,int qid,void * argp)34410122SJordan.Brown@Sun.COM directory_provider_ad_cb(
34510122SJordan.Brown@Sun.COM     LDAP *ld,
34610122SJordan.Brown@Sun.COM     LDAPMessage **ldapres,
34710122SJordan.Brown@Sun.COM     int rc,
34810122SJordan.Brown@Sun.COM     int qid,
34910122SJordan.Brown@Sun.COM     void *argp)
35010122SJordan.Brown@Sun.COM {
35110122SJordan.Brown@Sun.COM 	NOTE(ARGUNUSED(rc, qid))
35210122SJordan.Brown@Sun.COM 	struct cbinfo *cbinfo = (struct cbinfo *)argp;
35310122SJordan.Brown@Sun.COM 	LDAPMessage *msg = *ldapres;
35410122SJordan.Brown@Sun.COM 
35510122SJordan.Brown@Sun.COM 	for (msg = ldap_first_entry(ld, msg);
35610122SJordan.Brown@Sun.COM 	    msg != NULL;
35710122SJordan.Brown@Sun.COM 	    msg = ldap_next_entry(ld, msg)) {
35810122SJordan.Brown@Sun.COM 		directory_provider_ad_cb1(ld, msg, cbinfo);
35910122SJordan.Brown@Sun.COM 	}
36010122SJordan.Brown@Sun.COM }
36110122SJordan.Brown@Sun.COM 
36210122SJordan.Brown@Sun.COM /*
36310122SJordan.Brown@Sun.COM  * Process a single entry returned by an LDAP callback.
36410122SJordan.Brown@Sun.COM  * Note that this performs a function roughly equivalent to the
36510122SJordan.Brown@Sun.COM  * directory*Populate() functions in the other providers.
36610122SJordan.Brown@Sun.COM  * Given an LDAP response, populate the directory entry for return to
36710122SJordan.Brown@Sun.COM  * the caller.  This one differs primarily in that we're working directly
36810122SJordan.Brown@Sun.COM  * with LDAP, so we don't have to do any attribute translation.
36910122SJordan.Brown@Sun.COM  */
37010122SJordan.Brown@Sun.COM static
37110122SJordan.Brown@Sun.COM void
directory_provider_ad_cb1(LDAP * ld,LDAPMessage * msg,struct cbinfo * cbinfo)37210122SJordan.Brown@Sun.COM directory_provider_ad_cb1(
37310122SJordan.Brown@Sun.COM     LDAP *ld,
37410122SJordan.Brown@Sun.COM     LDAPMessage *msg,
37510122SJordan.Brown@Sun.COM     struct cbinfo *cbinfo)
37610122SJordan.Brown@Sun.COM {
37710122SJordan.Brown@Sun.COM 	int nattrs = cbinfo->nattrs;
37810122SJordan.Brown@Sun.COM 	const char * const *attrs = cbinfo->attrs;
37910122SJordan.Brown@Sun.COM 	directory_entry_rpc *pent = cbinfo->entry;
38010122SJordan.Brown@Sun.COM 
38110122SJordan.Brown@Sun.COM 	int i;
38210122SJordan.Brown@Sun.COM 	directory_values_rpc *llvals;
38310122SJordan.Brown@Sun.COM 	directory_error_t de;
38410122SJordan.Brown@Sun.COM 	char *domain = NULL;
38510122SJordan.Brown@Sun.COM 
38610122SJordan.Brown@Sun.COM 	/*
38710122SJordan.Brown@Sun.COM 	 * We don't have a way to filter for entries from the right domain
38810122SJordan.Brown@Sun.COM 	 * in the LDAP query, so we check for it here.  Searches based on
38910122SJordan.Brown@Sun.COM 	 * samAccountName might yield results from the wrong domain.
39010122SJordan.Brown@Sun.COM 	 */
39110122SJordan.Brown@Sun.COM 	de = get_domain(ld, msg, &domain);
39210122SJordan.Brown@Sun.COM 	if (de != NULL)
39310122SJordan.Brown@Sun.COM 		goto err;
39410122SJordan.Brown@Sun.COM 
39510122SJordan.Brown@Sun.COM 	if (cbinfo->domain != NULL && !domain_eq(cbinfo->domain, domain))
39610122SJordan.Brown@Sun.COM 		goto out;
39710122SJordan.Brown@Sun.COM 
39810122SJordan.Brown@Sun.COM 	/*
39910122SJordan.Brown@Sun.COM 	 * If we've already found a match, error.
40010122SJordan.Brown@Sun.COM 	 */
40110122SJordan.Brown@Sun.COM 	if (pent->status != DIRECTORY_NOT_FOUND) {
40210122SJordan.Brown@Sun.COM 		de = directory_error("Duplicate.AD",
40310122SJordan.Brown@Sun.COM 		    "Multiple matching entries found", NULL);
40410122SJordan.Brown@Sun.COM 		goto err;
40510122SJordan.Brown@Sun.COM 	}
40610122SJordan.Brown@Sun.COM 
40710122SJordan.Brown@Sun.COM 	llvals = calloc(nattrs, sizeof (directory_values_rpc));
40810122SJordan.Brown@Sun.COM 	if (llvals == NULL)
40910122SJordan.Brown@Sun.COM 		goto nomem;
41010122SJordan.Brown@Sun.COM 
41110122SJordan.Brown@Sun.COM 	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
41210122SJordan.Brown@Sun.COM 	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
41310122SJordan.Brown@Sun.COM 	pent->status = DIRECTORY_FOUND;
41410122SJordan.Brown@Sun.COM 
41510122SJordan.Brown@Sun.COM 	for (i = 0; i < nattrs; i++) {
41610122SJordan.Brown@Sun.COM 		struct berval **bv;
41710122SJordan.Brown@Sun.COM 		const char *a = attrs[i];
41810122SJordan.Brown@Sun.COM 		directory_values_rpc *val = &llvals[i];
41910122SJordan.Brown@Sun.COM 
42010122SJordan.Brown@Sun.COM 		bv = ldap_get_values_len(ld, msg, a);
42110122SJordan.Brown@Sun.COM #if	defined(DUMP_VALUES)
42210122SJordan.Brown@Sun.COM 		dump_bv_list(attrs[i], bv);
42310122SJordan.Brown@Sun.COM #endif
42410122SJordan.Brown@Sun.COM 		if (bv != NULL) {
42510122SJordan.Brown@Sun.COM 			de = bv_list_dav(val, bv);
42610122SJordan.Brown@Sun.COM 			ldap_value_free_len(bv);
42710122SJordan.Brown@Sun.COM 			if (de != NULL)
42810122SJordan.Brown@Sun.COM 				goto err;
429*12890SJoyce.McIntosh@Sun.COM 		} else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
43010122SJordan.Brown@Sun.COM 			bv = ldap_get_values_len(ld, msg, "sAMAccountName");
43110122SJordan.Brown@Sun.COM 			if (bv != NULL) {
43210122SJordan.Brown@Sun.COM 				int n = ldap_count_values_len(bv);
43310122SJordan.Brown@Sun.COM 				if (n > 0) {
43410122SJordan.Brown@Sun.COM 					char *tmp;
43510122SJordan.Brown@Sun.COM 					(void) asprintf(&tmp, "%.*s@%s",
43610122SJordan.Brown@Sun.COM 					    bv[0]->bv_len, bv[0]->bv_val,
43710122SJordan.Brown@Sun.COM 					    domain);
43810122SJordan.Brown@Sun.COM 					if (tmp == NULL)
43910122SJordan.Brown@Sun.COM 						goto nomem;
44010122SJordan.Brown@Sun.COM 					const char *ctmp = tmp;
44110122SJordan.Brown@Sun.COM 					de = str_list_dav(val, &ctmp, 1);
44210122SJordan.Brown@Sun.COM 					free(tmp);
44310122SJordan.Brown@Sun.COM 					if (de != NULL)
44410122SJordan.Brown@Sun.COM 						goto err;
44510122SJordan.Brown@Sun.COM 				}
44610122SJordan.Brown@Sun.COM 			}
447*12890SJoyce.McIntosh@Sun.COM 		} else if (uu_strcaseeq(a, "x-sun-provider")) {
44810122SJordan.Brown@Sun.COM 			const char *provider = "LDAP-AD";
44910122SJordan.Brown@Sun.COM 			de = str_list_dav(val, &provider, 1);
45010122SJordan.Brown@Sun.COM 		}
45110122SJordan.Brown@Sun.COM 	}
45210122SJordan.Brown@Sun.COM 
45310122SJordan.Brown@Sun.COM 	goto out;
45410122SJordan.Brown@Sun.COM 
45510122SJordan.Brown@Sun.COM nomem:
45610122SJordan.Brown@Sun.COM 	de = directory_error("ENOMEM.users",
45710122SJordan.Brown@Sun.COM 	    "No memory allocating return value for user lookup", NULL);
45810122SJordan.Brown@Sun.COM 
45910122SJordan.Brown@Sun.COM err:
46010122SJordan.Brown@Sun.COM 	directory_entry_set_error(pent, de);
46110122SJordan.Brown@Sun.COM 	de = NULL;
46210122SJordan.Brown@Sun.COM 
46310122SJordan.Brown@Sun.COM out:
46410122SJordan.Brown@Sun.COM 	free(domain);
46510122SJordan.Brown@Sun.COM }
46610122SJordan.Brown@Sun.COM 
46710122SJordan.Brown@Sun.COM /*
46810122SJordan.Brown@Sun.COM  * Given a struct berval, populate a directory attribute value (which is a
46910122SJordan.Brown@Sun.COM  * list of values).
47010122SJordan.Brown@Sun.COM  * Note that here we populate the DAV with the exact bytes that LDAP returns.
47110122SJordan.Brown@Sun.COM  * Back over in the client it appends a \0 so that strings are null
47210122SJordan.Brown@Sun.COM  * terminated.
47310122SJordan.Brown@Sun.COM  */
47410122SJordan.Brown@Sun.COM static
47510122SJordan.Brown@Sun.COM directory_error_t
bv_list_dav(directory_values_rpc * lvals,struct berval ** bv)47610122SJordan.Brown@Sun.COM bv_list_dav(directory_values_rpc *lvals, struct berval **bv)
47710122SJordan.Brown@Sun.COM {
47810122SJordan.Brown@Sun.COM 	directory_value_rpc *dav;
47910122SJordan.Brown@Sun.COM 	int n;
48010122SJordan.Brown@Sun.COM 	int i;
48110122SJordan.Brown@Sun.COM 
48210122SJordan.Brown@Sun.COM 	n = ldap_count_values_len(bv);
48310122SJordan.Brown@Sun.COM 
48410122SJordan.Brown@Sun.COM 	dav = calloc(n, sizeof (directory_value_rpc));
48510122SJordan.Brown@Sun.COM 	if (dav == NULL)
48610122SJordan.Brown@Sun.COM 		goto nomem;
48710122SJordan.Brown@Sun.COM 
48810122SJordan.Brown@Sun.COM 	lvals->directory_values_rpc_u.values.values_val = dav;
48910122SJordan.Brown@Sun.COM 	lvals->directory_values_rpc_u.values.values_len = n;
49010122SJordan.Brown@Sun.COM 	lvals->found = TRUE;
49110122SJordan.Brown@Sun.COM 
49210122SJordan.Brown@Sun.COM 	for (i = 0; i < n; i++) {
49310122SJordan.Brown@Sun.COM 		dav[i].directory_value_rpc_val =
494*12890SJoyce.McIntosh@Sun.COM 		    uu_memdup(bv[i]->bv_val, bv[i]->bv_len);
49510122SJordan.Brown@Sun.COM 		if (dav[i].directory_value_rpc_val == NULL)
49610122SJordan.Brown@Sun.COM 			goto nomem;
49710122SJordan.Brown@Sun.COM 		dav[i].directory_value_rpc_len = bv[i]->bv_len;
49810122SJordan.Brown@Sun.COM 	}
49910122SJordan.Brown@Sun.COM 
50010122SJordan.Brown@Sun.COM 	return (NULL);
50110122SJordan.Brown@Sun.COM 
50210122SJordan.Brown@Sun.COM nomem:
50310122SJordan.Brown@Sun.COM 	return (directory_error("ENOMEM.bv_list_dav",
50410122SJordan.Brown@Sun.COM 	    "Insufficient memory copying values"));
50510122SJordan.Brown@Sun.COM }
50610122SJordan.Brown@Sun.COM 
50710122SJordan.Brown@Sun.COM #if	defined(DUMP_VALUES)
50810122SJordan.Brown@Sun.COM static
50910122SJordan.Brown@Sun.COM void
dump_bv_list(const char * attr,struct berval ** bv)51010122SJordan.Brown@Sun.COM dump_bv_list(const char *attr, struct berval **bv)
51110122SJordan.Brown@Sun.COM {
51210122SJordan.Brown@Sun.COM 	int i;
51310122SJordan.Brown@Sun.COM 
51410122SJordan.Brown@Sun.COM 	if (bv == NULL) {
51510122SJordan.Brown@Sun.COM 		(void) fprintf(stderr, "%s:  (empty)\n", attr);
51610122SJordan.Brown@Sun.COM 		return;
51710122SJordan.Brown@Sun.COM 	}
51810122SJordan.Brown@Sun.COM 	for (i = 0; bv[i] != NULL; i++) {
51910122SJordan.Brown@Sun.COM 		(void) fprintf(stderr, "%s[%d] =\n", attr, i);
52010122SJordan.Brown@Sun.COM 		dump(stderr, "    ", bv[i]->bv_val, bv[i]->bv_len);
52110122SJordan.Brown@Sun.COM 	}
52210122SJordan.Brown@Sun.COM }
52310122SJordan.Brown@Sun.COM #endif	/* DUMP_VALUES */
52410122SJordan.Brown@Sun.COM 
52510122SJordan.Brown@Sun.COM /*
52610122SJordan.Brown@Sun.COM  * Return the domain associated with the specified entry.
52710122SJordan.Brown@Sun.COM  */
52810122SJordan.Brown@Sun.COM static
52910122SJordan.Brown@Sun.COM directory_error_t
get_domain(LDAP * ld,LDAPMessage * msg,char ** domain)53010122SJordan.Brown@Sun.COM get_domain(
53110122SJordan.Brown@Sun.COM     LDAP *ld,
53210122SJordan.Brown@Sun.COM     LDAPMessage *msg,
53310122SJordan.Brown@Sun.COM     char **domain)
53410122SJordan.Brown@Sun.COM {
53510122SJordan.Brown@Sun.COM 	*domain = NULL;
53610122SJordan.Brown@Sun.COM 
53710122SJordan.Brown@Sun.COM 	char *dn = ldap_get_dn(ld, msg);
53810122SJordan.Brown@Sun.COM 	if (dn == NULL) {
53910122SJordan.Brown@Sun.COM 		char buf[100];	/* big enough for any int */
54010122SJordan.Brown@Sun.COM 		char *m;
54110122SJordan.Brown@Sun.COM 		char *s;
54210122SJordan.Brown@Sun.COM 		int err = ldap_get_lderrno(ld, &m, &s);
54310122SJordan.Brown@Sun.COM 		(void) snprintf(buf, sizeof (buf), "%d", err);
54410122SJordan.Brown@Sun.COM 
54510122SJordan.Brown@Sun.COM 		return directory_error("AD.get_domain.ldap_get_dn",
54610122SJordan.Brown@Sun.COM 		    "ldap_get_dn: %1 (%2)\n"
54710122SJordan.Brown@Sun.COM 		    "matched: %3\n"
54810122SJordan.Brown@Sun.COM 		    "error:   %4",
54910122SJordan.Brown@Sun.COM 		    ldap_err2string(err), buf,
55010122SJordan.Brown@Sun.COM 		    m == NULL ? "(null)" : m,
55110122SJordan.Brown@Sun.COM 		    s == NULL ? "(null)" : s,
55210122SJordan.Brown@Sun.COM 		    NULL);
55310122SJordan.Brown@Sun.COM 	}
55410122SJordan.Brown@Sun.COM 
55510122SJordan.Brown@Sun.COM 	*domain = adutils_dn2dns(dn);
55610122SJordan.Brown@Sun.COM 	if (*domain == NULL) {
55710122SJordan.Brown@Sun.COM 		directory_error_t de;
55810122SJordan.Brown@Sun.COM 
55910122SJordan.Brown@Sun.COM 		de = directory_error("Unknown.get_domain.adutils_dn2dns",
56010122SJordan.Brown@Sun.COM 		    "get_domain:  Unexpected error from adutils_dn2dns(%1)",
56110122SJordan.Brown@Sun.COM 		    dn, NULL);
56210122SJordan.Brown@Sun.COM 		free(dn);
56310122SJordan.Brown@Sun.COM 		return (de);
56410122SJordan.Brown@Sun.COM 	}
56510122SJordan.Brown@Sun.COM 	free(dn);
56610122SJordan.Brown@Sun.COM 
56710122SJordan.Brown@Sun.COM 	return (NULL);
56810122SJordan.Brown@Sun.COM }
56910122SJordan.Brown@Sun.COM 
57010122SJordan.Brown@Sun.COM /*
57110122SJordan.Brown@Sun.COM  * Given an error report from libadutils, generate a directory_error_t.
57210122SJordan.Brown@Sun.COM  */
57310122SJordan.Brown@Sun.COM static
57410122SJordan.Brown@Sun.COM directory_error_t
directory_provider_ad_utils_error(char * func,int rc)57510122SJordan.Brown@Sun.COM directory_provider_ad_utils_error(char *func, int rc)
57610122SJordan.Brown@Sun.COM {
57710122SJordan.Brown@Sun.COM 	char rcstr[100];	/* plenty for any int */
57810122SJordan.Brown@Sun.COM 	char code[100];		/* plenty for any int */
57910122SJordan.Brown@Sun.COM 	(void) snprintf(rcstr, sizeof (rcstr), "%d", rc);
58010122SJordan.Brown@Sun.COM 	(void) snprintf(code, sizeof (code), "ADUTILS.%d", rc);
58110122SJordan.Brown@Sun.COM 
58210122SJordan.Brown@Sun.COM 	return (directory_error(code,
58310122SJordan.Brown@Sun.COM 	    "Error %2 from adutils function %1", func, rcstr, NULL));
58410122SJordan.Brown@Sun.COM }
58510122SJordan.Brown@Sun.COM 
58610122SJordan.Brown@Sun.COM struct directory_provider_static directory_provider_ad = {
58710122SJordan.Brown@Sun.COM 	"AD",
58810122SJordan.Brown@Sun.COM 	directory_provider_ad_get,
58910122SJordan.Brown@Sun.COM };
590