11fcced4cSJordan Brown /* 21fcced4cSJordan Brown * CDDL HEADER START 31fcced4cSJordan Brown * 41fcced4cSJordan Brown * The contents of this file are subject to the terms of the 51fcced4cSJordan Brown * Common Development and Distribution License (the "License"). 61fcced4cSJordan Brown * You may not use this file except in compliance with the License. 71fcced4cSJordan Brown * 81fcced4cSJordan Brown * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91fcced4cSJordan Brown * or http://www.opensolaris.org/os/licensing. 101fcced4cSJordan Brown * See the License for the specific language governing permissions 111fcced4cSJordan Brown * and limitations under the License. 121fcced4cSJordan Brown * 131fcced4cSJordan Brown * When distributing Covered Code, include this CDDL HEADER in each 141fcced4cSJordan Brown * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151fcced4cSJordan Brown * If applicable, add the following below this CDDL HEADER, with the 161fcced4cSJordan Brown * fields enclosed by brackets "[]" replaced with your own identifying 171fcced4cSJordan Brown * information: Portions Copyright [yyyy] [name of copyright owner] 181fcced4cSJordan Brown * 191fcced4cSJordan Brown * CDDL HEADER END 201fcced4cSJordan Brown */ 211fcced4cSJordan Brown 221fcced4cSJordan Brown /* 231fcced4cSJordan Brown * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 241fcced4cSJordan Brown * Use is subject to license terms. 251fcced4cSJordan Brown */ 261fcced4cSJordan Brown 271fcced4cSJordan Brown /* 281fcced4cSJordan Brown * Retrieve directory information for Active Directory users. 291fcced4cSJordan Brown */ 301fcced4cSJordan Brown 311fcced4cSJordan Brown #include <ldap.h> 321fcced4cSJordan Brown #include <lber.h> 331fcced4cSJordan Brown #include <pwd.h> 341fcced4cSJordan Brown #include <malloc.h> 351fcced4cSJordan Brown #include <string.h> 361fcced4cSJordan Brown #include <stdlib.h> 371fcced4cSJordan Brown #include <netdb.h> 381fcced4cSJordan Brown #include <libadutils.h> 391fcced4cSJordan Brown #include <note.h> 401fcced4cSJordan Brown #include <assert.h> 411fcced4cSJordan Brown #include "directory.h" 421fcced4cSJordan Brown #include "directory_private.h" 431fcced4cSJordan Brown #include "idmapd.h" 441fcced4cSJordan Brown #include <rpcsvc/idmap_prot.h> 451fcced4cSJordan Brown #include "directory_server_impl.h" 461fcced4cSJordan Brown #include "miscutils.h" 471fcced4cSJordan Brown 481fcced4cSJordan Brown /* 491fcced4cSJordan Brown * Information required by the function that handles the callback from LDAP 501fcced4cSJordan Brown * when responses are received. 511fcced4cSJordan Brown */ 521fcced4cSJordan Brown struct cbinfo { 531fcced4cSJordan Brown const char * const *attrs; 541fcced4cSJordan Brown int nattrs; 551fcced4cSJordan Brown directory_entry_rpc *entry; 561fcced4cSJordan Brown const char *domain; 571fcced4cSJordan Brown }; 581fcced4cSJordan Brown 591fcced4cSJordan Brown static void directory_provider_ad_cb(LDAP *ld, LDAPMessage **ldapres, int rc, 601fcced4cSJordan Brown int qid, void *argp); 611fcced4cSJordan Brown static void directory_provider_ad_cb1(LDAP *ld, LDAPMessage *msg, 621fcced4cSJordan Brown struct cbinfo *cbinfo); 631fcced4cSJordan Brown static directory_error_t bv_list_dav(directory_values_rpc *lvals, 641fcced4cSJordan Brown struct berval **bv); 651fcced4cSJordan Brown static directory_error_t directory_provider_ad_lookup( 661fcced4cSJordan Brown directory_entry_rpc *pent, const char * const * attrs, int nattrs, 671fcced4cSJordan Brown const char *domain, const char *filter); 681fcced4cSJordan Brown static directory_error_t get_domain(LDAP *ld, LDAPMessage *ldapres, 691fcced4cSJordan Brown char **domain); 701fcced4cSJordan Brown static directory_error_t directory_provider_ad_utils_error(char *func, int rc); 711fcced4cSJordan Brown 721fcced4cSJordan Brown #if defined(DUMP_VALUES) 731fcced4cSJordan Brown static void dump_bv_list(const char *attr, struct berval **bv); 741fcced4cSJordan Brown #endif 751fcced4cSJordan Brown 761fcced4cSJordan Brown #define MAX_EXTRA_ATTRS 1 /* sAMAccountName */ 771fcced4cSJordan Brown 781fcced4cSJordan Brown /* 791fcced4cSJordan Brown * Add an entry to a NULL-terminated list, if it's not already there. 801fcced4cSJordan Brown * Assumes that the list has been allocated large enough for all additions, 811fcced4cSJordan Brown * and prefilled with NULL. 821fcced4cSJordan Brown */ 831fcced4cSJordan Brown static 841fcced4cSJordan Brown void 851fcced4cSJordan Brown maybe_add_to_list(const char **list, const char *s) 861fcced4cSJordan Brown { 871fcced4cSJordan Brown for (; *list != NULL; list++) { 881fcced4cSJordan Brown if (strcaseeq(*list, s)) 891fcced4cSJordan Brown return; 901fcced4cSJordan Brown } 911fcced4cSJordan Brown *list = s; 921fcced4cSJordan Brown } 931fcced4cSJordan Brown 941fcced4cSJordan Brown /* 951fcced4cSJordan Brown * Copy a counted attribute list to a NULL-terminated one. 961fcced4cSJordan Brown * In the process, examine the requested attributes and augment 971fcced4cSJordan Brown * the list as required to support any synthesized attributes 981fcced4cSJordan Brown * requested. 991fcced4cSJordan Brown */ 1001fcced4cSJordan Brown static 1011fcced4cSJordan Brown const char ** 1021fcced4cSJordan Brown copy_and_augment_attr_list(char **req_list, int req_list_len) 1031fcced4cSJordan Brown { 1041fcced4cSJordan Brown const char **new_list; 1051fcced4cSJordan Brown int i; 1061fcced4cSJordan Brown 1071fcced4cSJordan Brown new_list = 1081fcced4cSJordan Brown calloc(req_list_len + MAX_EXTRA_ATTRS + 1, sizeof (*new_list)); 1091fcced4cSJordan Brown if (new_list == NULL) 1101fcced4cSJordan Brown return (NULL); 1111fcced4cSJordan Brown 1121fcced4cSJordan Brown (void) memcpy(new_list, req_list, req_list_len * sizeof (char *)); 1131fcced4cSJordan Brown 1141fcced4cSJordan Brown for (i = 0; i < req_list_len; i++) { 1151fcced4cSJordan Brown const char *a = req_list[i]; 1161fcced4cSJordan Brown /* 1171fcced4cSJordan Brown * Note that you must update MAX_EXTRA_ATTRS above if you 1181fcced4cSJordan Brown * add to this list. 1191fcced4cSJordan Brown */ 1201fcced4cSJordan Brown if (strcaseeq(a, "x-sun-canonicalName")) { 1211fcced4cSJordan Brown maybe_add_to_list(new_list, "sAMAccountName"); 1221fcced4cSJordan Brown continue; 1231fcced4cSJordan Brown } 1241fcced4cSJordan Brown /* None needed for x-sun-provider */ 1251fcced4cSJordan Brown } 1261fcced4cSJordan Brown 1271fcced4cSJordan Brown return (new_list); 1281fcced4cSJordan Brown } 1291fcced4cSJordan Brown 1301fcced4cSJordan Brown /* 1311fcced4cSJordan Brown * Retrieve information by name. 1321fcced4cSJordan Brown * Called indirectly through the Directory_provider_static structure. 1331fcced4cSJordan Brown */ 1341fcced4cSJordan Brown static 1351fcced4cSJordan Brown directory_error_t 1361fcced4cSJordan Brown directory_provider_ad_get( 1371fcced4cSJordan Brown directory_entry_rpc *del, 1381fcced4cSJordan Brown idmap_utf8str_list *ids, 1391fcced4cSJordan Brown char *types, 1401fcced4cSJordan Brown idmap_utf8str_list *attrs) 1411fcced4cSJordan Brown { 1421fcced4cSJordan Brown int i; 1431fcced4cSJordan Brown const char **attrs2; 1441fcced4cSJordan Brown directory_error_t de = NULL; 1451fcced4cSJordan Brown 1461fcced4cSJordan Brown /* 1471fcced4cSJordan Brown * If we don't have any AD servers handy, we can't find anything. 1481fcced4cSJordan Brown */ 149*e3f2c991SKeyur Desai if (_idmapdstate.num_gcs < 1) { 1501fcced4cSJordan Brown return (NULL); 1511fcced4cSJordan Brown } 1521fcced4cSJordan Brown 1531fcced4cSJordan Brown RDLOCK_CONFIG() 1541fcced4cSJordan Brown 1551fcced4cSJordan Brown /* 6835280 spurious lint error if the strlen is in the declaration */ 1561fcced4cSJordan Brown int len = strlen(_idmapdstate.cfg->pgcfg.default_domain); 1571fcced4cSJordan Brown char default_domain[len + 1]; 1581fcced4cSJordan Brown (void) strcpy(default_domain, _idmapdstate.cfg->pgcfg.default_domain); 1591fcced4cSJordan Brown 1601fcced4cSJordan Brown UNLOCK_CONFIG(); 1611fcced4cSJordan Brown 1621fcced4cSJordan Brown /* 1631fcced4cSJordan Brown * Turn our counted-array argument into a NULL-terminated array. 1641fcced4cSJordan Brown * At the same time, add in any attributes that we need to support 1651fcced4cSJordan Brown * any requested synthesized attributes. 1661fcced4cSJordan Brown */ 1671fcced4cSJordan Brown attrs2 = copy_and_augment_attr_list(attrs->idmap_utf8str_list_val, 1681fcced4cSJordan Brown attrs->idmap_utf8str_list_len); 1691fcced4cSJordan Brown if (attrs2 == NULL) 1701fcced4cSJordan Brown goto nomem; 1711fcced4cSJordan Brown 1721fcced4cSJordan Brown for (i = 0; i < ids->idmap_utf8str_list_len; i++) { 1731fcced4cSJordan Brown char *vw[3]; 1741fcced4cSJordan Brown int type; 1751fcced4cSJordan Brown 1761fcced4cSJordan Brown /* 1771fcced4cSJordan Brown * Extract the type for this particular ID. 1781fcced4cSJordan Brown * Advance to the next type, if it's there, else keep 1791fcced4cSJordan Brown * using this type until we run out of IDs. 1801fcced4cSJordan Brown */ 1811fcced4cSJordan Brown type = *types; 1821fcced4cSJordan Brown if (*(types+1) != '\0') 1831fcced4cSJordan Brown types++; 1841fcced4cSJordan Brown 1851fcced4cSJordan Brown /* 1861fcced4cSJordan Brown * If this entry has already been handled, one way or another, 1871fcced4cSJordan Brown * skip it. 1881fcced4cSJordan Brown */ 1891fcced4cSJordan Brown if (del[i].status != DIRECTORY_NOT_FOUND) 1901fcced4cSJordan Brown continue; 1911fcced4cSJordan Brown 1921fcced4cSJordan Brown char *id = ids->idmap_utf8str_list_val[i]; 1931fcced4cSJordan Brown 1941fcced4cSJordan Brown /* 1951fcced4cSJordan Brown * Allow for expanding every character to \xx, plus some 1961fcced4cSJordan Brown * space for the query syntax. 1971fcced4cSJordan Brown */ 1981fcced4cSJordan Brown int id_len = strlen(id); 1991fcced4cSJordan Brown char filter[1000 + id_len*3]; 2001fcced4cSJordan Brown 2011fcced4cSJordan Brown if (type == DIRECTORY_ID_SID[0]) { 2021fcced4cSJordan Brown /* 2031fcced4cSJordan Brown * Mildly surprisingly, AD appears to allow searching 2041fcced4cSJordan Brown * based on text SIDs. Must be a special case on the 2051fcced4cSJordan Brown * server end. 2061fcced4cSJordan Brown */ 2071fcced4cSJordan Brown ldap_build_filter(filter, sizeof (filter), 2081fcced4cSJordan Brown "(objectSid=%v)", NULL, NULL, NULL, id, NULL); 2091fcced4cSJordan Brown 2101fcced4cSJordan Brown de = directory_provider_ad_lookup(&del[i], attrs2, 2111fcced4cSJordan Brown attrs->idmap_utf8str_list_len, NULL, filter); 2121fcced4cSJordan Brown if (de != NULL) { 2131fcced4cSJordan Brown directory_entry_set_error(&del[i], de); 2141fcced4cSJordan Brown de = NULL; 2151fcced4cSJordan Brown } 2161fcced4cSJordan Brown } else { 2171fcced4cSJordan Brown int id_len = strlen(id); 2181fcced4cSJordan Brown char name[id_len + 1]; 2191fcced4cSJordan Brown char domain[id_len + 1]; 2201fcced4cSJordan Brown 2211fcced4cSJordan Brown split_name(name, domain, id); 2221fcced4cSJordan Brown 2231fcced4cSJordan Brown vw[0] = name; 2241fcced4cSJordan Brown 2251fcced4cSJordan Brown if (streq(domain, "")) { 2261fcced4cSJordan Brown vw[1] = default_domain; 2271fcced4cSJordan Brown } else { 2281fcced4cSJordan Brown vw[1] = domain; 2291fcced4cSJordan Brown } 2301fcced4cSJordan Brown 2311fcced4cSJordan Brown if (type == DIRECTORY_ID_USER[0]) 2321fcced4cSJordan Brown vw[2] = "user"; 2331fcced4cSJordan Brown else if (type == DIRECTORY_ID_GROUP[0]) 2341fcced4cSJordan Brown vw[2] = "group"; 2351fcced4cSJordan Brown else 2361fcced4cSJordan Brown vw[2] = "*"; 2371fcced4cSJordan Brown 2381fcced4cSJordan Brown /* 2391fcced4cSJordan Brown * Try samAccountName. 2401fcced4cSJordan Brown * Note that here we rely on checking the returned 2411fcced4cSJordan Brown * distinguishedName to make sure that we found an 2421fcced4cSJordan Brown * entry from the right domain, because there's no 2431fcced4cSJordan Brown * attribute we can straightforwardly filter for to 2441fcced4cSJordan Brown * match domain. 2451fcced4cSJordan Brown * 2461fcced4cSJordan Brown * Eventually we should perhaps also try 2471fcced4cSJordan Brown * userPrincipalName. 2481fcced4cSJordan Brown */ 2491fcced4cSJordan Brown ldap_build_filter(filter, sizeof (filter), 2501fcced4cSJordan Brown "(&(samAccountName=%v1)(objectClass=%v3))", 2511fcced4cSJordan Brown NULL, NULL, NULL, NULL, vw); 2521fcced4cSJordan Brown 2531fcced4cSJordan Brown de = directory_provider_ad_lookup(&del[i], attrs2, 2541fcced4cSJordan Brown attrs->idmap_utf8str_list_len, vw[1], filter); 2551fcced4cSJordan Brown if (de != NULL) { 2561fcced4cSJordan Brown directory_entry_set_error(&del[i], de); 2571fcced4cSJordan Brown de = NULL; 2581fcced4cSJordan Brown } 2591fcced4cSJordan Brown } 2601fcced4cSJordan Brown } 2611fcced4cSJordan Brown 2621fcced4cSJordan Brown de = NULL; 2631fcced4cSJordan Brown 2641fcced4cSJordan Brown goto out; 2651fcced4cSJordan Brown 2661fcced4cSJordan Brown nomem: 2671fcced4cSJordan Brown de = directory_error("ENOMEM.AD", 2681fcced4cSJordan Brown "Out of memory during AD lookup", NULL); 2691fcced4cSJordan Brown out: 2701fcced4cSJordan Brown free(attrs2); 2711fcced4cSJordan Brown return (de); 2721fcced4cSJordan Brown } 2731fcced4cSJordan Brown 2741fcced4cSJordan Brown /* 2751fcced4cSJordan Brown * Note that attrs is NULL terminated, and that nattrs is the number 2761fcced4cSJordan Brown * of attributes requested by the user... which might be fewer than are 2771fcced4cSJordan Brown * in attrs because of attributes that we need for our own processing. 2781fcced4cSJordan Brown */ 2791fcced4cSJordan Brown static 2801fcced4cSJordan Brown directory_error_t 2811fcced4cSJordan Brown directory_provider_ad_lookup( 2821fcced4cSJordan Brown directory_entry_rpc *pent, 2831fcced4cSJordan Brown const char * const * attrs, 2841fcced4cSJordan Brown int nattrs, 2851fcced4cSJordan Brown const char *domain, 2861fcced4cSJordan Brown const char *filter) 2871fcced4cSJordan Brown { 2881fcced4cSJordan Brown adutils_ad_t *ad; 2891fcced4cSJordan Brown adutils_rc batchrc; 2901fcced4cSJordan Brown struct cbinfo cbinfo; 2911fcced4cSJordan Brown adutils_query_state_t *qs; 2921fcced4cSJordan Brown int rc; 2931fcced4cSJordan Brown 2941fcced4cSJordan Brown /* 2951fcced4cSJordan Brown * NEEDSWORK: Should eventually handle other forests. 2961fcced4cSJordan Brown * NEEDSWORK: Should eventually handle non-GC attributes. 2971fcced4cSJordan Brown */ 298*e3f2c991SKeyur Desai ad = _idmapdstate.gcs[0]; 2991fcced4cSJordan Brown 3001fcced4cSJordan Brown /* Stash away information for the callback function. */ 3011fcced4cSJordan Brown cbinfo.attrs = attrs; 3021fcced4cSJordan Brown cbinfo.nattrs = nattrs; 3031fcced4cSJordan Brown cbinfo.entry = pent; 3041fcced4cSJordan Brown cbinfo.domain = domain; 3051fcced4cSJordan Brown 3061fcced4cSJordan Brown rc = adutils_lookup_batch_start(ad, 1, directory_provider_ad_cb, 3071fcced4cSJordan Brown &cbinfo, &qs); 3081fcced4cSJordan Brown if (rc != ADUTILS_SUCCESS) { 3091fcced4cSJordan Brown return (directory_provider_ad_utils_error( 3101fcced4cSJordan Brown "adutils_lookup_batch_start", rc)); 3111fcced4cSJordan Brown } 3121fcced4cSJordan Brown 3131fcced4cSJordan Brown rc = adutils_lookup_batch_add(qs, filter, attrs, domain, 3141fcced4cSJordan Brown NULL, &batchrc); 3151fcced4cSJordan Brown if (rc != ADUTILS_SUCCESS) { 3161fcced4cSJordan Brown adutils_lookup_batch_release(&qs); 3171fcced4cSJordan Brown return (directory_provider_ad_utils_error( 3181fcced4cSJordan Brown "adutils_lookup_batch_add", rc)); 3191fcced4cSJordan Brown } 3201fcced4cSJordan Brown 3211fcced4cSJordan Brown rc = adutils_lookup_batch_end(&qs); 3221fcced4cSJordan Brown if (rc != ADUTILS_SUCCESS) { 3231fcced4cSJordan Brown return (directory_provider_ad_utils_error( 3241fcced4cSJordan Brown "adutils_lookup_batch_end", rc)); 3251fcced4cSJordan Brown } 3261fcced4cSJordan Brown 3271fcced4cSJordan Brown if (batchrc != ADUTILS_SUCCESS) { 3281fcced4cSJordan Brown /* 3291fcced4cSJordan Brown * NEEDSWORK: We're consistently getting -9997 here. 3301fcced4cSJordan Brown * What does it mean? 3311fcced4cSJordan Brown */ 3321fcced4cSJordan Brown return (NULL); 3331fcced4cSJordan Brown } 3341fcced4cSJordan Brown 3351fcced4cSJordan Brown return (NULL); 3361fcced4cSJordan Brown } 3371fcced4cSJordan Brown 3381fcced4cSJordan Brown /* 3391fcced4cSJordan Brown * Callback from the LDAP functions when they get responses. 3401fcced4cSJordan Brown * We don't really need (nor want) asynchronous handling, but it's 3411fcced4cSJordan Brown * what libadutils gives us. 3421fcced4cSJordan Brown */ 3431fcced4cSJordan Brown static 3441fcced4cSJordan Brown void 3451fcced4cSJordan Brown directory_provider_ad_cb( 3461fcced4cSJordan Brown LDAP *ld, 3471fcced4cSJordan Brown LDAPMessage **ldapres, 3481fcced4cSJordan Brown int rc, 3491fcced4cSJordan Brown int qid, 3501fcced4cSJordan Brown void *argp) 3511fcced4cSJordan Brown { 3521fcced4cSJordan Brown NOTE(ARGUNUSED(rc, qid)) 3531fcced4cSJordan Brown struct cbinfo *cbinfo = (struct cbinfo *)argp; 3541fcced4cSJordan Brown LDAPMessage *msg = *ldapres; 3551fcced4cSJordan Brown 3561fcced4cSJordan Brown for (msg = ldap_first_entry(ld, msg); 3571fcced4cSJordan Brown msg != NULL; 3581fcced4cSJordan Brown msg = ldap_next_entry(ld, msg)) { 3591fcced4cSJordan Brown directory_provider_ad_cb1(ld, msg, cbinfo); 3601fcced4cSJordan Brown } 3611fcced4cSJordan Brown } 3621fcced4cSJordan Brown 3631fcced4cSJordan Brown /* 3641fcced4cSJordan Brown * Process a single entry returned by an LDAP callback. 3651fcced4cSJordan Brown * Note that this performs a function roughly equivalent to the 3661fcced4cSJordan Brown * directory*Populate() functions in the other providers. 3671fcced4cSJordan Brown * Given an LDAP response, populate the directory entry for return to 3681fcced4cSJordan Brown * the caller. This one differs primarily in that we're working directly 3691fcced4cSJordan Brown * with LDAP, so we don't have to do any attribute translation. 3701fcced4cSJordan Brown */ 3711fcced4cSJordan Brown static 3721fcced4cSJordan Brown void 3731fcced4cSJordan Brown directory_provider_ad_cb1( 3741fcced4cSJordan Brown LDAP *ld, 3751fcced4cSJordan Brown LDAPMessage *msg, 3761fcced4cSJordan Brown struct cbinfo *cbinfo) 3771fcced4cSJordan Brown { 3781fcced4cSJordan Brown int nattrs = cbinfo->nattrs; 3791fcced4cSJordan Brown const char * const *attrs = cbinfo->attrs; 3801fcced4cSJordan Brown directory_entry_rpc *pent = cbinfo->entry; 3811fcced4cSJordan Brown 3821fcced4cSJordan Brown int i; 3831fcced4cSJordan Brown directory_values_rpc *llvals; 3841fcced4cSJordan Brown directory_error_t de; 3851fcced4cSJordan Brown char *domain = NULL; 3861fcced4cSJordan Brown 3871fcced4cSJordan Brown /* 3881fcced4cSJordan Brown * We don't have a way to filter for entries from the right domain 3891fcced4cSJordan Brown * in the LDAP query, so we check for it here. Searches based on 3901fcced4cSJordan Brown * samAccountName might yield results from the wrong domain. 3911fcced4cSJordan Brown */ 3921fcced4cSJordan Brown de = get_domain(ld, msg, &domain); 3931fcced4cSJordan Brown if (de != NULL) 3941fcced4cSJordan Brown goto err; 3951fcced4cSJordan Brown 3961fcced4cSJordan Brown if (cbinfo->domain != NULL && !domain_eq(cbinfo->domain, domain)) 3971fcced4cSJordan Brown goto out; 3981fcced4cSJordan Brown 3991fcced4cSJordan Brown /* 4001fcced4cSJordan Brown * If we've already found a match, error. 4011fcced4cSJordan Brown */ 4021fcced4cSJordan Brown if (pent->status != DIRECTORY_NOT_FOUND) { 4031fcced4cSJordan Brown de = directory_error("Duplicate.AD", 4041fcced4cSJordan Brown "Multiple matching entries found", NULL); 4051fcced4cSJordan Brown goto err; 4061fcced4cSJordan Brown } 4071fcced4cSJordan Brown 4081fcced4cSJordan Brown llvals = calloc(nattrs, sizeof (directory_values_rpc)); 4091fcced4cSJordan Brown if (llvals == NULL) 4101fcced4cSJordan Brown goto nomem; 4111fcced4cSJordan Brown 4121fcced4cSJordan Brown pent->directory_entry_rpc_u.attrs.attrs_val = llvals; 4131fcced4cSJordan Brown pent->directory_entry_rpc_u.attrs.attrs_len = nattrs; 4141fcced4cSJordan Brown pent->status = DIRECTORY_FOUND; 4151fcced4cSJordan Brown 4161fcced4cSJordan Brown for (i = 0; i < nattrs; i++) { 4171fcced4cSJordan Brown struct berval **bv; 4181fcced4cSJordan Brown const char *a = attrs[i]; 4191fcced4cSJordan Brown directory_values_rpc *val = &llvals[i]; 4201fcced4cSJordan Brown 4211fcced4cSJordan Brown bv = ldap_get_values_len(ld, msg, a); 4221fcced4cSJordan Brown #if defined(DUMP_VALUES) 4231fcced4cSJordan Brown dump_bv_list(attrs[i], bv); 4241fcced4cSJordan Brown #endif 4251fcced4cSJordan Brown if (bv != NULL) { 4261fcced4cSJordan Brown de = bv_list_dav(val, bv); 4271fcced4cSJordan Brown ldap_value_free_len(bv); 4281fcced4cSJordan Brown if (de != NULL) 4291fcced4cSJordan Brown goto err; 4301fcced4cSJordan Brown } else if (strcaseeq(a, "x-sun-canonicalName")) { 4311fcced4cSJordan Brown bv = ldap_get_values_len(ld, msg, "sAMAccountName"); 4321fcced4cSJordan Brown if (bv != NULL) { 4331fcced4cSJordan Brown int n = ldap_count_values_len(bv); 4341fcced4cSJordan Brown if (n > 0) { 4351fcced4cSJordan Brown char *tmp; 4361fcced4cSJordan Brown (void) asprintf(&tmp, "%.*s@%s", 4371fcced4cSJordan Brown bv[0]->bv_len, bv[0]->bv_val, 4381fcced4cSJordan Brown domain); 4391fcced4cSJordan Brown if (tmp == NULL) 4401fcced4cSJordan Brown goto nomem; 4411fcced4cSJordan Brown const char *ctmp = tmp; 4421fcced4cSJordan Brown de = str_list_dav(val, &ctmp, 1); 4431fcced4cSJordan Brown free(tmp); 4441fcced4cSJordan Brown if (de != NULL) 4451fcced4cSJordan Brown goto err; 4461fcced4cSJordan Brown } 4471fcced4cSJordan Brown } 4481fcced4cSJordan Brown } else if (strcaseeq(a, "x-sun-provider")) { 4491fcced4cSJordan Brown const char *provider = "LDAP-AD"; 4501fcced4cSJordan Brown de = str_list_dav(val, &provider, 1); 4511fcced4cSJordan Brown } 4521fcced4cSJordan Brown } 4531fcced4cSJordan Brown 4541fcced4cSJordan Brown goto out; 4551fcced4cSJordan Brown 4561fcced4cSJordan Brown nomem: 4571fcced4cSJordan Brown de = directory_error("ENOMEM.users", 4581fcced4cSJordan Brown "No memory allocating return value for user lookup", NULL); 4591fcced4cSJordan Brown 4601fcced4cSJordan Brown err: 4611fcced4cSJordan Brown directory_entry_set_error(pent, de); 4621fcced4cSJordan Brown de = NULL; 4631fcced4cSJordan Brown 4641fcced4cSJordan Brown out: 4651fcced4cSJordan Brown free(domain); 4661fcced4cSJordan Brown } 4671fcced4cSJordan Brown 4681fcced4cSJordan Brown /* 4691fcced4cSJordan Brown * Given a struct berval, populate a directory attribute value (which is a 4701fcced4cSJordan Brown * list of values). 4711fcced4cSJordan Brown * Note that here we populate the DAV with the exact bytes that LDAP returns. 4721fcced4cSJordan Brown * Back over in the client it appends a \0 so that strings are null 4731fcced4cSJordan Brown * terminated. 4741fcced4cSJordan Brown */ 4751fcced4cSJordan Brown static 4761fcced4cSJordan Brown directory_error_t 4771fcced4cSJordan Brown bv_list_dav(directory_values_rpc *lvals, struct berval **bv) 4781fcced4cSJordan Brown { 4791fcced4cSJordan Brown directory_value_rpc *dav; 4801fcced4cSJordan Brown int n; 4811fcced4cSJordan Brown int i; 4821fcced4cSJordan Brown 4831fcced4cSJordan Brown n = ldap_count_values_len(bv); 4841fcced4cSJordan Brown 4851fcced4cSJordan Brown dav = calloc(n, sizeof (directory_value_rpc)); 4861fcced4cSJordan Brown if (dav == NULL) 4871fcced4cSJordan Brown goto nomem; 4881fcced4cSJordan Brown 4891fcced4cSJordan Brown lvals->directory_values_rpc_u.values.values_val = dav; 4901fcced4cSJordan Brown lvals->directory_values_rpc_u.values.values_len = n; 4911fcced4cSJordan Brown lvals->found = TRUE; 4921fcced4cSJordan Brown 4931fcced4cSJordan Brown for (i = 0; i < n; i++) { 4941fcced4cSJordan Brown dav[i].directory_value_rpc_val = 4951fcced4cSJordan Brown memdup(bv[i]->bv_val, bv[i]->bv_len); 4961fcced4cSJordan Brown if (dav[i].directory_value_rpc_val == NULL) 4971fcced4cSJordan Brown goto nomem; 4981fcced4cSJordan Brown dav[i].directory_value_rpc_len = bv[i]->bv_len; 4991fcced4cSJordan Brown } 5001fcced4cSJordan Brown 5011fcced4cSJordan Brown return (NULL); 5021fcced4cSJordan Brown 5031fcced4cSJordan Brown nomem: 5041fcced4cSJordan Brown return (directory_error("ENOMEM.bv_list_dav", 5051fcced4cSJordan Brown "Insufficient memory copying values")); 5061fcced4cSJordan Brown } 5071fcced4cSJordan Brown 5081fcced4cSJordan Brown #if defined(DUMP_VALUES) 5091fcced4cSJordan Brown static 5101fcced4cSJordan Brown void 5111fcced4cSJordan Brown dump_bv_list(const char *attr, struct berval **bv) 5121fcced4cSJordan Brown { 5131fcced4cSJordan Brown int i; 5141fcced4cSJordan Brown 5151fcced4cSJordan Brown if (bv == NULL) { 5161fcced4cSJordan Brown (void) fprintf(stderr, "%s: (empty)\n", attr); 5171fcced4cSJordan Brown return; 5181fcced4cSJordan Brown } 5191fcced4cSJordan Brown for (i = 0; bv[i] != NULL; i++) { 5201fcced4cSJordan Brown (void) fprintf(stderr, "%s[%d] =\n", attr, i); 5211fcced4cSJordan Brown dump(stderr, " ", bv[i]->bv_val, bv[i]->bv_len); 5221fcced4cSJordan Brown } 5231fcced4cSJordan Brown } 5241fcced4cSJordan Brown #endif /* DUMP_VALUES */ 5251fcced4cSJordan Brown 5261fcced4cSJordan Brown /* 5271fcced4cSJordan Brown * Return the domain associated with the specified entry. 5281fcced4cSJordan Brown */ 5291fcced4cSJordan Brown static 5301fcced4cSJordan Brown directory_error_t 5311fcced4cSJordan Brown get_domain( 5321fcced4cSJordan Brown LDAP *ld, 5331fcced4cSJordan Brown LDAPMessage *msg, 5341fcced4cSJordan Brown char **domain) 5351fcced4cSJordan Brown { 5361fcced4cSJordan Brown *domain = NULL; 5371fcced4cSJordan Brown 5381fcced4cSJordan Brown char *dn = ldap_get_dn(ld, msg); 5391fcced4cSJordan Brown if (dn == NULL) { 5401fcced4cSJordan Brown char buf[100]; /* big enough for any int */ 5411fcced4cSJordan Brown char *m; 5421fcced4cSJordan Brown char *s; 5431fcced4cSJordan Brown int err = ldap_get_lderrno(ld, &m, &s); 5441fcced4cSJordan Brown (void) snprintf(buf, sizeof (buf), "%d", err); 5451fcced4cSJordan Brown 5461fcced4cSJordan Brown return directory_error("AD.get_domain.ldap_get_dn", 5471fcced4cSJordan Brown "ldap_get_dn: %1 (%2)\n" 5481fcced4cSJordan Brown "matched: %3\n" 5491fcced4cSJordan Brown "error: %4", 5501fcced4cSJordan Brown ldap_err2string(err), buf, 5511fcced4cSJordan Brown m == NULL ? "(null)" : m, 5521fcced4cSJordan Brown s == NULL ? "(null)" : s, 5531fcced4cSJordan Brown NULL); 5541fcced4cSJordan Brown } 5551fcced4cSJordan Brown 5561fcced4cSJordan Brown *domain = adutils_dn2dns(dn); 5571fcced4cSJordan Brown if (*domain == NULL) { 5581fcced4cSJordan Brown directory_error_t de; 5591fcced4cSJordan Brown 5601fcced4cSJordan Brown de = directory_error("Unknown.get_domain.adutils_dn2dns", 5611fcced4cSJordan Brown "get_domain: Unexpected error from adutils_dn2dns(%1)", 5621fcced4cSJordan Brown dn, NULL); 5631fcced4cSJordan Brown free(dn); 5641fcced4cSJordan Brown return (de); 5651fcced4cSJordan Brown } 5661fcced4cSJordan Brown free(dn); 5671fcced4cSJordan Brown 5681fcced4cSJordan Brown return (NULL); 5691fcced4cSJordan Brown } 5701fcced4cSJordan Brown 5711fcced4cSJordan Brown /* 5721fcced4cSJordan Brown * Given an error report from libadutils, generate a directory_error_t. 5731fcced4cSJordan Brown */ 5741fcced4cSJordan Brown static 5751fcced4cSJordan Brown directory_error_t 5761fcced4cSJordan Brown directory_provider_ad_utils_error(char *func, int rc) 5771fcced4cSJordan Brown { 5781fcced4cSJordan Brown char rcstr[100]; /* plenty for any int */ 5791fcced4cSJordan Brown char code[100]; /* plenty for any int */ 5801fcced4cSJordan Brown (void) snprintf(rcstr, sizeof (rcstr), "%d", rc); 5811fcced4cSJordan Brown (void) snprintf(code, sizeof (code), "ADUTILS.%d", rc); 5821fcced4cSJordan Brown 5831fcced4cSJordan Brown return (directory_error(code, 5841fcced4cSJordan Brown "Error %2 from adutils function %1", func, rcstr, NULL)); 5851fcced4cSJordan Brown } 5861fcced4cSJordan Brown 5871fcced4cSJordan Brown struct directory_provider_static directory_provider_ad = { 5881fcced4cSJordan Brown "AD", 5891fcced4cSJordan Brown directory_provider_ad_get, 5901fcced4cSJordan Brown }; 591