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