14520Snw141292 /* 24520Snw141292 * CDDL HEADER START 34520Snw141292 * 44520Snw141292 * The contents of this file are subject to the terms of the 54520Snw141292 * Common Development and Distribution License (the "License"). 64520Snw141292 * You may not use this file except in compliance with the License. 74520Snw141292 * 84520Snw141292 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94520Snw141292 * or http://www.opensolaris.org/os/licensing. 104520Snw141292 * See the License for the specific language governing permissions 114520Snw141292 * and limitations under the License. 124520Snw141292 * 134520Snw141292 * When distributing Covered Code, include this CDDL HEADER in each 144520Snw141292 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154520Snw141292 * If applicable, add the following below this CDDL HEADER, with the 164520Snw141292 * fields enclosed by brackets "[]" replaced with your own identifying 174520Snw141292 * information: Portions Copyright [yyyy] [name of copyright owner] 184520Snw141292 * 194520Snw141292 * CDDL HEADER END 204520Snw141292 */ 214520Snw141292 224520Snw141292 /* 235908Sjp151216 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 244520Snw141292 * Use is subject to license terms. 254520Snw141292 */ 264520Snw141292 274520Snw141292 /* 284520Snw141292 * Processes name2sid & sid2name batched lookups for a given user or 294520Snw141292 * computer from an AD Directory server using GSSAPI authentication 304520Snw141292 */ 314520Snw141292 324520Snw141292 #include <stdio.h> 334520Snw141292 #include <stdlib.h> 344520Snw141292 #include <alloca.h> 354520Snw141292 #include <string.h> 364520Snw141292 #include <strings.h> 374520Snw141292 #include <lber.h> 384520Snw141292 #include <ldap.h> 394520Snw141292 #include <sasl/sasl.h> 404520Snw141292 #include <string.h> 414520Snw141292 #include <ctype.h> 424520Snw141292 #include <pthread.h> 434520Snw141292 #include <synch.h> 444520Snw141292 #include <atomic.h> 454520Snw141292 #include <errno.h> 464520Snw141292 #include <assert.h> 474520Snw141292 #include <limits.h> 488040SBaban.Kenkre@Sun.COM #include <time.h> 495696Snw141292 #include <sys/u8_textprep.h> 508040SBaban.Kenkre@Sun.COM #include "libadutils.h" 516616Sdm199847 #include "nldaputils.h" 524520Snw141292 #include "idmapd.h" 534520Snw141292 544520Snw141292 /* Attribute names and filter format strings */ 555447Snw141292 #define SAN "sAMAccountName" 565447Snw141292 #define OBJSID "objectSid" 575447Snw141292 #define OBJCLASS "objectClass" 584520Snw141292 #define SANFILTER "(sAMAccountName=%.*s)" 595447Snw141292 #define OBJSIDFILTER "(objectSid=%s)" 604520Snw141292 618040SBaban.Kenkre@Sun.COM void idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, 628040SBaban.Kenkre@Sun.COM int qid, void *argp); 634520Snw141292 644520Snw141292 /* 654520Snw141292 * A place to put the results of a batched (async) query 664520Snw141292 * 674520Snw141292 * There is one of these for every query added to a batch object 684520Snw141292 * (idmap_query_state, see below). 694520Snw141292 */ 704520Snw141292 typedef struct idmap_q { 715447Snw141292 /* 725447Snw141292 * data used for validating search result entries for name->SID 735731Sbaban * lookups 745447Snw141292 */ 755696Snw141292 char *ecanonname; /* expected canon name */ 765696Snw141292 char *edomain; /* expected domain name */ 775731Sbaban int eunixtype; /* expected unix type */ 785447Snw141292 /* results */ 795696Snw141292 char **canonname; /* actual canon name */ 804520Snw141292 char **domain; /* name of domain of object */ 815731Sbaban char **sid; /* stringified SID */ 825731Sbaban rid_t *rid; /* RID */ 835731Sbaban int *sid_type; /* user or group SID? */ 845731Sbaban char **unixname; /* unixname for name mapping */ 856386Sjp151216 char **dn; /* DN of entry */ 866386Sjp151216 char **attr; /* Attr for name mapping */ 876386Sjp151216 char **value; /* value for name mapping */ 884520Snw141292 idmap_retcode *rc; 898040SBaban.Kenkre@Sun.COM adutils_rc ad_rc; 908040SBaban.Kenkre@Sun.COM adutils_result_t *result; 915447Snw141292 926531Sjp151216 /* 936531Sjp151216 * The LDAP search entry result is placed here to be processed 946531Sjp151216 * when the search done result is received. 956531Sjp151216 */ 966531Sjp151216 LDAPMessage *search_res; /* The LDAP search result */ 974520Snw141292 } idmap_q_t; 984520Snw141292 994520Snw141292 /* Batch context structure; typedef is in header file */ 1004520Snw141292 struct idmap_query_state { 1018040SBaban.Kenkre@Sun.COM adutils_query_state_t *qs; 102*8361SJulian.Pullen@Sun.COM int qsize; /* Queue size */ 103*8361SJulian.Pullen@Sun.COM uint32_t qcount; /* Number of queued requests */ 1045731Sbaban const char *ad_unixuser_attr; 1055731Sbaban const char *ad_unixgroup_attr; 1064520Snw141292 idmap_q_t queries[1]; /* array of query results */ 1074520Snw141292 }; 1084520Snw141292 1094644Sbaban static pthread_t reaperid = 0; 1104520Snw141292 1114520Snw141292 /* 1124520Snw141292 * Keep connection management simple for now, extend or replace later 1134520Snw141292 * with updated libsldap code. 1144520Snw141292 */ 1154520Snw141292 #define ADREAPERSLEEP 60 1164520Snw141292 1174520Snw141292 /* 1184520Snw141292 * Idle connection reaping side of connection management 1194520Snw141292 * 1204520Snw141292 * Every minute wake up and look for connections that have been idle for 1214520Snw141292 * five minutes or more and close them. 1224520Snw141292 */ 1234520Snw141292 /*ARGSUSED*/ 1244520Snw141292 static 1254520Snw141292 void 1264520Snw141292 adreaper(void *arg) 1274520Snw141292 { 1284520Snw141292 timespec_t ts; 1294520Snw141292 1304520Snw141292 ts.tv_sec = ADREAPERSLEEP; 1314520Snw141292 ts.tv_nsec = 0; 1324520Snw141292 1334520Snw141292 for (;;) { 1344520Snw141292 /* 1354520Snw141292 * nanosleep(3RT) is thead-safe (no SIGALRM) and more 1364520Snw141292 * portable than usleep(3C) 1374520Snw141292 */ 1384520Snw141292 (void) nanosleep(&ts, NULL); 1398040SBaban.Kenkre@Sun.COM adutils_reap_idle_connections(); 1404520Snw141292 } 1414520Snw141292 } 1424520Snw141292 1434520Snw141292 /* 1444520Snw141292 * Take ad_host_config_t information, create a ad_host_t, 1454520Snw141292 * populate it and add it to the list of hosts. 1464520Snw141292 */ 1474520Snw141292 1484520Snw141292 int 1498040SBaban.Kenkre@Sun.COM idmap_add_ds(adutils_ad_t *ad, const char *host, int port) 1504520Snw141292 { 1518040SBaban.Kenkre@Sun.COM int ret = -1; 1524520Snw141292 1538040SBaban.Kenkre@Sun.COM if (adutils_add_ds(ad, host, port) == ADUTILS_SUCCESS) 1548040SBaban.Kenkre@Sun.COM ret = 0; 1554520Snw141292 1564520Snw141292 /* Start reaper if it doesn't exist */ 1578040SBaban.Kenkre@Sun.COM if (ret == 0 && reaperid == 0) 1584520Snw141292 (void) pthread_create(&reaperid, NULL, 1594520Snw141292 (void *(*)(void *))adreaper, (void *)NULL); 1604520Snw141292 return (ret); 1614520Snw141292 } 1624520Snw141292 1634520Snw141292 static 1648040SBaban.Kenkre@Sun.COM idmap_retcode 1658040SBaban.Kenkre@Sun.COM map_adrc2idmaprc(adutils_rc adrc) 1664520Snw141292 { 1678040SBaban.Kenkre@Sun.COM switch (adrc) { 1688040SBaban.Kenkre@Sun.COM case ADUTILS_SUCCESS: 1698040SBaban.Kenkre@Sun.COM return (IDMAP_SUCCESS); 1708040SBaban.Kenkre@Sun.COM case ADUTILS_ERR_NOTFOUND: 1718040SBaban.Kenkre@Sun.COM return (IDMAP_ERR_NOTFOUND); 1728040SBaban.Kenkre@Sun.COM case ADUTILS_ERR_MEMORY: 1738040SBaban.Kenkre@Sun.COM return (IDMAP_ERR_MEMORY); 1748040SBaban.Kenkre@Sun.COM case ADUTILS_ERR_DOMAIN: 1758040SBaban.Kenkre@Sun.COM return (IDMAP_ERR_DOMAIN); 1768040SBaban.Kenkre@Sun.COM case ADUTILS_ERR_OTHER: 1778040SBaban.Kenkre@Sun.COM return (IDMAP_ERR_OTHER); 1788040SBaban.Kenkre@Sun.COM case ADUTILS_ERR_RETRIABLE_NET_ERR: 1798040SBaban.Kenkre@Sun.COM return (IDMAP_ERR_RETRIABLE_NET_ERR); 1808040SBaban.Kenkre@Sun.COM default: 1818040SBaban.Kenkre@Sun.COM return (IDMAP_ERR_INTERNAL); 1824520Snw141292 } 1838040SBaban.Kenkre@Sun.COM /* NOTREACHED */ 1844520Snw141292 } 1854520Snw141292 1868040SBaban.Kenkre@Sun.COM idmap_retcode 1878040SBaban.Kenkre@Sun.COM idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries, 1888040SBaban.Kenkre@Sun.COM idmap_query_state_t **state) 1894520Snw141292 { 1908040SBaban.Kenkre@Sun.COM idmap_query_state_t *new_state; 1918040SBaban.Kenkre@Sun.COM adutils_rc rc; 1924520Snw141292 1934520Snw141292 *state = NULL; 1944520Snw141292 195*8361SJulian.Pullen@Sun.COM assert(ad != NULL); 1964520Snw141292 1974520Snw141292 new_state = calloc(1, sizeof (idmap_query_state_t) + 1984520Snw141292 (nqueries - 1) * sizeof (idmap_q_t)); 1994520Snw141292 if (new_state == NULL) 2004520Snw141292 return (IDMAP_ERR_MEMORY); 2014520Snw141292 2028040SBaban.Kenkre@Sun.COM if ((rc = adutils_lookup_batch_start(ad, nqueries, 2038040SBaban.Kenkre@Sun.COM idmap_ldap_res_search_cb, new_state, &new_state->qs)) 2048040SBaban.Kenkre@Sun.COM != ADUTILS_SUCCESS) { 2058040SBaban.Kenkre@Sun.COM free(new_state); 2068040SBaban.Kenkre@Sun.COM return (map_adrc2idmaprc(rc)); 2078040SBaban.Kenkre@Sun.COM } 2084520Snw141292 209*8361SJulian.Pullen@Sun.COM new_state->qsize = nqueries; 2104520Snw141292 *state = new_state; 2114520Snw141292 return (IDMAP_SUCCESS); 2124520Snw141292 } 2134520Snw141292 2144520Snw141292 /* 2155731Sbaban * Set unixuser_attr and unixgroup_attr for AD-based name mapping 2165731Sbaban */ 2175731Sbaban void 2185731Sbaban idmap_lookup_batch_set_unixattr(idmap_query_state_t *state, 2195908Sjp151216 const char *unixuser_attr, const char *unixgroup_attr) 2205908Sjp151216 { 2215731Sbaban state->ad_unixuser_attr = unixuser_attr; 2225731Sbaban state->ad_unixgroup_attr = unixgroup_attr; 2235731Sbaban } 2245731Sbaban 2255731Sbaban /* 2265696Snw141292 * Take parsed attribute values from a search result entry and check if 2275696Snw141292 * it is the result that was desired and, if so, set the result fields 2285696Snw141292 * of the given idmap_q_t. 2295696Snw141292 * 2305731Sbaban * Frees the unused char * values. 2314520Snw141292 */ 2324520Snw141292 static 2335696Snw141292 void 2346386Sjp151216 idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr, 2356386Sjp151216 char *sid, rid_t rid, int sid_type, char *unixname) 2364520Snw141292 { 2375696Snw141292 char *domain; 2385731Sbaban int err1, err2; 2395696Snw141292 2405696Snw141292 assert(dn != NULL); 2415696Snw141292 2428040SBaban.Kenkre@Sun.COM if ((domain = adutils_dn2dns(dn)) == NULL) 2435696Snw141292 goto out; 2445696Snw141292 2455731Sbaban if (q->ecanonname != NULL && san != NULL) { 2465731Sbaban /* Check that this is the canonname that we were looking for */ 2475696Snw141292 if (u8_strcmp(q->ecanonname, san, 0, 2485696Snw141292 U8_STRCMP_CI_LOWER, /* no normalization, for now */ 2495731Sbaban U8_UNICODE_LATEST, &err1) != 0 || err1 != 0) 2505731Sbaban goto out; 2515731Sbaban } 2525731Sbaban 2535731Sbaban if (q->edomain != NULL) { 2545731Sbaban /* Check that this is the domain that we were looking for */ 2555731Sbaban if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER, 2565696Snw141292 U8_UNICODE_LATEST, &err2) != 0 || err2 != 0) 2575696Snw141292 goto out; 2585696Snw141292 } 2595696Snw141292 2606386Sjp151216 /* Copy the DN and attr and value */ 2616386Sjp151216 if (q->dn != NULL) 2626386Sjp151216 *q->dn = strdup(dn); 2636386Sjp151216 2646386Sjp151216 if (q->attr != NULL && attr != NULL) 2656386Sjp151216 *q->attr = strdup(attr); 2666386Sjp151216 2676386Sjp151216 if (q->value != NULL && unixname != NULL) 2686386Sjp151216 *q->value = strdup(unixname); 2696386Sjp151216 2705731Sbaban /* Set results */ 2715731Sbaban if (q->sid) { 2725731Sbaban *q->sid = sid; 2735731Sbaban sid = NULL; 2745731Sbaban } 2755731Sbaban if (q->rid) 2765731Sbaban *q->rid = rid; 2775731Sbaban if (q->sid_type) 2785731Sbaban *q->sid_type = sid_type; 2795731Sbaban if (q->unixname) { 2805731Sbaban *q->unixname = unixname; 2815731Sbaban unixname = NULL; 2825731Sbaban } 2835731Sbaban if (q->domain != NULL) { 2845731Sbaban *q->domain = domain; 2855731Sbaban domain = NULL; 2865731Sbaban } 2875731Sbaban if (q->canonname != NULL) { 2886616Sdm199847 /* 2896616Sdm199847 * The caller may be replacing the given winname by its 2906616Sdm199847 * canonical name and therefore free any old name before 2916616Sdm199847 * overwriting the field by the canonical name. 2926616Sdm199847 */ 2936616Sdm199847 free(*q->canonname); 2945731Sbaban *q->canonname = san; 2955731Sbaban san = NULL; 2965731Sbaban } 2975731Sbaban 2988040SBaban.Kenkre@Sun.COM q->ad_rc = ADUTILS_SUCCESS; 2995696Snw141292 3005696Snw141292 out: 3015696Snw141292 /* Free unused attribute values */ 3025696Snw141292 free(san); 3035696Snw141292 free(sid); 3045696Snw141292 free(domain); 3055731Sbaban free(unixname); 3064520Snw141292 } 3074520Snw141292 3084520Snw141292 #define BVAL_CASEEQ(bv, str) \ 3094520Snw141292 (((*(bv))->bv_len == (sizeof (str) - 1)) && \ 3104520Snw141292 strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0) 3114520Snw141292 3124520Snw141292 /* 3135696Snw141292 * Extract the class of the result entry. Returns 1 on success, 0 on 3145696Snw141292 * failure. 3154520Snw141292 */ 3164520Snw141292 static 3175447Snw141292 int 3185696Snw141292 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type) 3194520Snw141292 { 3204520Snw141292 BerValue **cbval; 3214520Snw141292 3225696Snw141292 *sid_type = _IDMAP_T_OTHER; 3234520Snw141292 if (bvalues == NULL) 3245447Snw141292 return (0); 3254520Snw141292 3265696Snw141292 /* 3275696Snw141292 * We iterate over all the values because computer is a 3285696Snw141292 * sub-class of user. 3295696Snw141292 */ 3304520Snw141292 for (cbval = bvalues; *cbval != NULL; cbval++) { 3314520Snw141292 if (BVAL_CASEEQ(cbval, "Computer")) { 3325696Snw141292 *sid_type = _IDMAP_T_COMPUTER; 3335696Snw141292 break; 3344520Snw141292 } else if (BVAL_CASEEQ(cbval, "Group")) { 3355696Snw141292 *sid_type = _IDMAP_T_GROUP; 3365696Snw141292 break; 3374520Snw141292 } else if (BVAL_CASEEQ(cbval, "USER")) { 3385696Snw141292 *sid_type = _IDMAP_T_USER; 3395696Snw141292 /* Continue looping -- this may be a computer yet */ 3405696Snw141292 } 3415696Snw141292 /* 3425696Snw141292 * "else if (*sid_type = _IDMAP_T_USER)" then this is a 3435696Snw141292 * new sub-class of user -- what to do with it?? 3445696Snw141292 */ 3454520Snw141292 } 3465447Snw141292 3475447Snw141292 return (1); 3484520Snw141292 } 3494520Snw141292 3504520Snw141292 /* 3514520Snw141292 * Handle a given search result entry 3524520Snw141292 */ 3534520Snw141292 static 3544520Snw141292 void 3558040SBaban.Kenkre@Sun.COM idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q, 3568040SBaban.Kenkre@Sun.COM LDAPMessage *res, LDAP *ld) 3574520Snw141292 { 3584520Snw141292 BerElement *ber = NULL; 3594520Snw141292 BerValue **bvalues; 3605696Snw141292 char *attr; 3615731Sbaban const char *unixuser_attr = NULL; 3625731Sbaban const char *unixgroup_attr = NULL; 3635731Sbaban char *unixuser = NULL; 3645731Sbaban char *unixgroup = NULL; 3655696Snw141292 char *dn = NULL; 3665696Snw141292 char *san = NULL; 3675696Snw141292 char *sid = NULL; 3685696Snw141292 rid_t rid = 0; 3695731Sbaban int sid_type = _IDMAP_T_UNDEF; 3705447Snw141292 int has_class, has_san, has_sid; 3715731Sbaban int has_unixuser, has_unixgroup; 3725447Snw141292 3735731Sbaban assert(q->rc != NULL); 3745731Sbaban 3758040SBaban.Kenkre@Sun.COM if ((dn = ldap_get_dn(ld, res)) == NULL) 3764520Snw141292 return; 3774520Snw141292 3785447Snw141292 assert(q->domain == NULL || *q->domain == NULL); 3794520Snw141292 3805731Sbaban /* 3815731Sbaban * If the caller has requested unixname then determine the 3825731Sbaban * AD attribute name that will have the unixname. 3835731Sbaban */ 3845731Sbaban if (q->unixname != NULL) { 3855731Sbaban if (q->eunixtype == _IDMAP_T_USER) 3865731Sbaban unixuser_attr = state->ad_unixuser_attr; 3875731Sbaban else if (q->eunixtype == _IDMAP_T_GROUP) 3885731Sbaban unixgroup_attr = state->ad_unixgroup_attr; 3895731Sbaban else if (q->eunixtype == _IDMAP_T_UNDEF) { 3905731Sbaban /* 3915731Sbaban * This is the case where we don't know 3925731Sbaban * before hand whether we need unixuser 3935731Sbaban * or unixgroup. This will be determined 3945731Sbaban * by the "sid_type" (i.e whether the given 3955731Sbaban * winname is user or group). If sid_type 3965731Sbaban * turns out to be user we will return 3975731Sbaban * unixuser (if found) and if it is a group 3985731Sbaban * we will return unixgroup (if found). We 3995731Sbaban * lookup for both ad_unixuser_attr and 4005731Sbaban * ad_unixgroup_attr and discard one of them 4015731Sbaban * after we know the "sidtype". This 4025731Sbaban * supports the following type of lookups. 4035731Sbaban * 4045731Sbaban * Example: 4055731Sbaban * $idmap show -c winname:foo 4065731Sbaban * In the above example, idmap will 4075731Sbaban * return uid if winname is winuser 4085731Sbaban * and gid if winname is wingroup. 4095731Sbaban */ 4105731Sbaban unixuser_attr = state->ad_unixuser_attr; 4115731Sbaban unixgroup_attr = state->ad_unixgroup_attr; 4125731Sbaban } 4135731Sbaban } 4145731Sbaban 4155731Sbaban has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0; 4168040SBaban.Kenkre@Sun.COM for (attr = ldap_first_attribute(ld, res, &ber); attr != NULL; 4178040SBaban.Kenkre@Sun.COM attr = ldap_next_attribute(ld, res, ber)) { 4184520Snw141292 bvalues = NULL; /* for memory management below */ 4194520Snw141292 4204520Snw141292 /* 4214520Snw141292 * If this is an attribute we are looking for and 4224520Snw141292 * haven't seen it yet, parse it 4234520Snw141292 */ 4245731Sbaban if (q->sid != NULL && !has_sid && 4255447Snw141292 strcasecmp(attr, OBJSID) == 0) { 4268040SBaban.Kenkre@Sun.COM bvalues = ldap_get_values_len(ld, res, attr); 4278040SBaban.Kenkre@Sun.COM if (bvalues != NULL) { 4288040SBaban.Kenkre@Sun.COM sid = adutils_bv_objsid2sidstr( 4298040SBaban.Kenkre@Sun.COM bvalues[0], &rid); 4308040SBaban.Kenkre@Sun.COM has_sid = (sid != NULL); 4318040SBaban.Kenkre@Sun.COM } 4325447Snw141292 } else if (!has_san && strcasecmp(attr, SAN) == 0) { 4338040SBaban.Kenkre@Sun.COM bvalues = ldap_get_values_len(ld, res, attr); 4348040SBaban.Kenkre@Sun.COM if (bvalues != NULL) { 4358040SBaban.Kenkre@Sun.COM san = adutils_bv_name2str(bvalues[0]); 4368040SBaban.Kenkre@Sun.COM has_san = (san != NULL); 4378040SBaban.Kenkre@Sun.COM } 4385447Snw141292 } else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) { 4398040SBaban.Kenkre@Sun.COM bvalues = ldap_get_values_len(ld, res, attr); 4405696Snw141292 has_class = idmap_bv_objclass2sidtype(bvalues, 4415696Snw141292 &sid_type); 4425731Sbaban if (has_class && q->unixname != NULL && 4435731Sbaban q->eunixtype == _IDMAP_T_UNDEF) { 4445731Sbaban /* 4455731Sbaban * This is the case where we didn't 4465731Sbaban * know whether we wanted unixuser or 4475731Sbaban * unixgroup as described above. 4485731Sbaban * Now since we know the "sid_type" 4495731Sbaban * we discard the unwanted value 4505731Sbaban * if it was retrieved before we 4515731Sbaban * got here. 4525731Sbaban */ 4535731Sbaban if (sid_type == _IDMAP_T_USER) { 4545731Sbaban free(unixgroup); 4555731Sbaban unixgroup_attr = unixgroup = NULL; 4565731Sbaban } else if (sid_type == _IDMAP_T_GROUP) { 4575731Sbaban free(unixuser); 4585731Sbaban unixuser_attr = unixuser = NULL; 4595731Sbaban } else { 4605731Sbaban free(unixuser); 4615731Sbaban free(unixgroup); 4625731Sbaban unixuser_attr = unixuser = NULL; 4635731Sbaban unixgroup_attr = unixgroup = NULL; 4645731Sbaban } 4655731Sbaban } 4665731Sbaban } else if (!has_unixuser && unixuser_attr != NULL && 4675731Sbaban strcasecmp(attr, unixuser_attr) == 0) { 4688040SBaban.Kenkre@Sun.COM bvalues = ldap_get_values_len(ld, res, attr); 4698040SBaban.Kenkre@Sun.COM if (bvalues != NULL) { 4708040SBaban.Kenkre@Sun.COM unixuser = adutils_bv_name2str(bvalues[0]); 4718040SBaban.Kenkre@Sun.COM has_unixuser = (unixuser != NULL); 4728040SBaban.Kenkre@Sun.COM } 4736386Sjp151216 4745731Sbaban } else if (!has_unixgroup && unixgroup_attr != NULL && 4755731Sbaban strcasecmp(attr, unixgroup_attr) == 0) { 4768040SBaban.Kenkre@Sun.COM bvalues = ldap_get_values_len(ld, res, attr); 4778040SBaban.Kenkre@Sun.COM if (bvalues != NULL) { 4788040SBaban.Kenkre@Sun.COM unixgroup = adutils_bv_name2str(bvalues[0]); 4798040SBaban.Kenkre@Sun.COM has_unixgroup = (unixgroup != NULL); 4808040SBaban.Kenkre@Sun.COM } 4814520Snw141292 } 4824520Snw141292 4834520Snw141292 if (bvalues != NULL) 4844520Snw141292 ldap_value_free_len(bvalues); 4854520Snw141292 ldap_memfree(attr); 4864520Snw141292 4875696Snw141292 if (has_class && has_san && 4885731Sbaban (q->sid == NULL || has_sid) && 4895731Sbaban (unixuser_attr == NULL || has_unixuser) && 4905731Sbaban (unixgroup_attr == NULL || has_unixgroup)) { 4915731Sbaban /* Got what we need */ 4925447Snw141292 break; 4935696Snw141292 } 4944520Snw141292 } 4954520Snw141292 4965731Sbaban if (!has_class) { 4975731Sbaban /* 4985731Sbaban * Didn't find objectclass. Something's wrong with our 4995731Sbaban * AD data. 5005731Sbaban */ 5015731Sbaban free(san); 5025731Sbaban free(sid); 5035731Sbaban free(unixuser); 5045731Sbaban free(unixgroup); 5055731Sbaban } else { 5065731Sbaban /* 5075731Sbaban * Either we got what we needed and came out of the loop 5085731Sbaban * early OR we completed the loop in which case we didn't 5095731Sbaban * find some attributes that we were looking for. In either 5105731Sbaban * case set the result with what we got. 5115731Sbaban */ 5126386Sjp151216 idmap_setqresults(q, san, dn, 5136386Sjp151216 (unixuser != NULL) ? unixuser_attr : unixgroup_attr, 5146386Sjp151216 sid, rid, sid_type, 5155731Sbaban (unixuser != NULL) ? unixuser : unixgroup); 5165731Sbaban } 5175731Sbaban 5184520Snw141292 if (ber != NULL) 5194520Snw141292 ber_free(ber, 0); 5204520Snw141292 5214520Snw141292 ldap_memfree(dn); 5224520Snw141292 } 5234520Snw141292 5248040SBaban.Kenkre@Sun.COM void 5258040SBaban.Kenkre@Sun.COM idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid, 5268040SBaban.Kenkre@Sun.COM void *argp) 5274520Snw141292 { 5288040SBaban.Kenkre@Sun.COM idmap_query_state_t *state = (idmap_query_state_t *)argp; 5298040SBaban.Kenkre@Sun.COM idmap_q_t *q = &(state->queries[qid]); 5304520Snw141292 5314520Snw141292 switch (rc) { 5324520Snw141292 case LDAP_RES_SEARCH_RESULT: 5338040SBaban.Kenkre@Sun.COM if (q->search_res != NULL) { 5348040SBaban.Kenkre@Sun.COM idmap_extract_object(state, q, q->search_res, ld); 5358040SBaban.Kenkre@Sun.COM (void) ldap_msgfree(q->search_res); 5368040SBaban.Kenkre@Sun.COM q->search_res = NULL; 5378040SBaban.Kenkre@Sun.COM } else 5388040SBaban.Kenkre@Sun.COM q->ad_rc = ADUTILS_ERR_NOTFOUND; 5394520Snw141292 break; 5408040SBaban.Kenkre@Sun.COM case LDAP_RES_SEARCH_ENTRY: 5418040SBaban.Kenkre@Sun.COM if (q->search_res == NULL) { 5428040SBaban.Kenkre@Sun.COM q->search_res = *res; 5438040SBaban.Kenkre@Sun.COM *res = NULL; 5448040SBaban.Kenkre@Sun.COM } 5454520Snw141292 break; 5464520Snw141292 default: 5474520Snw141292 break; 5484520Snw141292 } 5494884Sjp151216 } 5504884Sjp151216 5515447Snw141292 static 5525447Snw141292 void 5535447Snw141292 idmap_cleanup_batch(idmap_query_state_t *batch) 5545447Snw141292 { 5555447Snw141292 int i; 5565447Snw141292 5575447Snw141292 for (i = 0; i < batch->qcount; i++) { 5585696Snw141292 if (batch->queries[i].ecanonname != NULL) 5595696Snw141292 free(batch->queries[i].ecanonname); 5605696Snw141292 batch->queries[i].ecanonname = NULL; 5615696Snw141292 if (batch->queries[i].edomain != NULL) 5625696Snw141292 free(batch->queries[i].edomain); 5635696Snw141292 batch->queries[i].edomain = NULL; 5645447Snw141292 } 5655447Snw141292 } 5665447Snw141292 5674884Sjp151216 /* 5684884Sjp151216 * This routine frees the idmap_query_state_t structure 5694884Sjp151216 */ 5704520Snw141292 void 5714884Sjp151216 idmap_lookup_release_batch(idmap_query_state_t **state) 5724520Snw141292 { 5738040SBaban.Kenkre@Sun.COM if (state == NULL || *state == NULL) 5748040SBaban.Kenkre@Sun.COM return; 5758040SBaban.Kenkre@Sun.COM adutils_lookup_batch_release(&(*state)->qs); 5765447Snw141292 idmap_cleanup_batch(*state); 5774520Snw141292 free(*state); 5784520Snw141292 *state = NULL; 5794520Snw141292 } 5804520Snw141292 5814520Snw141292 idmap_retcode 5825968Snw141292 idmap_lookup_batch_end(idmap_query_state_t **state) 5834520Snw141292 { 5848040SBaban.Kenkre@Sun.COM adutils_rc ad_rc; 5858040SBaban.Kenkre@Sun.COM int i; 5868040SBaban.Kenkre@Sun.COM idmap_query_state_t *id_qs = *state; 5874520Snw141292 5888040SBaban.Kenkre@Sun.COM ad_rc = adutils_lookup_batch_end(&id_qs->qs); 5894520Snw141292 5908040SBaban.Kenkre@Sun.COM /* 5918040SBaban.Kenkre@Sun.COM * Map adutils rc to idmap_retcode in each 5928040SBaban.Kenkre@Sun.COM * query because consumers in dbutils.c 5938040SBaban.Kenkre@Sun.COM * expects idmap_retcode. 5948040SBaban.Kenkre@Sun.COM */ 5958040SBaban.Kenkre@Sun.COM for (i = 0; i < id_qs->qcount; i++) { 5968040SBaban.Kenkre@Sun.COM *id_qs->queries[i].rc = 5978040SBaban.Kenkre@Sun.COM map_adrc2idmaprc(id_qs->queries[i].ad_rc); 5984520Snw141292 } 5994884Sjp151216 idmap_lookup_release_batch(state); 6008040SBaban.Kenkre@Sun.COM return (map_adrc2idmaprc(ad_rc)); 6014520Snw141292 } 6024520Snw141292 6034520Snw141292 /* 6044520Snw141292 * Send one prepared search, queue up msgid, process what results are 6054520Snw141292 * available 6064520Snw141292 */ 6074520Snw141292 static 6084520Snw141292 idmap_retcode 6096386Sjp151216 idmap_batch_add1(idmap_query_state_t *state, const char *filter, 6106386Sjp151216 char *ecanonname, char *edomain, int eunixtype, 6116386Sjp151216 char **dn, char **attr, char **value, 6126386Sjp151216 char **canonname, char **dname, 6136386Sjp151216 char **sid, rid_t *rid, int *sid_type, char **unixname, 6146386Sjp151216 idmap_retcode *rc) 6154520Snw141292 { 6168040SBaban.Kenkre@Sun.COM adutils_rc ad_rc; 6178040SBaban.Kenkre@Sun.COM int qid, i; 6184520Snw141292 idmap_q_t *q; 6195696Snw141292 static char *attrs[] = { 6205696Snw141292 SAN, 6215696Snw141292 OBJSID, 6225696Snw141292 OBJCLASS, 6235731Sbaban NULL, /* placeholder for unixname attr */ 6245731Sbaban NULL, /* placeholder for unixname attr */ 6255696Snw141292 NULL 6265696Snw141292 }; 6274520Snw141292 628*8361SJulian.Pullen@Sun.COM qid = atomic_inc_32_nv(&state->qcount) - 1; 6294520Snw141292 q = &(state->queries[qid]); 6304520Snw141292 631*8361SJulian.Pullen@Sun.COM assert(qid < state->qsize); 632*8361SJulian.Pullen@Sun.COM 6335696Snw141292 /* 6348040SBaban.Kenkre@Sun.COM * Remember the expected canonname, domainname and unix type 6358040SBaban.Kenkre@Sun.COM * so we can check the results * against it 6365696Snw141292 */ 6375696Snw141292 q->ecanonname = ecanonname; 6385696Snw141292 q->edomain = edomain; 6395731Sbaban q->eunixtype = eunixtype; 6405447Snw141292 6414520Snw141292 /* Remember where to put the results */ 6425696Snw141292 q->canonname = canonname; 6435731Sbaban q->sid = sid; 6444520Snw141292 q->domain = dname; 6454520Snw141292 q->rid = rid; 6464520Snw141292 q->sid_type = sid_type; 6474520Snw141292 q->rc = rc; 6485731Sbaban q->unixname = unixname; 6496386Sjp151216 q->dn = dn; 6506386Sjp151216 q->attr = attr; 6516386Sjp151216 q->value = value; 6525731Sbaban 6535731Sbaban /* Add unixuser/unixgroup attribute names to the attrs list */ 6545731Sbaban if (unixname != NULL) { 6555731Sbaban i = 3; 6565731Sbaban if (eunixtype != _IDMAP_T_GROUP && 6575731Sbaban state->ad_unixuser_attr != NULL) 6585731Sbaban attrs[i++] = (char *)state->ad_unixuser_attr; 6595731Sbaban if (eunixtype != _IDMAP_T_USER && 6605731Sbaban state->ad_unixgroup_attr != NULL) 6615731Sbaban attrs[i] = (char *)state->ad_unixgroup_attr; 6625731Sbaban } 6634520Snw141292 6644520Snw141292 /* 6654520Snw141292 * Provide sane defaults for the results in case we never hear 6664520Snw141292 * back from the DS before closing the connection. 6675447Snw141292 * 6685447Snw141292 * In particular we default the result to indicate a retriable 6695447Snw141292 * error. The first complete matching result entry will cause 6705447Snw141292 * this to be set to IDMAP_SUCCESS, and the end of the results 6715447Snw141292 * for this search will cause this to indicate "not found" if no 6725447Snw141292 * result entries arrived or no complete ones matched the lookup 6735447Snw141292 * we were doing. 6744520Snw141292 */ 6754520Snw141292 *rc = IDMAP_ERR_RETRIABLE_NET_ERR; 6765731Sbaban if (sid_type != NULL) 6775731Sbaban *sid_type = _IDMAP_T_OTHER; 6785731Sbaban if (sid != NULL) 6795731Sbaban *sid = NULL; 6804520Snw141292 if (dname != NULL) 6814520Snw141292 *dname = NULL; 6824520Snw141292 if (rid != NULL) 6834520Snw141292 *rid = 0; 6846386Sjp151216 if (dn != NULL) 6856386Sjp151216 *dn = NULL; 6866386Sjp151216 if (attr != NULL) 6876386Sjp151216 *attr = NULL; 6886386Sjp151216 if (value != NULL) 6896386Sjp151216 *value = NULL; 6904520Snw141292 6916616Sdm199847 /* 6926616Sdm199847 * Don't set *canonname to NULL because it may be pointing to the 6936616Sdm199847 * given winname. Later on if we get a canonical name from AD the 6946616Sdm199847 * old name if any will be freed before assigning the new name. 6956616Sdm199847 */ 6966616Sdm199847 6974520Snw141292 /* 6988040SBaban.Kenkre@Sun.COM * Invoke the mother of all APIs i.e. the adutils API 6994520Snw141292 */ 7008040SBaban.Kenkre@Sun.COM ad_rc = adutils_lookup_batch_add(state->qs, filter, 7018040SBaban.Kenkre@Sun.COM (const char **)attrs, 7028040SBaban.Kenkre@Sun.COM edomain, &q->result, &q->ad_rc); 7038040SBaban.Kenkre@Sun.COM return (map_adrc2idmaprc(ad_rc)); 7044520Snw141292 } 7054520Snw141292 7064520Snw141292 idmap_retcode 7074520Snw141292 idmap_name2sid_batch_add1(idmap_query_state_t *state, 7085731Sbaban const char *name, const char *dname, int eunixtype, 7096386Sjp151216 char **dn, char **attr, char **value, 7106386Sjp151216 char **canonname, char **sid, rid_t *rid, 7116386Sjp151216 int *sid_type, char **unixname, idmap_retcode *rc) 7124520Snw141292 { 7134520Snw141292 idmap_retcode retcode; 7145447Snw141292 int len, samAcctNameLen; 7156616Sdm199847 char *filter = NULL, *s_name; 7165696Snw141292 char *ecanonname, *edomain; /* expected canonname */ 7174520Snw141292 7184520Snw141292 /* 7195447Snw141292 * Strategy: search the global catalog for user/group by 7205447Snw141292 * sAMAccountName = user/groupname with "" as the base DN and by 7215447Snw141292 * userPrincipalName = user/groupname@domain. The result 7225447Snw141292 * entries will be checked to conform to the name and domain 7235447Snw141292 * name given here. The DN, sAMAccountName, userPrincipalName, 7245447Snw141292 * objectSid and objectClass of the result entries are all we 7255447Snw141292 * need to figure out which entries match the lookup, the SID of 7265447Snw141292 * the user/group and whether it is a user or a group. 7274520Snw141292 */ 7284520Snw141292 7294520Snw141292 /* 7305447Snw141292 * We need the name and the domain name separately and as 7315447Snw141292 * name@domain. We also allow the domain to be provided 7325447Snw141292 * separately. 7334520Snw141292 */ 7345232Snw141292 samAcctNameLen = strlen(name); 7355447Snw141292 7365696Snw141292 if ((ecanonname = strdup(name)) == NULL) 7375696Snw141292 return (IDMAP_ERR_MEMORY); 7384520Snw141292 7395447Snw141292 if (dname == NULL || *dname == '\0') { 7405696Snw141292 if ((dname = strchr(name, '@')) != NULL) { 7415696Snw141292 /* 'name' is qualified with a domain name */ 7425696Snw141292 if ((edomain = strdup(dname + 1)) == NULL) { 7435696Snw141292 free(ecanonname); 7445696Snw141292 return (IDMAP_ERR_MEMORY); 7455696Snw141292 } 7465696Snw141292 *strchr(ecanonname, '@') = '\0'; 7475696Snw141292 } else { 7488040SBaban.Kenkre@Sun.COM /* 'name' not qualified and dname not given */ 749*8361SJulian.Pullen@Sun.COM dname = adutils_lookup_batch_getdefdomain(state->qs); 7508040SBaban.Kenkre@Sun.COM assert(dname != NULL); 7518040SBaban.Kenkre@Sun.COM if (*dname == '\0') { 7525696Snw141292 free(ecanonname); 7535696Snw141292 return (IDMAP_ERR_DOMAIN); 7545696Snw141292 } 7558040SBaban.Kenkre@Sun.COM edomain = strdup(dname); 7565696Snw141292 if (edomain == NULL) { 7575696Snw141292 free(ecanonname); 7585696Snw141292 return (IDMAP_ERR_MEMORY); 7595696Snw141292 } 7605696Snw141292 } 7615696Snw141292 } else { 7625696Snw141292 if ((edomain = strdup(dname)) == NULL) { 7635696Snw141292 free(ecanonname); 7645447Snw141292 return (IDMAP_ERR_MEMORY); 7655696Snw141292 } 7665447Snw141292 } 7674520Snw141292 768*8361SJulian.Pullen@Sun.COM if (!adutils_lookup_check_domain(state->qs, dname)) { 769*8361SJulian.Pullen@Sun.COM free(ecanonname); 770*8361SJulian.Pullen@Sun.COM free(edomain); 771*8361SJulian.Pullen@Sun.COM return (IDMAP_ERR_DOMAIN_NOTFOUND); 772*8361SJulian.Pullen@Sun.COM } 773*8361SJulian.Pullen@Sun.COM 7746616Sdm199847 s_name = sanitize_for_ldap_filter(name); 7756616Sdm199847 if (s_name == NULL) { 7766616Sdm199847 free(ecanonname); 7776616Sdm199847 free(edomain); 7786616Sdm199847 return (IDMAP_ERR_MEMORY); 7796616Sdm199847 } 7806616Sdm199847 7814520Snw141292 /* Assemble filter */ 7826616Sdm199847 len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1; 7835447Snw141292 if ((filter = (char *)malloc(len)) == NULL) { 7845696Snw141292 free(ecanonname); 7856616Sdm199847 free(edomain); 7866616Sdm199847 if (s_name != name) 7876616Sdm199847 free(s_name); 7884520Snw141292 return (IDMAP_ERR_MEMORY); 7894520Snw141292 } 7906616Sdm199847 (void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name); 7916616Sdm199847 if (s_name != name) 7926616Sdm199847 free(s_name); 7934520Snw141292 7945696Snw141292 retcode = idmap_batch_add1(state, filter, ecanonname, edomain, 7956386Sjp151216 eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type, 7966386Sjp151216 unixname, rc); 7974520Snw141292 7984520Snw141292 free(filter); 7994520Snw141292 8004520Snw141292 return (retcode); 8014520Snw141292 } 8024520Snw141292 8034520Snw141292 idmap_retcode 8044520Snw141292 idmap_sid2name_batch_add1(idmap_query_state_t *state, 8055731Sbaban const char *sid, const rid_t *rid, int eunixtype, 8066386Sjp151216 char **dn, char **attr, char **value, 8076386Sjp151216 char **name, char **dname, int *sid_type, 8086386Sjp151216 char **unixname, idmap_retcode *rc) 8094520Snw141292 { 8104520Snw141292 idmap_retcode retcode; 8114520Snw141292 int flen, ret; 8124520Snw141292 char *filter = NULL; 8138040SBaban.Kenkre@Sun.COM char cbinsid[ADUTILS_MAXHEXBINSID + 1]; 8144520Snw141292 8154520Snw141292 /* 8164520Snw141292 * Strategy: search [the global catalog] for user/group by 8174520Snw141292 * objectSid = SID with empty base DN. The DN, sAMAccountName 8184520Snw141292 * and objectClass of the result are all we need to figure out 8194520Snw141292 * the name of the SID and whether it is a user, a group or a 8204520Snw141292 * computer. 8214520Snw141292 */ 8224520Snw141292 823*8361SJulian.Pullen@Sun.COM if (!adutils_lookup_check_sid_prefix(state->qs, sid)) 824*8361SJulian.Pullen@Sun.COM return (IDMAP_ERR_DOMAIN_NOTFOUND); 825*8361SJulian.Pullen@Sun.COM 8268040SBaban.Kenkre@Sun.COM ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); 8274520Snw141292 if (ret != 0) 8284520Snw141292 return (IDMAP_ERR_SID); 8294520Snw141292 8304520Snw141292 /* Assemble filter */ 8315447Snw141292 flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1; 8324520Snw141292 if ((filter = (char *)malloc(flen)) == NULL) 8334520Snw141292 return (IDMAP_ERR_MEMORY); 8345447Snw141292 (void) snprintf(filter, flen, OBJSIDFILTER, cbinsid); 8354520Snw141292 8365731Sbaban retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype, 8376386Sjp151216 dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc); 8384520Snw141292 8394520Snw141292 free(filter); 8404520Snw141292 8414520Snw141292 return (retcode); 8424520Snw141292 } 8435731Sbaban 8445731Sbaban idmap_retcode 8455731Sbaban idmap_unixname2sid_batch_add1(idmap_query_state_t *state, 8465731Sbaban const char *unixname, int is_user, int is_wuser, 8476386Sjp151216 char **dn, char **attr, char **value, 8486386Sjp151216 char **sid, rid_t *rid, char **name, 8496386Sjp151216 char **dname, int *sid_type, idmap_retcode *rc) 8505731Sbaban { 8515731Sbaban idmap_retcode retcode; 8525731Sbaban int len, ulen; 8536616Sdm199847 char *filter = NULL, *s_unixname; 8545731Sbaban const char *attrname = NULL; 8555731Sbaban 8565731Sbaban /* Get unixuser or unixgroup AD attribute name */ 8575731Sbaban attrname = (is_user) ? 8585731Sbaban state->ad_unixuser_attr : state->ad_unixgroup_attr; 8595731Sbaban if (attrname == NULL) 8605731Sbaban return (IDMAP_ERR_NOTFOUND); 8615731Sbaban 8626616Sdm199847 s_unixname = sanitize_for_ldap_filter(unixname); 8636616Sdm199847 if (s_unixname == NULL) 8646616Sdm199847 return (IDMAP_ERR_MEMORY); 8656616Sdm199847 8665731Sbaban /* Assemble filter */ 8675731Sbaban ulen = strlen(unixname); 8685731Sbaban len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))", 8696616Sdm199847 is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1; 8706616Sdm199847 if ((filter = (char *)malloc(len)) == NULL) { 8716616Sdm199847 if (s_unixname != unixname) 8726616Sdm199847 free(s_unixname); 8735731Sbaban return (IDMAP_ERR_MEMORY); 8746616Sdm199847 } 8755731Sbaban (void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))", 8766616Sdm199847 is_wuser ? "user" : "group", attrname, ulen, s_unixname); 8776616Sdm199847 if (s_unixname != unixname) 8786616Sdm199847 free(s_unixname); 8795731Sbaban 8805731Sbaban retcode = idmap_batch_add1(state, filter, NULL, NULL, 8816386Sjp151216 _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type, 8826386Sjp151216 NULL, rc); 8836386Sjp151216 8846386Sjp151216 if (retcode == IDMAP_SUCCESS && attr != NULL) { 8856386Sjp151216 if ((*attr = strdup(attrname)) == NULL) 8866386Sjp151216 retcode = IDMAP_ERR_MEMORY; 8876386Sjp151216 } 8886386Sjp151216 8896386Sjp151216 if (retcode == IDMAP_SUCCESS && value != NULL) { 8906386Sjp151216 if (ulen > 0) { 8916386Sjp151216 if ((*value = strdup(unixname)) == NULL) 8926386Sjp151216 retcode = IDMAP_ERR_MEMORY; 8936386Sjp151216 } 8946386Sjp151216 else 8956386Sjp151216 *value = NULL; 8966386Sjp151216 } 8975731Sbaban 8985731Sbaban free(filter); 8995731Sbaban 9005731Sbaban return (retcode); 9015731Sbaban } 902