1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  *
  25  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 /*
  29  * Processes name2sid & sid2name batched lookups for a given user or
  30  * computer from an AD Directory server using GSSAPI authentication
  31  */
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <alloca.h>
  36 #include <string.h>
  37 #include <strings.h>
  38 #include <lber.h>
  39 #include <ldap.h>
  40 #include <sasl/sasl.h>
  41 #include <string.h>
  42 #include <ctype.h>
  43 #include <pthread.h>
  44 #include <synch.h>
  45 #include <atomic.h>
  46 #include <errno.h>
  47 #include <assert.h>
  48 #include <limits.h>
  49 #include <time.h>
  50 #include <sys/u8_textprep.h>
  51 #include "libadutils.h"
  52 #include "nldaputils.h"
  53 #include "idmapd.h"
  54 
  55 /* Attribute names and filter format strings */
  56 #define SAN             "sAMAccountName"
  57 #define OBJSID          "objectSid"
  58 #define OBJCLASS        "objectClass"
  59 #define UIDNUMBER       "uidNumber"
  60 #define GIDNUMBER       "gidNumber"
  61 #define UIDNUMBERFILTER "(&(objectclass=user)(uidNumber=%u))"
  62 #define GIDNUMBERFILTER "(&(objectclass=group)(gidNumber=%u))"
  63 #define SANFILTER       "(sAMAccountName=%s)"
  64 #define OBJSIDFILTER    "(|(objectSid=%s)(sIDHistory=%s))"
  65 
  66 void    idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc,
  67                 int qid, void *argp);
  68 
  69 /*
  70  * A place to put the results of a batched (async) query
  71  *
  72  * There is one of these for every query added to a batch object
  73  * (idmap_query_state, see below).
  74  */
  75 typedef struct idmap_q {
  76         /*
  77          * data used for validating search result entries for name->SID
  78          * lookups
  79          */
  80         char                    *ecanonname;    /* expected canon name */
  81         char                    *edomain;       /* expected domain name */
  82         idmap_id_type           esidtype;       /* expected SID type */
  83         /* results */
  84         char                    **canonname;    /* actual canon name */
  85         char                    **domain;       /* name of domain of object */
  86         char                    **sid;          /* stringified SID */
  87         rid_t                   *rid;           /* RID */
  88         idmap_id_type           *sid_type;      /* user or group SID? */
  89         char                    **unixname;     /* unixname for name mapping */
  90         char                    **dn;           /* DN of entry */
  91         char                    **attr;         /* Attr for name mapping */
  92         char                    **value;        /* value for name mapping */
  93         posix_id_t              *pid;           /* Posix ID found via IDMU */
  94         idmap_retcode           *rc;
  95         adutils_rc              ad_rc;
  96         adutils_result_t        *result;
  97 
  98         /*
  99          * The LDAP search entry result is placed here to be processed
 100          * when the search done result is received.
 101          */
 102         LDAPMessage             *search_res;    /* The LDAP search result */
 103 } idmap_q_t;
 104 
 105 /* Batch context structure; typedef is in header file */
 106 struct idmap_query_state {
 107         adutils_query_state_t   *qs;
 108         int                     qsize;          /* Queue size */
 109         uint32_t                qcount;         /* Number of queued requests */
 110         const char              *ad_unixuser_attr;
 111         const char              *ad_unixgroup_attr;
 112         int                     directory_based_mapping;        /* enum */
 113         char                    *default_domain;
 114         idmap_q_t               queries[1];     /* array of query results */
 115 };
 116 
 117 static pthread_t        reaperid = 0;
 118 
 119 /*
 120  * Keep connection management simple for now, extend or replace later
 121  * with updated libsldap code.
 122  */
 123 #define ADREAPERSLEEP   60
 124 
 125 /*
 126  * Idle connection reaping side of connection management
 127  *
 128  * Every minute wake up and look for connections that have been idle for
 129  * five minutes or more and close them.
 130  */
 131 /*ARGSUSED*/
 132 static
 133 void
 134 adreaper(void *arg)
 135 {
 136         timespec_t      ts;
 137 
 138         ts.tv_sec = ADREAPERSLEEP;
 139         ts.tv_nsec = 0;
 140 
 141         for (;;) {
 142                 /*
 143                  * nanosleep(3RT) is thead-safe (no SIGALRM) and more
 144                  * portable than usleep(3C)
 145                  */
 146                 (void) nanosleep(&ts, NULL);
 147                 adutils_reap_idle_connections();
 148         }
 149 }
 150 
 151 /*
 152  * Take ad_host_config_t information, create a ad_host_t,
 153  * populate it and add it to the list of hosts.
 154  */
 155 
 156 int
 157 idmap_add_ds(adutils_ad_t *ad, const char *host, int port)
 158 {
 159         int     ret = -1;
 160 
 161         if (adutils_add_ds(ad, host, port) == ADUTILS_SUCCESS)
 162                 ret = 0;
 163 
 164         /* Start reaper if it doesn't exist */
 165         if (ret == 0 && reaperid == 0)
 166                 (void) pthread_create(&reaperid, NULL,
 167                     (void *(*)(void *))adreaper, (void *)NULL);
 168         return (ret);
 169 }
 170 
 171 static
 172 idmap_retcode
 173 map_adrc2idmaprc(adutils_rc adrc)
 174 {
 175         switch (adrc) {
 176         case ADUTILS_SUCCESS:
 177                 return (IDMAP_SUCCESS);
 178         case ADUTILS_ERR_NOTFOUND:
 179                 return (IDMAP_ERR_NOTFOUND);
 180         case ADUTILS_ERR_MEMORY:
 181                 return (IDMAP_ERR_MEMORY);
 182         case ADUTILS_ERR_DOMAIN:
 183                 return (IDMAP_ERR_DOMAIN);
 184         case ADUTILS_ERR_OTHER:
 185                 return (IDMAP_ERR_OTHER);
 186         case ADUTILS_ERR_RETRIABLE_NET_ERR:
 187                 return (IDMAP_ERR_RETRIABLE_NET_ERR);
 188         default:
 189                 return (IDMAP_ERR_INTERNAL);
 190         }
 191         /* NOTREACHED */
 192 }
 193 
 194 idmap_retcode
 195 idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries,
 196         int directory_based_mapping, const char *default_domain,
 197         idmap_query_state_t **state)
 198 {
 199         idmap_query_state_t     *new_state;
 200         adutils_rc              rc;
 201 
 202         *state = NULL;
 203 
 204         assert(ad != NULL);
 205 
 206         new_state = calloc(1, sizeof (idmap_query_state_t) +
 207             (nqueries - 1) * sizeof (idmap_q_t));
 208         if (new_state == NULL)
 209                 return (IDMAP_ERR_MEMORY);
 210 
 211         if ((rc = adutils_lookup_batch_start(ad, nqueries,
 212             idmap_ldap_res_search_cb, new_state, &new_state->qs))
 213             != ADUTILS_SUCCESS) {
 214                 idmap_lookup_release_batch(&new_state);
 215                 return (map_adrc2idmaprc(rc));
 216         }
 217 
 218         new_state->default_domain = strdup(default_domain);
 219         if (new_state->default_domain == NULL) {
 220                 idmap_lookup_release_batch(&new_state);
 221                 return (IDMAP_ERR_MEMORY);
 222         }
 223 
 224         new_state->directory_based_mapping = directory_based_mapping;
 225         new_state->qsize = nqueries;
 226         *state = new_state;
 227         return (IDMAP_SUCCESS);
 228 }
 229 
 230 /*
 231  * Set unixuser_attr and unixgroup_attr for AD-based name mapping
 232  */
 233 void
 234 idmap_lookup_batch_set_unixattr(idmap_query_state_t *state,
 235                 const char *unixuser_attr, const char *unixgroup_attr)
 236 {
 237         state->ad_unixuser_attr = unixuser_attr;
 238         state->ad_unixgroup_attr = unixgroup_attr;
 239 }
 240 
 241 /*
 242  * Take parsed attribute values from a search result entry and check if
 243  * it is the result that was desired and, if so, set the result fields
 244  * of the given idmap_q_t.
 245  *
 246  * Except for dn and attr, all strings are consumed, either by transferring
 247  * them over into the request results (where the caller will eventually free
 248  * them) or by freeing them here.  Note that this aligns with the "const"
 249  * declarations below.
 250  */
 251 static
 252 void
 253 idmap_setqresults(
 254     idmap_q_t *q,
 255     char *san,
 256     const char *dn,
 257     const char *attr,
 258     char *value,
 259     char *sid,
 260     rid_t rid,
 261     int sid_type,
 262     char *unixname,
 263     posix_id_t pid)
 264 {
 265         char *domain;
 266         int err1;
 267 
 268         assert(dn != NULL);
 269 
 270         if ((domain = adutils_dn2dns(dn)) == NULL)
 271                 goto out;
 272 
 273         if (q->ecanonname != NULL && san != NULL) {
 274                 /* Check that this is the canonname that we were looking for */
 275                 if (u8_strcmp(q->ecanonname, san, 0,
 276                     U8_STRCMP_CI_LOWER, /* no normalization, for now */
 277                     U8_UNICODE_LATEST, &err1) != 0 || err1 != 0)
 278                         goto out;
 279         }
 280 
 281         if (q->edomain != NULL) {
 282                 /* Check that this is the domain that we were looking for */
 283                 if (!domain_eq(q->edomain, domain))
 284                         goto out;
 285         }
 286 
 287         /* Copy the DN and attr and value */
 288         if (q->dn != NULL)
 289                 *q->dn = strdup(dn);
 290 
 291         if (q->attr != NULL && attr != NULL)
 292                 *q->attr = strdup(attr);
 293 
 294         if (q->value != NULL && value != NULL) {
 295                 *q->value = value;
 296                 value = NULL;
 297         }
 298 
 299         /* Set results */
 300         if (q->sid) {
 301                 *q->sid = sid;
 302                 sid = NULL;
 303         }
 304         if (q->rid)
 305                 *q->rid = rid;
 306         if (q->sid_type)
 307                 *q->sid_type = sid_type;
 308         if (q->unixname) {
 309                 *q->unixname = unixname;
 310                 unixname = NULL;
 311         }
 312         if (q->domain != NULL) {
 313                 *q->domain = domain;
 314                 domain = NULL;
 315         }
 316         if (q->canonname != NULL) {
 317                 /*
 318                  * The caller may be replacing the given winname by its
 319                  * canonical name and therefore free any old name before
 320                  * overwriting the field by the canonical name.
 321                  */
 322                 free(*q->canonname);
 323                 *q->canonname = san;
 324                 san = NULL;
 325         }
 326 
 327         if (q->pid != NULL && pid != IDMAP_SENTINEL_PID) {
 328                 *q->pid = pid;
 329         }
 330 
 331         q->ad_rc = ADUTILS_SUCCESS;
 332 
 333 out:
 334         /* Free unused attribute values */
 335         free(san);
 336         free(sid);
 337         free(domain);
 338         free(unixname);
 339         free(value);
 340 }
 341 
 342 #define BVAL_CASEEQ(bv, str) \
 343                 (((*(bv))->bv_len == (sizeof (str) - 1)) && \
 344                     strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
 345 
 346 /*
 347  * Extract the class of the result entry.  Returns 1 on success, 0 on
 348  * failure.
 349  */
 350 static
 351 int
 352 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type)
 353 {
 354         BerValue        **cbval;
 355 
 356         *sid_type = IDMAP_SID;
 357         if (bvalues == NULL)
 358                 return (0);
 359 
 360         /*
 361          * We consider Computer to be a subclass of User, so we can just
 362          * ignore Computer entries and pay attention to the accompanying
 363          * User entries.
 364          */
 365         for (cbval = bvalues; *cbval != NULL; cbval++) {
 366                 if (BVAL_CASEEQ(cbval, "group")) {
 367                         *sid_type = IDMAP_GSID;
 368                         break;
 369                 } else if (BVAL_CASEEQ(cbval, "user")) {
 370                         *sid_type = IDMAP_USID;
 371                         break;
 372                 }
 373                 /*
 374                  * "else if (*sid_type = IDMAP_USID)" then this is a
 375                  * new sub-class of user -- what to do with it??
 376                  */
 377         }
 378 
 379         return (1);
 380 }
 381 
 382 /*
 383  * Handle a given search result entry
 384  */
 385 static
 386 void
 387 idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q,
 388         LDAPMessage *res, LDAP *ld)
 389 {
 390         BerValue                **bvalues;
 391         const char              *attr = NULL;
 392         char                    *value = NULL;
 393         char                    *unix_name = NULL;
 394         char                    *dn;
 395         char                    *san = NULL;
 396         char                    *sid = NULL;
 397         rid_t                   rid = 0;
 398         int                     sid_type;
 399         int                     ok;
 400         posix_id_t              pid = IDMAP_SENTINEL_PID;
 401 
 402         assert(q->rc != NULL);
 403         assert(q->domain == NULL || *q->domain == NULL);
 404 
 405         if ((dn = ldap_get_dn(ld, res)) == NULL)
 406                 return;
 407 
 408         bvalues = ldap_get_values_len(ld, res, OBJCLASS);
 409         if (bvalues == NULL) {
 410                 /*
 411                  * Didn't find objectclass. Something's wrong with our
 412                  * AD data.
 413                  */
 414                 idmapdlog(LOG_ERR, "%s has no %s", dn, OBJCLASS);
 415                 goto out;
 416         }
 417         ok = idmap_bv_objclass2sidtype(bvalues, &sid_type);
 418         ldap_value_free_len(bvalues);
 419         if (!ok) {
 420                 /*
 421                  * Didn't understand objectclass. Something's wrong with our
 422                  * AD data.
 423                  */
 424                 idmapdlog(LOG_ERR, "%s has unexpected %s", dn, OBJCLASS);
 425                 goto out;
 426         }
 427 
 428         if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU &&
 429             q->pid != NULL) {
 430                 if (sid_type == IDMAP_USID)
 431                         attr = UIDNUMBER;
 432                 else if (sid_type == IDMAP_GSID)
 433                         attr = GIDNUMBER;
 434                 if (attr != NULL) {
 435                         bvalues = ldap_get_values_len(ld, res, attr);
 436                         if (bvalues != NULL) {
 437                                 value = adutils_bv_str(bvalues[0]);
 438                                 if (!adutils_bv_uint(bvalues[0], &pid)) {
 439                                         idmapdlog(LOG_ERR,
 440                                             "%s has Invalid %s value \"%s\"",
 441                                             dn, attr, value);
 442                                 }
 443                                 ldap_value_free_len(bvalues);
 444                         }
 445                 }
 446         }
 447 
 448         if (state->directory_based_mapping == DIRECTORY_MAPPING_NAME &&
 449             q->unixname != NULL) {
 450                 /*
 451                  * If the caller has requested unixname then determine the
 452                  * AD attribute name that will have the unixname, and retrieve
 453                  * its value.
 454                  */
 455                 idmap_id_type esidtype;
 456                 /*
 457                  * Determine the target type.
 458                  *
 459                  * If the caller specified one, use that.  Otherwise, give the
 460                  * same type that as we found for the Windows user.
 461                  */
 462                 esidtype = q->esidtype;
 463                 if (esidtype == IDMAP_SID)
 464                         esidtype = sid_type;
 465 
 466                 if (esidtype == IDMAP_USID)
 467                         attr = state->ad_unixuser_attr;
 468                 else if (esidtype == IDMAP_GSID)
 469                         attr = state->ad_unixgroup_attr;
 470 
 471                 if (attr != NULL) {
 472                         bvalues = ldap_get_values_len(ld, res, attr);
 473                         if (bvalues != NULL) {
 474                                 unix_name = adutils_bv_str(bvalues[0]);
 475                                 ldap_value_free_len(bvalues);
 476                                 value = strdup(unix_name);
 477                         }
 478                 }
 479         }
 480 
 481         bvalues = ldap_get_values_len(ld, res, SAN);
 482         if (bvalues != NULL) {
 483                 san = adutils_bv_str(bvalues[0]);
 484                 ldap_value_free_len(bvalues);
 485         }
 486 
 487         if (q->sid != NULL) {
 488                 bvalues = ldap_get_values_len(ld, res, OBJSID);
 489                 if (bvalues != NULL) {
 490                         sid = adutils_bv_objsid2sidstr(bvalues[0], &rid);
 491                         ldap_value_free_len(bvalues);
 492                 }
 493         }
 494 
 495         idmap_setqresults(q, san, dn,
 496             attr, value,
 497             sid, rid, sid_type,
 498             unix_name, pid);
 499 
 500 out:
 501         ldap_memfree(dn);
 502 }
 503 
 504 void
 505 idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid,
 506                 void *argp)
 507 {
 508         idmap_query_state_t     *state = (idmap_query_state_t *)argp;
 509         idmap_q_t               *q = &(state->queries[qid]);
 510 
 511         switch (rc) {
 512         case LDAP_RES_SEARCH_RESULT:
 513                 if (q->search_res != NULL) {
 514                         idmap_extract_object(state, q, q->search_res, ld);
 515                         (void) ldap_msgfree(q->search_res);
 516                         q->search_res = NULL;
 517                 } else
 518                         q->ad_rc = ADUTILS_ERR_NOTFOUND;
 519                 break;
 520         case LDAP_RES_SEARCH_ENTRY:
 521                 if (q->search_res == NULL) {
 522                         q->search_res = *res;
 523                         *res = NULL;
 524                 }
 525                 break;
 526         default:
 527                 break;
 528         }
 529 }
 530 
 531 static
 532 void
 533 idmap_cleanup_batch(idmap_query_state_t *batch)
 534 {
 535         int i;
 536 
 537         for (i = 0; i < batch->qcount; i++) {
 538                 if (batch->queries[i].ecanonname != NULL)
 539                         free(batch->queries[i].ecanonname);
 540                 batch->queries[i].ecanonname = NULL;
 541                 if (batch->queries[i].edomain != NULL)
 542                         free(batch->queries[i].edomain);
 543                 batch->queries[i].edomain = NULL;
 544         }
 545 }
 546 
 547 /*
 548  * This routine frees the idmap_query_state_t structure
 549  */
 550 void
 551 idmap_lookup_release_batch(idmap_query_state_t **state)
 552 {
 553         if (state == NULL || *state == NULL)
 554                 return;
 555         adutils_lookup_batch_release(&(*state)->qs);
 556         idmap_cleanup_batch(*state);
 557         free((*state)->default_domain);
 558         free(*state);
 559         *state = NULL;
 560 }
 561 
 562 idmap_retcode
 563 idmap_lookup_batch_end(idmap_query_state_t **state)
 564 {
 565         adutils_rc              ad_rc;
 566         int                     i;
 567         idmap_query_state_t     *id_qs = *state;
 568 
 569         ad_rc = adutils_lookup_batch_end(&id_qs->qs);
 570 
 571         /*
 572          * Map adutils rc to idmap_retcode in each
 573          * query because consumers in dbutils.c
 574          * expects idmap_retcode.
 575          */
 576         for (i = 0; i < id_qs->qcount; i++) {
 577                 *id_qs->queries[i].rc =
 578                     map_adrc2idmaprc(id_qs->queries[i].ad_rc);
 579         }
 580         idmap_lookup_release_batch(state);
 581         return (map_adrc2idmaprc(ad_rc));
 582 }
 583 
 584 /*
 585  * Send one prepared search, queue up msgid, process what results are
 586  * available
 587  */
 588 static
 589 idmap_retcode
 590 idmap_batch_add1(idmap_query_state_t *state, const char *filter,
 591         char *ecanonname, char *edomain, idmap_id_type esidtype,
 592         char **dn, char **attr, char **value,
 593         char **canonname, char **dname,
 594         char **sid, rid_t *rid, idmap_id_type *sid_type, char **unixname,
 595         posix_id_t *pid,
 596         idmap_retcode *rc)
 597 {
 598         adutils_rc      ad_rc;
 599         int             qid, i;
 600         idmap_q_t       *q;
 601         char    *attrs[20];     /* Plenty */
 602 
 603         qid = atomic_inc_32_nv(&state->qcount) - 1;
 604         q = &(state->queries[qid]);
 605 
 606         assert(qid < state->qsize);
 607 
 608         /*
 609          * Remember the expected canonname, domainname and unix type
 610          * so we can check the results * against it
 611          */
 612         q->ecanonname = ecanonname;
 613         q->edomain = edomain;
 614         q->esidtype = esidtype;
 615 
 616         /* Remember where to put the results */
 617         q->canonname = canonname;
 618         q->sid = sid;
 619         q->domain = dname;
 620         q->rid = rid;
 621         q->sid_type = sid_type;
 622         q->rc = rc;
 623         q->unixname = unixname;
 624         q->dn = dn;
 625         q->attr = attr;
 626         q->value = value;
 627         q->pid = pid;
 628 
 629         /* Add attributes that are not always needed */
 630         i = 0;
 631         attrs[i++] = SAN;
 632         attrs[i++] = OBJSID;
 633         attrs[i++] = OBJCLASS;
 634 
 635         if (unixname != NULL) {
 636                 /* Add unixuser/unixgroup attribute names to the attrs list */
 637                 if (esidtype != IDMAP_GSID &&
 638                     state->ad_unixuser_attr != NULL)
 639                         attrs[i++] = (char *)state->ad_unixuser_attr;
 640                 if (esidtype != IDMAP_USID &&
 641                     state->ad_unixgroup_attr != NULL)
 642                         attrs[i++] = (char *)state->ad_unixgroup_attr;
 643         }
 644 
 645         if (pid != NULL) {
 646                 if (esidtype != IDMAP_GSID)
 647                         attrs[i++] = UIDNUMBER;
 648                 if (esidtype != IDMAP_USID)
 649                         attrs[i++] = GIDNUMBER;
 650         }
 651 
 652         attrs[i] = NULL;
 653 
 654         /*
 655          * Provide sane defaults for the results in case we never hear
 656          * back from the DS before closing the connection.
 657          *
 658          * In particular we default the result to indicate a retriable
 659          * error.  The first complete matching result entry will cause
 660          * this to be set to IDMAP_SUCCESS, and the end of the results
 661          * for this search will cause this to indicate "not found" if no
 662          * result entries arrived or no complete ones matched the lookup
 663          * we were doing.
 664          */
 665         *rc = IDMAP_ERR_RETRIABLE_NET_ERR;
 666         if (sid_type != NULL)
 667                 *sid_type = IDMAP_SID;
 668         if (sid != NULL)
 669                 *sid = NULL;
 670         if (dname != NULL)
 671                 *dname = NULL;
 672         if (rid != NULL)
 673                 *rid = 0;
 674         if (dn != NULL)
 675                 *dn = NULL;
 676         if (attr != NULL)
 677                 *attr = NULL;
 678         if (value != NULL)
 679                 *value = NULL;
 680 
 681         /*
 682          * Don't set *canonname to NULL because it may be pointing to the
 683          * given winname. Later on if we get a canonical name from AD the
 684          * old name if any will be freed before assigning the new name.
 685          */
 686 
 687         /*
 688          * Invoke the mother of all APIs i.e. the adutils API
 689          */
 690         ad_rc = adutils_lookup_batch_add(state->qs, filter,
 691             (const char **)attrs,
 692             edomain, &q->result, &q->ad_rc);
 693         return (map_adrc2idmaprc(ad_rc));
 694 }
 695 
 696 idmap_retcode
 697 idmap_name2sid_batch_add1(idmap_query_state_t *state,
 698         const char *name, const char *dname, idmap_id_type esidtype,
 699         char **dn, char **attr, char **value,
 700         char **canonname, char **sid, rid_t *rid,
 701         idmap_id_type *sid_type, char **unixname,
 702         posix_id_t *pid, idmap_retcode *rc)
 703 {
 704         idmap_retcode   retcode;
 705         char            *filter, *s_name;
 706         char            *ecanonname, *edomain; /* expected canonname */
 707 
 708         /*
 709          * Strategy: search the global catalog for user/group by
 710          * sAMAccountName = user/groupname with "" as the base DN and by
 711          * userPrincipalName = user/groupname@domain.  The result
 712          * entries will be checked to conform to the name and domain
 713          * name given here.  The DN, sAMAccountName, userPrincipalName,
 714          * objectSid and objectClass of the result entries are all we
 715          * need to figure out which entries match the lookup, the SID of
 716          * the user/group and whether it is a user or a group.
 717          */
 718 
 719         if ((ecanonname = strdup(name)) == NULL)
 720                 return (IDMAP_ERR_MEMORY);
 721 
 722         if (dname == NULL || *dname == '\0') {
 723                 /* 'name' not qualified and dname not given */
 724                 dname = state->default_domain;
 725                 edomain = strdup(dname);
 726                 if (edomain == NULL) {
 727                         free(ecanonname);
 728                         return (IDMAP_ERR_MEMORY);
 729                 }
 730         } else {
 731                 if ((edomain = strdup(dname)) == NULL) {
 732                         free(ecanonname);
 733                         return (IDMAP_ERR_MEMORY);
 734                 }
 735         }
 736 
 737         if (!adutils_lookup_check_domain(state->qs, dname)) {
 738                 free(ecanonname);
 739                 free(edomain);
 740                 return (IDMAP_ERR_DOMAIN_NOTFOUND);
 741         }
 742 
 743         s_name = sanitize_for_ldap_filter(name);
 744         if (s_name == NULL) {
 745                 free(ecanonname);
 746                 free(edomain);
 747                 return (IDMAP_ERR_MEMORY);
 748         }
 749 
 750         /* Assemble filter */
 751         (void) asprintf(&filter, SANFILTER, s_name);
 752         if (s_name != name)
 753                 free(s_name);
 754         if (filter == NULL) {
 755                 free(ecanonname);
 756                 free(edomain);
 757                 return (IDMAP_ERR_MEMORY);
 758         }
 759 
 760         retcode = idmap_batch_add1(state, filter, ecanonname, edomain,
 761             esidtype, dn, attr, value, canonname, NULL, sid, rid, sid_type,
 762             unixname, pid, rc);
 763 
 764         free(filter);
 765 
 766         return (retcode);
 767 }
 768 
 769 idmap_retcode
 770 idmap_sid2name_batch_add1(idmap_query_state_t *state,
 771         const char *sid, const rid_t *rid, idmap_id_type esidtype,
 772         char **dn, char **attr, char **value,
 773         char **name, char **dname, idmap_id_type *sid_type,
 774         char **unixname, posix_id_t *pid, idmap_retcode *rc)
 775 {
 776         idmap_retcode   retcode;
 777         int             ret;
 778         char            *filter;
 779         char            cbinsid[ADUTILS_MAXHEXBINSID + 1];
 780 
 781         /*
 782          * Strategy: search [the global catalog] for user/group by
 783          * objectSid = SID with empty base DN.  The DN, sAMAccountName
 784          * and objectClass of the result are all we need to figure out
 785          * the name of the SID and whether it is a user, a group or a
 786          * computer.
 787          */
 788 
 789         if (!adutils_lookup_check_sid_prefix(state->qs, sid))
 790                 return (IDMAP_ERR_DOMAIN_NOTFOUND);
 791 
 792         ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
 793         if (ret != 0)
 794                 return (IDMAP_ERR_SID);
 795 
 796         /* Assemble filter */
 797         (void) asprintf(&filter, OBJSIDFILTER, cbinsid, cbinsid);
 798         if (filter == NULL)
 799                 return (IDMAP_ERR_MEMORY);
 800 
 801         retcode = idmap_batch_add1(state, filter, NULL, NULL, esidtype,
 802             dn, attr, value, name, dname, NULL, NULL, sid_type, unixname,
 803             pid, rc);
 804 
 805         free(filter);
 806 
 807         return (retcode);
 808 }
 809 
 810 idmap_retcode
 811 idmap_unixname2sid_batch_add1(idmap_query_state_t *state,
 812         const char *unixname, int is_user, int is_wuser,
 813         char **dn, char **attr, char **value,
 814         char **sid, rid_t *rid, char **name,
 815         char **dname, idmap_id_type *sid_type, idmap_retcode *rc)
 816 {
 817         idmap_retcode   retcode;
 818         char            *filter, *s_unixname;
 819         const char      *attrname;
 820 
 821         /* Get unixuser or unixgroup AD attribute name */
 822         attrname = (is_user) ?
 823             state->ad_unixuser_attr : state->ad_unixgroup_attr;
 824         if (attrname == NULL)
 825                 return (IDMAP_ERR_NOTFOUND);
 826 
 827         s_unixname = sanitize_for_ldap_filter(unixname);
 828         if (s_unixname == NULL)
 829                 return (IDMAP_ERR_MEMORY);
 830 
 831         /*  Assemble filter */
 832         (void) asprintf(&filter, "(&(objectclass=%s)(%s=%s))",
 833             is_wuser ? "user" : "group", attrname, s_unixname);
 834         if (s_unixname != unixname)
 835                 free(s_unixname);
 836         if (filter == NULL) {
 837                 return (IDMAP_ERR_MEMORY);
 838         }
 839 
 840         retcode = idmap_batch_add1(state, filter, NULL, NULL,
 841             IDMAP_POSIXID, dn, NULL, NULL, name, dname, sid, rid, sid_type,
 842             NULL, NULL, rc);
 843 
 844         if (retcode == IDMAP_SUCCESS && attr != NULL) {
 845                 if ((*attr = strdup(attrname)) == NULL)
 846                         retcode = IDMAP_ERR_MEMORY;
 847         }
 848 
 849         if (retcode == IDMAP_SUCCESS && value != NULL) {
 850                 if ((*value = strdup(unixname)) == NULL)
 851                         retcode = IDMAP_ERR_MEMORY;
 852         }
 853 
 854         free(filter);
 855 
 856         return (retcode);
 857 }
 858 
 859 idmap_retcode
 860 idmap_pid2sid_batch_add1(idmap_query_state_t *state,
 861         posix_id_t pid, int is_user,
 862         char **dn, char **attr, char **value,
 863         char **sid, rid_t *rid, char **name,
 864         char **dname, idmap_id_type *sid_type, idmap_retcode *rc)
 865 {
 866         idmap_retcode   retcode;
 867         char            *filter;
 868         const char      *attrname;
 869 
 870         /*  Assemble filter */
 871         if (is_user) {
 872                 (void) asprintf(&filter, UIDNUMBERFILTER, pid);
 873                 attrname = UIDNUMBER;
 874         } else {
 875                 (void) asprintf(&filter, GIDNUMBERFILTER, pid);
 876                 attrname = GIDNUMBER;
 877         }
 878         if (filter == NULL)
 879                 return (IDMAP_ERR_MEMORY);
 880 
 881         retcode = idmap_batch_add1(state, filter, NULL, NULL,
 882             IDMAP_POSIXID, dn, NULL, NULL, name, dname, sid, rid, sid_type,
 883             NULL, NULL, rc);
 884 
 885         if (retcode == IDMAP_SUCCESS && attr != NULL) {
 886                 if ((*attr = strdup(attrname)) == NULL)
 887                         retcode = IDMAP_ERR_MEMORY;
 888         }
 889 
 890         if (retcode == IDMAP_SUCCESS && value != NULL) {
 891                 (void) asprintf(value, "%u", pid);
 892                 if (*value == NULL)
 893                         retcode = IDMAP_ERR_MEMORY;
 894         }
 895 
 896         free(filter);
 897 
 898         return (retcode);
 899 }