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  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 /*
  27  * Database related utility routines
  28  */
  29 
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <errno.h>
  34 #include <sys/types.h>
  35 #include <sys/stat.h>
  36 #include <rpc/rpc.h>
  37 #include <sys/sid.h>
  38 #include <time.h>
  39 #include <pwd.h>
  40 #include <grp.h>
  41 #include <pthread.h>
  42 #include <assert.h>
  43 #include <sys/u8_textprep.h>
  44 #include <alloca.h>
  45 #include <libuutil.h>
  46 #include <note.h>
  47 
  48 #include "idmapd.h"
  49 #include "adutils.h"
  50 #include "string.h"
  51 #include "idmap_priv.h"
  52 #include "schema.h"
  53 #include "nldaputils.h"
  54 #include "idmap_lsa.h"
  55 
  56 
  57 static idmap_retcode sql_compile_n_step_once(sqlite *, char *,
  58                 sqlite_vm **, int *, int, const char ***);
  59 static idmap_retcode lookup_localsid2pid(idmap_mapping *, idmap_id_res *);
  60 static idmap_retcode lookup_cache_name2sid(sqlite *, const char *,
  61             const char *, char **, char **, idmap_rid_t *, idmap_id_type *);
  62 
  63 #define EMPTY_NAME(name)        (*name == 0 || strcmp(name, "\"\"") == 0)
  64 
  65 #define DO_NOT_ALLOC_NEW_ID_MAPPING(req)\
  66                 (req->flag & IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
  67 
  68 #define AVOID_NAMESERVICE(req)\
  69                 (req->flag & IDMAP_REQ_FLG_NO_NAMESERVICE)
  70 
  71 #define ALLOW_WK_OR_LOCAL_SIDS_ONLY(req)\
  72                 (req->flag & IDMAP_REQ_FLG_WK_OR_LOCAL_SIDS_ONLY)
  73 
  74 typedef enum init_db_option {
  75         FAIL_IF_CORRUPT = 0,
  76         REMOVE_IF_CORRUPT = 1
  77 } init_db_option_t;
  78 
  79 /*
  80  * Thread specific data to hold the database handles so that the
  81  * databases are not opened and closed for every request. It also
  82  * contains the sqlite busy handler structure.
  83  */
  84 
  85 struct idmap_busy {
  86         const char *name;
  87         const int *delays;
  88         int delay_size;
  89         int total;
  90         int sec;
  91 };
  92 
  93 
  94 typedef struct idmap_tsd {
  95         sqlite *db_db;
  96         sqlite *cache_db;
  97         struct idmap_busy cache_busy;
  98         struct idmap_busy db_busy;
  99 } idmap_tsd_t;
 100 
 101 /*
 102  * Flags to indicate how local the directory we're consulting is.
 103  * If neither is set, it means the directory belongs to a remote forest.
 104  */
 105 #define DOMAIN_IS_LOCAL 0x01
 106 #define FOREST_IS_LOCAL 0x02
 107 
 108 static const int cache_delay_table[] =
 109                 { 1, 2, 5, 10, 15, 20, 25, 30,  35,  40,
 110                 50,  50, 60, 70, 80, 90, 100};
 111 
 112 static const int db_delay_table[] =
 113                 { 5, 10, 15, 20, 30,  40,  55,  70, 100};
 114 
 115 
 116 static pthread_key_t    idmap_tsd_key;
 117 
 118 void
 119 idmap_tsd_destroy(void *key)
 120 {
 121 
 122         idmap_tsd_t     *tsd = (idmap_tsd_t *)key;
 123         if (tsd) {
 124                 if (tsd->db_db)
 125                         (void) sqlite_close(tsd->db_db);
 126                 if (tsd->cache_db)
 127                         (void) sqlite_close(tsd->cache_db);
 128                 free(tsd);
 129         }
 130 }
 131 
 132 void
 133 idmap_init_tsd_key(void)
 134 {
 135         int rc;
 136 
 137         rc = pthread_key_create(&idmap_tsd_key, idmap_tsd_destroy);
 138         assert(rc == 0);
 139 }
 140 
 141 
 142 
 143 idmap_tsd_t *
 144 idmap_get_tsd(void)
 145 {
 146         idmap_tsd_t     *tsd;
 147 
 148         if ((tsd = pthread_getspecific(idmap_tsd_key)) == NULL) {
 149                 /* No thread specific data so create it */
 150                 if ((tsd = malloc(sizeof (*tsd))) != NULL) {
 151                         /* Initialize thread specific data */
 152                         (void) memset(tsd, 0, sizeof (*tsd));
 153                         /* save the trhread specific data */
 154                         if (pthread_setspecific(idmap_tsd_key, tsd) != 0) {
 155                                 /* Can't store key */
 156                                 free(tsd);
 157                                 tsd = NULL;
 158                         }
 159                 } else {
 160                         tsd = NULL;
 161                 }
 162         }
 163 
 164         return (tsd);
 165 }
 166 
 167 /*
 168  * A simple wrapper around u8_textprep_str() that returns the Unicode
 169  * lower-case version of some string.  The result must be freed.
 170  */
 171 char *
 172 tolower_u8(const char *s)
 173 {
 174         char *res = NULL;
 175         char *outs;
 176         size_t inlen, outlen, inbytesleft, outbytesleft;
 177         int rc, err;
 178 
 179         /*
 180          * u8_textprep_str() does not allocate memory.  The input and
 181          * output buffers may differ in size (though that would be more
 182          * likely when normalization is done).  We have to loop over it...
 183          *
 184          * To improve the chances that we can avoid looping we add 10
 185          * bytes of output buffer room the first go around.
 186          */
 187         inlen = inbytesleft = strlen(s);
 188         outlen = outbytesleft = inlen + 10;
 189         if ((res = malloc(outlen)) == NULL)
 190                 return (NULL);
 191         outs = res;
 192 
 193         while ((rc = u8_textprep_str((char *)s, &inbytesleft, outs,
 194             &outbytesleft, U8_TEXTPREP_TOLOWER, U8_UNICODE_LATEST, &err)) < 0 &&
 195             err == E2BIG) {
 196                 if ((res = realloc(res, outlen + inbytesleft)) == NULL)
 197                         return (NULL);
 198                 /* adjust input/output buffer pointers */
 199                 s += (inlen - inbytesleft);
 200                 outs = res + outlen - outbytesleft;
 201                 /* adjust outbytesleft and outlen */
 202                 outlen += inbytesleft;
 203                 outbytesleft += inbytesleft;
 204         }
 205 
 206         if (rc < 0) {
 207                 free(res);
 208                 res = NULL;
 209                 return (NULL);
 210         }
 211 
 212         res[outlen - outbytesleft] = '\0';
 213 
 214         return (res);
 215 }
 216 
 217 static int sql_exec_tran_no_cb(sqlite *db, char *sql, const char *dbname,
 218         const char *while_doing);
 219 
 220 
 221 /*
 222  * Initialize 'dbname' using 'sql'
 223  */
 224 static
 225 int
 226 init_db_instance(const char *dbname, int version,
 227         const char *detect_version_sql, char * const *sql,
 228         init_db_option_t opt, int *created, int *upgraded)
 229 {
 230         int rc, curr_version;
 231         int tries = 1;
 232         int prio = LOG_NOTICE;
 233         sqlite *db = NULL;
 234         char *errmsg = NULL;
 235 
 236         *created = 0;
 237         *upgraded = 0;
 238 
 239         if (opt == REMOVE_IF_CORRUPT)
 240                 tries = 3;
 241 
 242 rinse_repeat:
 243         if (tries == 0) {
 244                 idmapdlog(LOG_ERR, "Failed to initialize db %s", dbname);
 245                 return (-1);
 246         }
 247         if (tries-- == 1)
 248                 /* Last try, log errors */
 249                 prio = LOG_ERR;
 250 
 251         db = sqlite_open(dbname, 0600, &errmsg);
 252         if (db == NULL) {
 253                 idmapdlog(prio, "Error creating database %s (%s)",
 254                     dbname, CHECK_NULL(errmsg));
 255                 sqlite_freemem(errmsg);
 256                 if (opt == REMOVE_IF_CORRUPT)
 257                         (void) unlink(dbname);
 258                 goto rinse_repeat;
 259         }
 260 
 261         sqlite_busy_timeout(db, 3000);
 262 
 263         /* Detect current version of schema in the db, if any */
 264         curr_version = 0;
 265         if (detect_version_sql != NULL) {
 266                 char *end, **results;
 267                 int nrow;
 268 
 269 #ifdef  IDMAPD_DEBUG
 270                 (void) fprintf(stderr, "Schema version detection SQL: %s\n",
 271                     detect_version_sql);
 272 #endif  /* IDMAPD_DEBUG */
 273                 rc = sqlite_get_table(db, detect_version_sql, &results,
 274                     &nrow, NULL, &errmsg);
 275                 if (rc != SQLITE_OK) {
 276                         idmapdlog(prio,
 277                             "Error detecting schema version of db %s (%s)",
 278                             dbname, errmsg);
 279                         sqlite_freemem(errmsg);
 280                         sqlite_free_table(results);
 281                         sqlite_close(db);
 282                         return (-1);
 283                 }
 284                 if (nrow != 1) {
 285                         idmapdlog(prio,
 286                             "Error detecting schema version of db %s", dbname);
 287                         sqlite_close(db);
 288                         sqlite_free_table(results);
 289                         return (-1);
 290                 }
 291                 curr_version = strtol(results[1], &end, 10);
 292                 sqlite_free_table(results);
 293         }
 294 
 295         if (curr_version < 0) {
 296                 if (opt == REMOVE_IF_CORRUPT)
 297                         (void) unlink(dbname);
 298                 goto rinse_repeat;
 299         }
 300 
 301         if (curr_version == version)
 302                 goto done;
 303 
 304         /* Install or upgrade schema */
 305 #ifdef  IDMAPD_DEBUG
 306         (void) fprintf(stderr, "Schema init/upgrade SQL: %s\n",
 307             sql[curr_version]);
 308 #endif  /* IDMAPD_DEBUG */
 309         rc = sql_exec_tran_no_cb(db, sql[curr_version], dbname,
 310             (curr_version == 0) ? "installing schema" : "upgrading schema");
 311         if (rc != 0) {
 312                 idmapdlog(prio, "Error %s schema for db %s", dbname,
 313                     (curr_version == 0) ? "installing schema" :
 314                     "upgrading schema");
 315                 if (opt == REMOVE_IF_CORRUPT)
 316                         (void) unlink(dbname);
 317                 goto rinse_repeat;
 318         }
 319 
 320         *upgraded = (curr_version > 0);
 321         *created = (curr_version == 0);
 322 
 323 done:
 324         (void) sqlite_close(db);
 325         return (0);
 326 }
 327 
 328 
 329 /*
 330  * This is the SQLite database busy handler that retries the SQL
 331  * operation until it is successful.
 332  */
 333 int
 334 /* LINTED E_FUNC_ARG_UNUSED */
 335 idmap_sqlite_busy_handler(void *arg, const char *table_name, int count)
 336 {
 337         struct idmap_busy       *busy = arg;
 338         int                     delay;
 339         struct timespec         rqtp;
 340 
 341         if (count == 1)  {
 342                 busy->total = 0;
 343                 busy->sec = 2;
 344         }
 345         if (busy->total > 1000 * busy->sec) {
 346                 idmapdlog(LOG_DEBUG,
 347                     "Thread %d waited %d sec for the %s database",
 348                     pthread_self(), busy->sec, busy->name);
 349                 busy->sec++;
 350         }
 351 
 352         if (count <= busy->delay_size) {
 353                 delay = busy->delays[count-1];
 354         } else {
 355                 delay = busy->delays[busy->delay_size - 1];
 356         }
 357         busy->total += delay;
 358         rqtp.tv_sec = 0;
 359         rqtp.tv_nsec = delay * (NANOSEC / MILLISEC);
 360         (void) nanosleep(&rqtp, NULL);
 361         return (1);
 362 }
 363 
 364 
 365 /*
 366  * Get the database handle
 367  */
 368 idmap_retcode
 369 get_db_handle(sqlite **db)
 370 {
 371         char            *errmsg;
 372         idmap_tsd_t     *tsd;
 373 
 374         /*
 375          * Retrieve the db handle from thread-specific storage
 376          * If none exists, open and store in thread-specific storage.
 377          */
 378         if ((tsd = idmap_get_tsd()) == NULL) {
 379                 idmapdlog(LOG_ERR,
 380                     "Error getting thread specific data for %s", IDMAP_DBNAME);
 381                 return (IDMAP_ERR_MEMORY);
 382         }
 383 
 384         if (tsd->db_db == NULL) {
 385                 tsd->db_db = sqlite_open(IDMAP_DBNAME, 0, &errmsg);
 386                 if (tsd->db_db == NULL) {
 387                         idmapdlog(LOG_ERR, "Error opening database %s (%s)",
 388                             IDMAP_DBNAME, CHECK_NULL(errmsg));
 389                         sqlite_freemem(errmsg);
 390                         return (IDMAP_ERR_DB);
 391                 }
 392 
 393                 tsd->db_busy.name = IDMAP_DBNAME;
 394                 tsd->db_busy.delays = db_delay_table;
 395                 tsd->db_busy.delay_size = sizeof (db_delay_table) /
 396                     sizeof (int);
 397                 sqlite_busy_handler(tsd->db_db, idmap_sqlite_busy_handler,
 398                     &tsd->db_busy);
 399         }
 400         *db = tsd->db_db;
 401         return (IDMAP_SUCCESS);
 402 }
 403 
 404 /*
 405  * Get the cache handle
 406  */
 407 idmap_retcode
 408 get_cache_handle(sqlite **cache)
 409 {
 410         char            *errmsg;
 411         idmap_tsd_t     *tsd;
 412 
 413         /*
 414          * Retrieve the db handle from thread-specific storage
 415          * If none exists, open and store in thread-specific storage.
 416          */
 417         if ((tsd = idmap_get_tsd()) == NULL) {
 418                 idmapdlog(LOG_ERR, "Error getting thread specific data for %s",
 419                     IDMAP_DBNAME);
 420                 return (IDMAP_ERR_MEMORY);
 421         }
 422 
 423         if (tsd->cache_db == NULL) {
 424                 tsd->cache_db = sqlite_open(IDMAP_CACHENAME, 0, &errmsg);
 425                 if (tsd->cache_db == NULL) {
 426                         idmapdlog(LOG_ERR, "Error opening database %s (%s)",
 427                             IDMAP_CACHENAME, CHECK_NULL(errmsg));
 428                         sqlite_freemem(errmsg);
 429                         return (IDMAP_ERR_DB);
 430                 }
 431 
 432                 tsd->cache_busy.name = IDMAP_CACHENAME;
 433                 tsd->cache_busy.delays = cache_delay_table;
 434                 tsd->cache_busy.delay_size = sizeof (cache_delay_table) /
 435                     sizeof (int);
 436                 sqlite_busy_handler(tsd->cache_db, idmap_sqlite_busy_handler,
 437                     &tsd->cache_busy);
 438         }
 439         *cache = tsd->cache_db;
 440         return (IDMAP_SUCCESS);
 441 }
 442 
 443 /*
 444  * Initialize cache and db
 445  */
 446 int
 447 init_dbs()
 448 {
 449         char *sql[4];
 450         int created, upgraded;
 451 
 452         /* name-based mappings; probably OK to blow away in a pinch(?) */
 453         sql[0] = DB_INSTALL_SQL;
 454         sql[1] = DB_UPGRADE_FROM_v1_SQL;
 455         sql[2] = NULL;
 456 
 457         if (init_db_instance(IDMAP_DBNAME, DB_VERSION, DB_VERSION_SQL, sql,
 458             FAIL_IF_CORRUPT, &created, &upgraded) < 0)
 459                 return (-1);
 460 
 461         /* mappings, name/SID lookup cache + ephemeral IDs; OK to blow away */
 462         sql[0] = CACHE_INSTALL_SQL;
 463         sql[1] = CACHE_UPGRADE_FROM_v1_SQL;
 464         sql[2] = CACHE_UPGRADE_FROM_v2_SQL;
 465         sql[3] = NULL;
 466 
 467         if (init_db_instance(IDMAP_CACHENAME, CACHE_VERSION, CACHE_VERSION_SQL,
 468             sql, REMOVE_IF_CORRUPT, &created, &upgraded) < 0)
 469                 return (-1);
 470 
 471         _idmapdstate.new_eph_db = (created || upgraded) ? 1 : 0;
 472 
 473         return (0);
 474 }
 475 
 476 /*
 477  * Finalize databases
 478  */
 479 void
 480 fini_dbs()
 481 {
 482 }
 483 
 484 /*
 485  * This table is a listing of status codes that will be returned to the
 486  * client when a SQL command fails with the corresponding error message.
 487  */
 488 static msg_table_t sqlmsgtable[] = {
 489         {IDMAP_ERR_U2W_NAMERULE_CONFLICT,
 490         "columns unixname, is_user, u2w_order are not unique"},
 491         {IDMAP_ERR_W2U_NAMERULE_CONFLICT,
 492         "columns winname, windomain, is_user, is_wuser, w2u_order are not"
 493         " unique"},
 494         {IDMAP_ERR_W2U_NAMERULE_CONFLICT, "Conflicting w2u namerules"},
 495         {-1, NULL}
 496 };
 497 
 498 /*
 499  * idmapd's version of string2stat to map SQLite messages to
 500  * status codes
 501  */
 502 idmap_retcode
 503 idmapd_string2stat(const char *msg)
 504 {
 505         int i;
 506         for (i = 0; sqlmsgtable[i].msg; i++) {
 507                 if (strcasecmp(sqlmsgtable[i].msg, msg) == 0)
 508                         return (sqlmsgtable[i].retcode);
 509         }
 510         return (IDMAP_ERR_OTHER);
 511 }
 512 
 513 /*
 514  * Executes some SQL in a transaction.
 515  *
 516  * Returns 0 on success, -1 if it failed but the rollback succeeded, -2
 517  * if the rollback failed.
 518  */
 519 static
 520 int
 521 sql_exec_tran_no_cb(sqlite *db, char *sql, const char *dbname,
 522         const char *while_doing)
 523 {
 524         char            *errmsg = NULL;
 525         int             rc;
 526 
 527         rc = sqlite_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &errmsg);
 528         if (rc != SQLITE_OK) {
 529                 idmapdlog(LOG_ERR, "Begin transaction failed (%s) "
 530                     "while %s (%s)", errmsg, while_doing, dbname);
 531                 sqlite_freemem(errmsg);
 532                 return (-1);
 533         }
 534 
 535         rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
 536         if (rc != SQLITE_OK) {
 537                 idmapdlog(LOG_ERR, "Database error (%s) while %s (%s)", errmsg,
 538                     while_doing, dbname);
 539                 sqlite_freemem(errmsg);
 540                 errmsg = NULL;
 541                 goto rollback;
 542         }
 543 
 544         rc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL, &errmsg);
 545         if (rc == SQLITE_OK) {
 546                 sqlite_freemem(errmsg);
 547                 return (0);
 548         }
 549 
 550         idmapdlog(LOG_ERR, "Database commit error (%s) while s (%s)",
 551             errmsg, while_doing, dbname);
 552         sqlite_freemem(errmsg);
 553         errmsg = NULL;
 554 
 555 rollback:
 556         rc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, &errmsg);
 557         if (rc != SQLITE_OK) {
 558                 idmapdlog(LOG_ERR, "Rollback failed (%s) while %s (%s)",
 559                     errmsg, while_doing, dbname);
 560                 sqlite_freemem(errmsg);
 561                 return (-2);
 562         }
 563         sqlite_freemem(errmsg);
 564 
 565         return (-1);
 566 }
 567 
 568 /*
 569  * Execute the given SQL statment without using any callbacks
 570  */
 571 idmap_retcode
 572 sql_exec_no_cb(sqlite *db, const char *dbname, char *sql)
 573 {
 574         char            *errmsg = NULL;
 575         int             r;
 576         idmap_retcode   retcode;
 577 
 578         r = sqlite_exec(db, sql, NULL, NULL, &errmsg);
 579         assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
 580 
 581         if (r != SQLITE_OK) {
 582                 idmapdlog(LOG_ERR, "Database error on %s while executing %s "
 583                     "(%s)", dbname, sql, CHECK_NULL(errmsg));
 584                 retcode = idmapd_string2stat(errmsg);
 585                 if (errmsg != NULL)
 586                         sqlite_freemem(errmsg);
 587                 return (retcode);
 588         }
 589 
 590         return (IDMAP_SUCCESS);
 591 }
 592 
 593 /*
 594  * Generate expression that can be used in WHERE statements.
 595  * Examples:
 596  * <prefix> <col>      <op> <value>   <suffix>
 597  * ""       "unixuser" "="  "foo" "AND"
 598  */
 599 idmap_retcode
 600 gen_sql_expr_from_rule(idmap_namerule *rule, char **out)
 601 {
 602         char    *s_windomain = NULL, *s_winname = NULL;
 603         char    *s_unixname = NULL;
 604         char    *dir;
 605         char    *lower_winname;
 606         int     retcode = IDMAP_SUCCESS;
 607 
 608         if (out == NULL)
 609                 return (IDMAP_ERR_ARG);
 610 
 611 
 612         if (!EMPTY_STRING(rule->windomain)) {
 613                 s_windomain =  sqlite_mprintf("AND windomain = %Q ",
 614                     rule->windomain);
 615                 if (s_windomain == NULL) {
 616                         retcode = IDMAP_ERR_MEMORY;
 617                         goto out;
 618                 }
 619         }
 620 
 621         if (!EMPTY_STRING(rule->winname)) {
 622                 if ((lower_winname = tolower_u8(rule->winname)) == NULL)
 623                         lower_winname = rule->winname;
 624                 s_winname = sqlite_mprintf(
 625                     "AND winname = %Q AND is_wuser = %d ",
 626                     lower_winname, rule->is_wuser ? 1 : 0);
 627                 if (lower_winname != rule->winname)
 628                         free(lower_winname);
 629                 if (s_winname == NULL) {
 630                         retcode = IDMAP_ERR_MEMORY;
 631                         goto out;
 632                 }
 633         }
 634 
 635         if (!EMPTY_STRING(rule->unixname)) {
 636                 s_unixname = sqlite_mprintf(
 637                     "AND unixname = %Q AND is_user = %d ",
 638                     rule->unixname, rule->is_user ? 1 : 0);
 639                 if (s_unixname == NULL) {
 640                         retcode = IDMAP_ERR_MEMORY;
 641                         goto out;
 642                 }
 643         }
 644 
 645         switch (rule->direction) {
 646         case IDMAP_DIRECTION_BI:
 647                 dir = "AND w2u_order > 0 AND u2w_order > 0";
 648                 break;
 649         case IDMAP_DIRECTION_W2U:
 650                 dir = "AND w2u_order > 0"
 651                     " AND (u2w_order = 0 OR u2w_order ISNULL)";
 652                 break;
 653         case IDMAP_DIRECTION_U2W:
 654                 dir = "AND u2w_order > 0"
 655                     " AND (w2u_order = 0 OR w2u_order ISNULL)";
 656                 break;
 657         default:
 658                 dir = "";
 659                 break;
 660         }
 661 
 662         *out = sqlite_mprintf("%s %s %s %s",
 663             s_windomain ? s_windomain : "",
 664             s_winname ? s_winname : "",
 665             s_unixname ? s_unixname : "",
 666             dir);
 667 
 668         if (*out == NULL) {
 669                 retcode = IDMAP_ERR_MEMORY;
 670                 idmapdlog(LOG_ERR, "Out of memory");
 671                 goto out;
 672         }
 673 
 674 out:
 675         if (s_windomain != NULL)
 676                 sqlite_freemem(s_windomain);
 677         if (s_winname != NULL)
 678                 sqlite_freemem(s_winname);
 679         if (s_unixname != NULL)
 680                 sqlite_freemem(s_unixname);
 681 
 682         return (retcode);
 683 }
 684 
 685 
 686 
 687 /*
 688  * Generate and execute SQL statement for LIST RPC calls
 689  */
 690 idmap_retcode
 691 process_list_svc_sql(sqlite *db, const char *dbname, char *sql, uint64_t limit,
 692                 int flag, list_svc_cb cb, void *result)
 693 {
 694         list_cb_data_t  cb_data;
 695         char            *errmsg = NULL;
 696         int             r;
 697         idmap_retcode   retcode = IDMAP_ERR_INTERNAL;
 698 
 699         (void) memset(&cb_data, 0, sizeof (cb_data));
 700         cb_data.result = result;
 701         cb_data.limit = limit;
 702         cb_data.flag = flag;
 703 
 704 
 705         r = sqlite_exec(db, sql, cb, &cb_data, &errmsg);
 706         assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
 707         switch (r) {
 708         case SQLITE_OK:
 709                 retcode = IDMAP_SUCCESS;
 710                 break;
 711 
 712         default:
 713                 retcode = IDMAP_ERR_INTERNAL;
 714                 idmapdlog(LOG_ERR, "Database error on %s while executing "
 715                     "%s (%s)", dbname, sql, CHECK_NULL(errmsg));
 716                 break;
 717         }
 718         if (errmsg != NULL)
 719                 sqlite_freemem(errmsg);
 720         return (retcode);
 721 }
 722 
 723 /*
 724  * This routine is called by callbacks that process the results of
 725  * LIST RPC calls to validate data and to allocate memory for
 726  * the result array.
 727  */
 728 idmap_retcode
 729 validate_list_cb_data(list_cb_data_t *cb_data, int argc, char **argv,
 730                 int ncol, uchar_t **list, size_t valsize)
 731 {
 732         size_t  nsize;
 733         void    *tmplist;
 734 
 735         if (cb_data->limit > 0 && cb_data->next == cb_data->limit)
 736                 return (IDMAP_NEXT);
 737 
 738         if (argc < ncol || argv == NULL) {
 739                 idmapdlog(LOG_ERR, "Invalid data");
 740                 return (IDMAP_ERR_INTERNAL);
 741         }
 742 
 743         /* alloc in bulk to reduce number of reallocs */
 744         if (cb_data->next >= cb_data->len) {
 745                 nsize = (cb_data->len + SIZE_INCR) * valsize;
 746                 tmplist = realloc(*list, nsize);
 747                 if (tmplist == NULL) {
 748                         idmapdlog(LOG_ERR, "Out of memory");
 749                         return (IDMAP_ERR_MEMORY);
 750                 }
 751                 *list = tmplist;
 752                 (void) memset(*list + (cb_data->len * valsize), 0,
 753                     SIZE_INCR * valsize);
 754                 cb_data->len += SIZE_INCR;
 755         }
 756         return (IDMAP_SUCCESS);
 757 }
 758 
 759 static
 760 idmap_retcode
 761 get_namerule_order(char *winname, char *windomain, char *unixname,
 762         int direction, int is_diagonal, int *w2u_order, int *u2w_order)
 763 {
 764         *w2u_order = 0;
 765         *u2w_order = 0;
 766 
 767         /*
 768          * Windows to UNIX lookup order:
 769          *  1. winname@domain (or winname) to ""
 770          *  2. winname@domain (or winname) to unixname
 771          *  3. winname@* to ""
 772          *  4. winname@* to unixname
 773          *  5. *@domain (or *) to *
 774          *  6. *@domain (or *) to ""
 775          *  7. *@domain (or *) to unixname
 776          *  8. *@* to *
 777          *  9. *@* to ""
 778          * 10. *@* to unixname
 779          *
 780          * winname is a special case of winname@domain when domain is the
 781          * default domain. Similarly * is a special case of *@domain when
 782          * domain is the default domain.
 783          *
 784          * Note that "" has priority over specific names because "" inhibits
 785          * mappings and traditionally deny rules always had higher priority.
 786          */
 787         if (direction != IDMAP_DIRECTION_U2W) {
 788                 /* bi-directional or from windows to unix */
 789                 if (winname == NULL)
 790                         return (IDMAP_ERR_W2U_NAMERULE);
 791                 else if (unixname == NULL)
 792                         return (IDMAP_ERR_W2U_NAMERULE);
 793                 else if (EMPTY_NAME(winname))
 794                         return (IDMAP_ERR_W2U_NAMERULE);
 795                 else if (*winname == '*' && windomain && *windomain == '*') {
 796                         if (*unixname == '*')
 797                                 *w2u_order = 8;
 798                         else if (EMPTY_NAME(unixname))
 799                                 *w2u_order = 9;
 800                         else /* unixname == name */
 801                                 *w2u_order = 10;
 802                 } else if (*winname == '*') {
 803                         if (*unixname == '*')
 804                                 *w2u_order = 5;
 805                         else if (EMPTY_NAME(unixname))
 806                                 *w2u_order = 6;
 807                         else /* name */
 808                                 *w2u_order = 7;
 809                 } else if (windomain != NULL && *windomain == '*') {
 810                         /* winname == name */
 811                         if (*unixname == '*')
 812                                 return (IDMAP_ERR_W2U_NAMERULE);
 813                         else if (EMPTY_NAME(unixname))
 814                                 *w2u_order = 3;
 815                         else /* name */
 816                                 *w2u_order = 4;
 817                 } else  {
 818                         /* winname == name && windomain == null or name */
 819                         if (*unixname == '*')
 820                                 return (IDMAP_ERR_W2U_NAMERULE);
 821                         else if (EMPTY_NAME(unixname))
 822                                 *w2u_order = 1;
 823                         else /* name */
 824                                 *w2u_order = 2;
 825                 }
 826 
 827         }
 828 
 829         /*
 830          * 1. unixname to "", non-diagonal
 831          * 2. unixname to winname@domain (or winname), non-diagonal
 832          * 3. unixname to "", diagonal
 833          * 4. unixname to winname@domain (or winname), diagonal
 834          * 5. * to *@domain (or *), non-diagonal
 835          * 5. * to *@domain (or *), diagonal
 836          * 7. * to ""
 837          * 8. * to winname@domain (or winname)
 838          * 9. * to "", non-diagonal
 839          * 10. * to winname@domain (or winname), diagonal
 840          */
 841         if (direction != IDMAP_DIRECTION_W2U) {
 842                 int diagonal = is_diagonal ? 1 : 0;
 843 
 844                 /* bi-directional or from unix to windows */
 845                 if (unixname == NULL || EMPTY_NAME(unixname))
 846                         return (IDMAP_ERR_U2W_NAMERULE);
 847                 else if (winname == NULL)
 848                         return (IDMAP_ERR_U2W_NAMERULE);
 849                 else if (windomain != NULL && *windomain == '*')
 850                         return (IDMAP_ERR_U2W_NAMERULE);
 851                 else if (*unixname == '*') {
 852                         if (*winname == '*')
 853                                 *u2w_order = 5 + diagonal;
 854                         else if (EMPTY_NAME(winname))
 855                                 *u2w_order = 7 + 2 * diagonal;
 856                         else
 857                                 *u2w_order = 8 + 2 * diagonal;
 858                 } else {
 859                         if (*winname == '*')
 860                                 return (IDMAP_ERR_U2W_NAMERULE);
 861                         else if (EMPTY_NAME(winname))
 862                                 *u2w_order = 1 + 2 * diagonal;
 863                         else
 864                                 *u2w_order = 2 + 2 * diagonal;
 865                 }
 866         }
 867         return (IDMAP_SUCCESS);
 868 }
 869 
 870 /*
 871  * Generate and execute SQL statement to add name-based mapping rule
 872  */
 873 idmap_retcode
 874 add_namerule(sqlite *db, idmap_namerule *rule)
 875 {
 876         char            *sql = NULL;
 877         idmap_stat      retcode;
 878         char            *dom = NULL;
 879         char            *name;
 880         int             w2u_order, u2w_order;
 881         char            w2ubuf[11], u2wbuf[11];
 882         char            *canonname = NULL;
 883         char            *canondomain = NULL;
 884 
 885         retcode = get_namerule_order(rule->winname, rule->windomain,
 886             rule->unixname, rule->direction,
 887             rule->is_user == rule->is_wuser ? 0 : 1, &w2u_order, &u2w_order);
 888         if (retcode != IDMAP_SUCCESS)
 889                 goto out;
 890 
 891         if (w2u_order)
 892                 (void) snprintf(w2ubuf, sizeof (w2ubuf), "%d", w2u_order);
 893         if (u2w_order)
 894                 (void) snprintf(u2wbuf, sizeof (u2wbuf), "%d", u2w_order);
 895 
 896         /*
 897          * For the triggers on namerules table to work correctly:
 898          * 1) Use NULL instead of 0 for w2u_order and u2w_order
 899          * 2) Use "" instead of NULL for "no domain"
 900          */
 901 
 902         name = rule->winname;
 903         dom = rule->windomain;
 904 
 905         RDLOCK_CONFIG();
 906         if (lookup_wksids_name2sid(name, dom,
 907             &canonname, &canondomain,
 908             NULL, NULL, NULL) == IDMAP_SUCCESS) {
 909                 name = canonname;
 910                 dom = canondomain;
 911         } else if (EMPTY_STRING(dom)) {
 912                 if (_idmapdstate.cfg->pgcfg.default_domain)
 913                         dom = _idmapdstate.cfg->pgcfg.default_domain;
 914                 else
 915                         dom = "";
 916         }
 917         sql = sqlite_mprintf("INSERT into namerules "
 918             "(is_user, is_wuser, windomain, winname_display, is_nt4, "
 919             "unixname, w2u_order, u2w_order) "
 920             "VALUES(%d, %d, %Q, %Q, %d, %Q, %q, %q);",
 921             rule->is_user ? 1 : 0, rule->is_wuser ? 1 : 0, dom,
 922             name, rule->is_nt4 ? 1 : 0, rule->unixname,
 923             w2u_order ? w2ubuf : NULL, u2w_order ? u2wbuf : NULL);
 924         UNLOCK_CONFIG();
 925 
 926         if (sql == NULL) {
 927                 retcode = IDMAP_ERR_INTERNAL;
 928                 idmapdlog(LOG_ERR, "Out of memory");
 929                 goto out;
 930         }
 931 
 932         retcode = sql_exec_no_cb(db, IDMAP_DBNAME, sql);
 933 
 934         if (retcode == IDMAP_ERR_OTHER)
 935                 retcode = IDMAP_ERR_CFG;
 936 
 937 out:
 938         free(canonname);
 939         free(canondomain);
 940         if (sql != NULL)
 941                 sqlite_freemem(sql);
 942         return (retcode);
 943 }
 944 
 945 /*
 946  * Flush name-based mapping rules
 947  */
 948 idmap_retcode
 949 flush_namerules(sqlite *db)
 950 {
 951         idmap_stat      retcode;
 952 
 953         retcode = sql_exec_no_cb(db, IDMAP_DBNAME, "DELETE FROM namerules;");
 954 
 955         return (retcode);
 956 }
 957 
 958 /*
 959  * Generate and execute SQL statement to remove a name-based mapping rule
 960  */
 961 idmap_retcode
 962 rm_namerule(sqlite *db, idmap_namerule *rule)
 963 {
 964         char            *sql = NULL;
 965         idmap_stat      retcode;
 966         char            *expr = NULL;
 967 
 968         if (rule->direction < 0 && EMPTY_STRING(rule->windomain) &&
 969             EMPTY_STRING(rule->winname) && EMPTY_STRING(rule->unixname))
 970                 return (IDMAP_SUCCESS);
 971 
 972         retcode = gen_sql_expr_from_rule(rule, &expr);
 973         if (retcode != IDMAP_SUCCESS)
 974                 goto out;
 975 
 976         sql = sqlite_mprintf("DELETE FROM namerules WHERE 1 %s;", expr);
 977 
 978         if (sql == NULL) {
 979                 retcode = IDMAP_ERR_INTERNAL;
 980                 idmapdlog(LOG_ERR, "Out of memory");
 981                 goto out;
 982         }
 983 
 984 
 985         retcode = sql_exec_no_cb(db, IDMAP_DBNAME, sql);
 986 
 987 out:
 988         if (expr != NULL)
 989                 sqlite_freemem(expr);
 990         if (sql != NULL)
 991                 sqlite_freemem(sql);
 992         return (retcode);
 993 }
 994 
 995 /*
 996  * Compile the given SQL query and step just once.
 997  *
 998  * Input:
 999  * db  - db handle
1000  * sql - SQL statement
1001  *
1002  * Output:
1003  * vm     -  virtual SQL machine
1004  * ncol   - number of columns in the result
1005  * values - column values
1006  *
1007  * Return values:
1008  * IDMAP_SUCCESS
1009  * IDMAP_ERR_NOTFOUND
1010  * IDMAP_ERR_INTERNAL
1011  */
1012 
1013 static
1014 idmap_retcode
1015 sql_compile_n_step_once(sqlite *db, char *sql, sqlite_vm **vm, int *ncol,
1016                 int reqcol, const char ***values)
1017 {
1018         char            *errmsg = NULL;
1019         int             r;
1020 
1021         if ((r = sqlite_compile(db, sql, NULL, vm, &errmsg)) != SQLITE_OK) {
1022                 idmapdlog(LOG_ERR, "Database error during %s (%s)", sql,
1023                     CHECK_NULL(errmsg));
1024                 sqlite_freemem(errmsg);
1025                 return (IDMAP_ERR_INTERNAL);
1026         }
1027 
1028         r = sqlite_step(*vm, ncol, values, NULL);
1029         assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
1030 
1031         if (r == SQLITE_ROW) {
1032                 if (ncol != NULL && *ncol < reqcol) {
1033                         (void) sqlite_finalize(*vm, NULL);
1034                         *vm = NULL;
1035                         return (IDMAP_ERR_INTERNAL);
1036                 }
1037                 /* Caller will call finalize after using the results */
1038                 return (IDMAP_SUCCESS);
1039         } else if (r == SQLITE_DONE) {
1040                 (void) sqlite_finalize(*vm, NULL);
1041                 *vm = NULL;
1042                 return (IDMAP_ERR_NOTFOUND);
1043         }
1044 
1045         (void) sqlite_finalize(*vm, &errmsg);
1046         *vm = NULL;
1047         idmapdlog(LOG_ERR, "Database error during %s (%s)", sql,
1048             CHECK_NULL(errmsg));
1049         sqlite_freemem(errmsg);
1050         return (IDMAP_ERR_INTERNAL);
1051 }
1052 
1053 /*
1054  * Load config in the state.
1055  *
1056  * nm_siduid and nm_sidgid fields:
1057  * state->nm_siduid represents mode used by sid2uid and uid2sid
1058  * requests for directory-based name mappings. Similarly,
1059  * state->nm_sidgid represents mode used by sid2gid and gid2sid
1060  * requests.
1061  *
1062  * sid2uid/uid2sid:
1063  * none       -> directory_based_mapping != DIRECTORY_MAPPING_NAME
1064  * AD-mode    -> !nldap_winname_attr && ad_unixuser_attr
1065  * nldap-mode -> nldap_winname_attr && !ad_unixuser_attr
1066  * mixed-mode -> nldap_winname_attr && ad_unixuser_attr
1067  *
1068  * sid2gid/gid2sid:
1069  * none       -> directory_based_mapping != DIRECTORY_MAPPING_NAME
1070  * AD-mode    -> !nldap_winname_attr && ad_unixgroup_attr
1071  * nldap-mode -> nldap_winname_attr && !ad_unixgroup_attr
1072  * mixed-mode -> nldap_winname_attr && ad_unixgroup_attr
1073  */
1074 idmap_retcode
1075 load_cfg_in_state(lookup_state_t *state)
1076 {
1077         state->nm_siduid = IDMAP_NM_NONE;
1078         state->nm_sidgid = IDMAP_NM_NONE;
1079         RDLOCK_CONFIG();
1080 
1081         state->eph_map_unres_sids = 0;
1082         if (_idmapdstate.cfg->pgcfg.eph_map_unres_sids)
1083                 state->eph_map_unres_sids = 1;
1084 
1085         state->id_cache_timeout =
1086             _idmapdstate.cfg->pgcfg.id_cache_timeout;
1087         state->name_cache_timeout =
1088             _idmapdstate.cfg->pgcfg.name_cache_timeout;
1089 
1090         state->directory_based_mapping =
1091             _idmapdstate.cfg->pgcfg.directory_based_mapping;
1092 
1093         if (_idmapdstate.cfg->pgcfg.default_domain != NULL) {
1094                 state->defdom =
1095                     strdup(_idmapdstate.cfg->pgcfg.default_domain);
1096                 if (state->defdom == NULL) {
1097                         UNLOCK_CONFIG();
1098                         return (IDMAP_ERR_MEMORY);
1099                 }
1100         } else {
1101                 UNLOCK_CONFIG();
1102                 return (IDMAP_SUCCESS);
1103         }
1104 
1105         if (_idmapdstate.cfg->pgcfg.directory_based_mapping !=
1106             DIRECTORY_MAPPING_NAME) {
1107                 UNLOCK_CONFIG();
1108                 return (IDMAP_SUCCESS);
1109         }
1110 
1111         if (_idmapdstate.cfg->pgcfg.nldap_winname_attr != NULL) {
1112                 state->nm_siduid =
1113                     (_idmapdstate.cfg->pgcfg.ad_unixuser_attr != NULL)
1114                     ? IDMAP_NM_MIXED : IDMAP_NM_NLDAP;
1115                 state->nm_sidgid =
1116                     (_idmapdstate.cfg->pgcfg.ad_unixgroup_attr != NULL)
1117                     ? IDMAP_NM_MIXED : IDMAP_NM_NLDAP;
1118         } else {
1119                 state->nm_siduid =
1120                     (_idmapdstate.cfg->pgcfg.ad_unixuser_attr != NULL)
1121                     ? IDMAP_NM_AD : IDMAP_NM_NONE;
1122                 state->nm_sidgid =
1123                     (_idmapdstate.cfg->pgcfg.ad_unixgroup_attr != NULL)
1124                     ? IDMAP_NM_AD : IDMAP_NM_NONE;
1125         }
1126         if (_idmapdstate.cfg->pgcfg.ad_unixuser_attr != NULL) {
1127                 state->ad_unixuser_attr =
1128                     strdup(_idmapdstate.cfg->pgcfg.ad_unixuser_attr);
1129                 if (state->ad_unixuser_attr == NULL) {
1130                         UNLOCK_CONFIG();
1131                         return (IDMAP_ERR_MEMORY);
1132                 }
1133         }
1134         if (_idmapdstate.cfg->pgcfg.ad_unixgroup_attr != NULL) {
1135                 state->ad_unixgroup_attr =
1136                     strdup(_idmapdstate.cfg->pgcfg.ad_unixgroup_attr);
1137                 if (state->ad_unixgroup_attr == NULL) {
1138                         UNLOCK_CONFIG();
1139                         return (IDMAP_ERR_MEMORY);
1140                 }
1141         }
1142         if (_idmapdstate.cfg->pgcfg.nldap_winname_attr != NULL) {
1143                 state->nldap_winname_attr =
1144                     strdup(_idmapdstate.cfg->pgcfg.nldap_winname_attr);
1145                 if (state->nldap_winname_attr == NULL) {
1146                         UNLOCK_CONFIG();
1147                         return (IDMAP_ERR_MEMORY);
1148                 }
1149         }
1150         UNLOCK_CONFIG();
1151         return (IDMAP_SUCCESS);
1152 }
1153 
1154 /*
1155  * Set the rule with specified values.
1156  * All the strings are copied.
1157  */
1158 static void
1159 idmap_namerule_set(idmap_namerule *rule, const char *windomain,
1160                 const char *winname, const char *unixname, boolean_t is_user,
1161                 boolean_t is_wuser, boolean_t is_nt4, int direction)
1162 {
1163         /*
1164          * Only update if they differ because we have to free
1165          * and duplicate the strings
1166          */
1167         if (rule->windomain == NULL || windomain == NULL ||
1168             strcmp(rule->windomain, windomain) != 0) {
1169                 if (rule->windomain != NULL) {
1170                         free(rule->windomain);
1171                         rule->windomain = NULL;
1172                 }
1173                 if (windomain != NULL)
1174                         rule->windomain = strdup(windomain);
1175         }
1176 
1177         if (rule->winname == NULL || winname == NULL ||
1178             strcmp(rule->winname, winname) != 0) {
1179                 if (rule->winname != NULL) {
1180                         free(rule->winname);
1181                         rule->winname = NULL;
1182                 }
1183                 if (winname != NULL)
1184                         rule->winname = strdup(winname);
1185         }
1186 
1187         if (rule->unixname == NULL || unixname == NULL ||
1188             strcmp(rule->unixname, unixname) != 0) {
1189                 if (rule->unixname != NULL) {
1190                         free(rule->unixname);
1191                         rule->unixname = NULL;
1192                 }
1193                 if (unixname != NULL)
1194                         rule->unixname = strdup(unixname);
1195         }
1196 
1197         rule->is_user = is_user;
1198         rule->is_wuser = is_wuser;
1199         rule->is_nt4 = is_nt4;
1200         rule->direction = direction;
1201 }
1202 
1203 /*
1204  * Lookup well-known SIDs table either by winname or by SID.
1205  *
1206  * If the given winname or SID is a well-known SID then we set is_wksid
1207  * variable and then proceed to see if the SID has a hard mapping to
1208  * a particular UID/GID (Ex: Creator Owner/Creator Group mapped to
1209  * fixed ephemeral ids). The direction flag indicates whether we have
1210  * a mapping; UNDEF indicates that we do not.
1211  *
1212  * If we find a mapping then we return success, except for the
1213  * special case of IDMAP_SENTINEL_PID which indicates an inhibited mapping.
1214  *
1215  * If we find a matching entry, but no mapping, we supply SID, name, and type
1216  * information and return "not found".  Higher layers will probably
1217  * do ephemeral mapping.
1218  *
1219  * If we do not find a match, we return "not found" and leave the question
1220  * to higher layers.
1221  */
1222 static
1223 idmap_retcode
1224 lookup_wksids_sid2pid(idmap_mapping *req, idmap_id_res *res, int *is_wksid)
1225 {
1226         const wksids_table_t *wksid;
1227 
1228         *is_wksid = 0;
1229 
1230         assert(req->id1.idmap_id_u.sid.prefix != NULL ||
1231             req->id1name != NULL);
1232 
1233         if (req->id1.idmap_id_u.sid.prefix != NULL) {
1234                 wksid = find_wksid_by_sid(req->id1.idmap_id_u.sid.prefix,
1235                     req->id1.idmap_id_u.sid.rid, res->id.idtype);
1236         } else {
1237                 wksid = find_wksid_by_name(req->id1name, req->id1domain,
1238                     res->id.idtype);
1239         }
1240         if (wksid == NULL)
1241                 return (IDMAP_ERR_NOTFOUND);
1242 
1243         /* Found matching entry. */
1244 
1245         /* Fill in name if it was not already there. */
1246         if (req->id1name == NULL) {
1247                 req->id1name = strdup(wksid->winname);
1248                 if (req->id1name == NULL)
1249                         return (IDMAP_ERR_MEMORY);
1250         }
1251 
1252         /* Fill in SID if it was not already there */
1253         if (req->id1.idmap_id_u.sid.prefix == NULL) {
1254                 if (wksid->sidprefix != NULL) {
1255                         req->id1.idmap_id_u.sid.prefix =
1256                             strdup(wksid->sidprefix);
1257                 } else {
1258                         RDLOCK_CONFIG();
1259                         req->id1.idmap_id_u.sid.prefix =
1260                             strdup(_idmapdstate.cfg->pgcfg.machine_sid);
1261                         UNLOCK_CONFIG();
1262                 }
1263                 if (req->id1.idmap_id_u.sid.prefix == NULL)
1264                         return (IDMAP_ERR_MEMORY);
1265                 req->id1.idmap_id_u.sid.rid = wksid->rid;
1266         }
1267 
1268         /* Fill in the canonical domain if not already there */
1269         if (req->id1domain == NULL) {
1270                 const char *dom;
1271 
1272                 RDLOCK_CONFIG();
1273                 if (wksid->domain != NULL)
1274                         dom = wksid->domain;
1275                 else
1276                         dom = _idmapdstate.hostname;
1277                 req->id1domain = strdup(dom);
1278                 UNLOCK_CONFIG();
1279                 if (req->id1domain == NULL)
1280                         return (IDMAP_ERR_MEMORY);
1281         }
1282 
1283         *is_wksid = 1;
1284         req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
1285 
1286         req->id1.idtype = wksid->is_wuser ? IDMAP_USID : IDMAP_GSID;
1287 
1288         if (res->id.idtype == IDMAP_POSIXID) {
1289                 res->id.idtype = wksid->is_wuser ? IDMAP_UID : IDMAP_GID;
1290         }
1291 
1292         if (wksid->direction == IDMAP_DIRECTION_UNDEF) {
1293                 /*
1294                  * We don't have a mapping
1295                  * (But note that we may have supplied SID, name, or type
1296                  * information.)
1297                  */
1298                 return (IDMAP_ERR_NOTFOUND);
1299         }
1300 
1301         /*
1302          * We have an explicit mapping.
1303          */
1304         if (wksid->pid == IDMAP_SENTINEL_PID) {
1305                 /*
1306                  * ... which is that mapping is inhibited.
1307                  */
1308                 return (IDMAP_ERR_NOMAPPING);
1309         }
1310 
1311         switch (res->id.idtype) {
1312         case IDMAP_UID:
1313                 res->id.idmap_id_u.uid = wksid->pid;
1314                 break;
1315         case IDMAP_GID:
1316                 res->id.idmap_id_u.gid = wksid->pid;
1317                 break;
1318         default:
1319                 /* IDMAP_POSIXID is eliminated above */
1320                 return (IDMAP_ERR_NOTSUPPORTED);
1321         }
1322 
1323         res->direction = wksid->direction;
1324         res->info.how.map_type = IDMAP_MAP_TYPE_KNOWN_SID;
1325         res->info.src = IDMAP_MAP_SRC_HARD_CODED;
1326         return (IDMAP_SUCCESS);
1327 }
1328 
1329 
1330 /*
1331  * Look for an entry mapping a PID to a SID.
1332  *
1333  * Note that direction=UNDEF entries do not specify a mapping,
1334  * and that IDMAP_SENTINEL_PID entries represent either an inhibited
1335  * mapping or an ephemeral mapping.  We don't handle either here;
1336  * they are filtered out by find_wksid_by_pid.
1337  */
1338 static
1339 idmap_retcode
1340 lookup_wksids_pid2sid(idmap_mapping *req, idmap_id_res *res, int is_user)
1341 {
1342         const wksids_table_t *wksid;
1343 
1344         wksid = find_wksid_by_pid(req->id1.idmap_id_u.uid, is_user);
1345         if (wksid == NULL)
1346                 return (IDMAP_ERR_NOTFOUND);
1347 
1348         if (res->id.idtype == IDMAP_SID) {
1349                 res->id.idtype = wksid->is_wuser ? IDMAP_USID : IDMAP_GSID;
1350         }
1351         res->id.idmap_id_u.sid.rid = wksid->rid;
1352 
1353         if (wksid->sidprefix != NULL) {
1354                 res->id.idmap_id_u.sid.prefix =
1355                     strdup(wksid->sidprefix);
1356         } else {
1357                 RDLOCK_CONFIG();
1358                 res->id.idmap_id_u.sid.prefix =
1359                     strdup(_idmapdstate.cfg->pgcfg.machine_sid);
1360                 UNLOCK_CONFIG();
1361         }
1362 
1363         if (res->id.idmap_id_u.sid.prefix == NULL) {
1364                 idmapdlog(LOG_ERR, "Out of memory");
1365                 return (IDMAP_ERR_MEMORY);
1366         }
1367 
1368         /* Fill in name if it was not already there. */
1369         if (req->id2name == NULL) {
1370                 req->id2name = strdup(wksid->winname);
1371                 if (req->id2name == NULL)
1372                         return (IDMAP_ERR_MEMORY);
1373         }
1374 
1375         /* Fill in the canonical domain if not already there */
1376         if (req->id2domain == NULL) {
1377                 const char *dom;
1378 
1379                 RDLOCK_CONFIG();
1380                 if (wksid->domain != NULL)
1381                         dom = wksid->domain;
1382                 else
1383                         dom = _idmapdstate.hostname;
1384                 req->id2domain = strdup(dom);
1385                 UNLOCK_CONFIG();
1386                 if (req->id2domain == NULL)
1387                         return (IDMAP_ERR_MEMORY);
1388         }
1389 
1390         res->direction = wksid->direction;
1391         res->info.how.map_type = IDMAP_MAP_TYPE_KNOWN_SID;
1392         res->info.src = IDMAP_MAP_SRC_HARD_CODED;
1393         return (IDMAP_SUCCESS);
1394 }
1395 
1396 /*
1397  * Look up a name in the wksids list, matching name and, if supplied, domain,
1398  * and extract data.
1399  *
1400  * Given:
1401  * name         Windows user name
1402  * domain       Windows domain name (or NULL)
1403  *
1404  * Return:  Error code
1405  *
1406  * *canonname   canonical name (if canonname non-NULL) [1]
1407  * *canondomain canonical domain (if canondomain non-NULL) [1]
1408  * *sidprefix   SID prefix (if sidprefix non-NULL) [1]
1409  * *rid         RID (if rid non-NULL) [2]
1410  * *type        Type (if type non-NULL) [2]
1411  *
1412  * [1] malloc'ed, NULL on error
1413  * [2] Undefined on error
1414  */
1415 idmap_retcode
1416 lookup_wksids_name2sid(
1417     const char *name,
1418     const char *domain,
1419     char **canonname,
1420     char **canondomain,
1421     char **sidprefix,
1422     idmap_rid_t *rid,
1423     idmap_id_type *type)
1424 {
1425         const wksids_table_t *wksid;
1426 
1427         if (sidprefix != NULL)
1428                 *sidprefix = NULL;
1429         if (canonname != NULL)
1430                 *canonname = NULL;
1431         if (canondomain != NULL)
1432                 *canondomain = NULL;
1433 
1434         wksid = find_wksid_by_name(name, domain, IDMAP_POSIXID);
1435         if (wksid == NULL)
1436                 return (IDMAP_ERR_NOTFOUND);
1437 
1438         if (sidprefix != NULL) {
1439                 if (wksid->sidprefix != NULL) {
1440                         *sidprefix = strdup(wksid->sidprefix);
1441                 } else {
1442                         RDLOCK_CONFIG();
1443                         *sidprefix = strdup(
1444                             _idmapdstate.cfg->pgcfg.machine_sid);
1445                         UNLOCK_CONFIG();
1446                 }
1447                 if (*sidprefix == NULL)
1448                         goto nomem;
1449         }
1450 
1451         if (rid != NULL)
1452                 *rid = wksid->rid;
1453 
1454         if (canonname != NULL) {
1455                 *canonname = strdup(wksid->winname);
1456                 if (*canonname == NULL)
1457                         goto nomem;
1458         }
1459 
1460         if (canondomain != NULL) {
1461                 if (wksid->domain != NULL) {
1462                         *canondomain = strdup(wksid->domain);
1463                 } else {
1464                         RDLOCK_CONFIG();
1465                         *canondomain = strdup(_idmapdstate.hostname);
1466                         UNLOCK_CONFIG();
1467                 }
1468                 if (*canondomain == NULL)
1469                         goto nomem;
1470         }
1471 
1472         if (type != NULL)
1473                 *type = (wksid->is_wuser) ?
1474                     IDMAP_USID : IDMAP_GSID;
1475 
1476         return (IDMAP_SUCCESS);
1477 
1478 nomem:
1479         idmapdlog(LOG_ERR, "Out of memory");
1480 
1481         if (sidprefix != NULL) {
1482                 free(*sidprefix);
1483                 *sidprefix = NULL;
1484         }
1485 
1486         if (canonname != NULL) {
1487                 free(*canonname);
1488                 *canonname = NULL;
1489         }
1490 
1491         if (canondomain != NULL) {
1492                 free(*canondomain);
1493                 *canondomain = NULL;
1494         }
1495 
1496         return (IDMAP_ERR_MEMORY);
1497 }
1498 
1499 static
1500 idmap_retcode
1501 lookup_cache_sid2pid(sqlite *cache, idmap_mapping *req, idmap_id_res *res)
1502 {
1503         char            *end;
1504         char            *sql = NULL;
1505         const char      **values;
1506         sqlite_vm       *vm = NULL;
1507         int             ncol, is_user;
1508         uid_t           pid;
1509         time_t          curtime, exp;
1510         idmap_retcode   retcode;
1511         char            *is_user_string, *lower_name;
1512 
1513         /* Current time */
1514         errno = 0;
1515         if ((curtime = time(NULL)) == (time_t)-1) {
1516                 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
1517                     strerror(errno));
1518                 retcode = IDMAP_ERR_INTERNAL;
1519                 goto out;
1520         }
1521 
1522         switch (res->id.idtype) {
1523         case IDMAP_UID:
1524                 is_user_string = "1";
1525                 break;
1526         case IDMAP_GID:
1527                 is_user_string = "0";
1528                 break;
1529         case IDMAP_POSIXID:
1530                 /* the non-diagonal mapping */
1531                 is_user_string = "is_wuser";
1532                 break;
1533         default:
1534                 retcode = IDMAP_ERR_NOTSUPPORTED;
1535                 goto out;
1536         }
1537 
1538         /* SQL to lookup the cache */
1539 
1540         if (req->id1.idmap_id_u.sid.prefix != NULL) {
1541                 sql = sqlite_mprintf("SELECT pid, is_user, expiration, "
1542                     "unixname, u2w, is_wuser, "
1543                     "map_type, map_dn, map_attr, map_value, "
1544                     "map_windomain, map_winname, map_unixname, map_is_nt4 "
1545                     "FROM idmap_cache WHERE is_user = %s AND "
1546                     "sidprefix = %Q AND rid = %u AND w2u = 1 AND "
1547                     "(pid >= 2147483648 OR "
1548                     "(expiration = 0 OR expiration ISNULL OR "
1549                     "expiration > %d));",
1550                     is_user_string, req->id1.idmap_id_u.sid.prefix,
1551                     req->id1.idmap_id_u.sid.rid, curtime);
1552         } else if (req->id1name != NULL) {
1553                 if ((lower_name = tolower_u8(req->id1name)) == NULL)
1554                         lower_name = req->id1name;
1555                 sql = sqlite_mprintf("SELECT pid, is_user, expiration, "
1556                     "unixname, u2w, is_wuser, "
1557                     "map_type, map_dn, map_attr, map_value, "
1558                     "map_windomain, map_winname, map_unixname, map_is_nt4 "
1559                     "FROM idmap_cache WHERE is_user = %s AND "
1560                     "winname = %Q AND windomain = %Q AND w2u = 1 AND "
1561                     "(pid >= 2147483648 OR "
1562                     "(expiration = 0 OR expiration ISNULL OR "
1563                     "expiration > %d));",
1564                     is_user_string, lower_name, req->id1domain,
1565                     curtime);
1566                 if (lower_name != req->id1name)
1567                         free(lower_name);
1568         } else {
1569                 retcode = IDMAP_ERR_ARG;
1570                 goto out;
1571         }
1572         if (sql == NULL) {
1573                 idmapdlog(LOG_ERR, "Out of memory");
1574                 retcode = IDMAP_ERR_MEMORY;
1575                 goto out;
1576         }
1577         retcode = sql_compile_n_step_once(cache, sql, &vm, &ncol,
1578             14, &values);
1579         sqlite_freemem(sql);
1580 
1581         if (retcode == IDMAP_ERR_NOTFOUND) {
1582                 goto out;
1583         } else if (retcode == IDMAP_SUCCESS) {
1584                 /* sanity checks */
1585                 if (values[0] == NULL || values[1] == NULL) {
1586                         retcode = IDMAP_ERR_CACHE;
1587                         goto out;
1588                 }
1589 
1590                 pid = strtoul(values[0], &end, 10);
1591                 is_user = strncmp(values[1], "0", 2) ? 1 : 0;
1592 
1593                 if (is_user) {
1594                         res->id.idtype = IDMAP_UID;
1595                         res->id.idmap_id_u.uid = pid;
1596                 } else {
1597                         res->id.idtype = IDMAP_GID;
1598                         res->id.idmap_id_u.gid = pid;
1599                 }
1600 
1601                 /*
1602                  * We may have an expired ephemeral mapping. Consider
1603                  * the expired entry as valid if we are not going to
1604                  * perform name-based mapping. But do not renew the
1605                  * expiration.
1606                  * If we will be doing name-based mapping then store the
1607                  * ephemeral pid in the result so that we can use it
1608                  * if we end up doing dynamic mapping again.
1609                  */
1610                 if (!DO_NOT_ALLOC_NEW_ID_MAPPING(req) &&
1611                     !AVOID_NAMESERVICE(req) &&
1612                     IDMAP_ID_IS_EPHEMERAL(pid) && values[2] != NULL) {
1613                         exp = strtoll(values[2], &end, 10);
1614                         if (exp && exp <= curtime) {
1615                                 /* Store the ephemeral pid */
1616                                 res->direction = IDMAP_DIRECTION_BI;
1617                                 req->direction |= is_user
1618                                     ? _IDMAP_F_EXP_EPH_UID
1619                                     : _IDMAP_F_EXP_EPH_GID;
1620                                 retcode = IDMAP_ERR_NOTFOUND;
1621                         }
1622                 }
1623         }
1624 
1625 out:
1626         if (retcode == IDMAP_SUCCESS) {
1627                 if (values[4] != NULL)
1628                         res->direction =
1629                             (strtol(values[4], &end, 10) == 0)?
1630                             IDMAP_DIRECTION_W2U:IDMAP_DIRECTION_BI;
1631                 else
1632                         res->direction = IDMAP_DIRECTION_W2U;
1633 
1634                 if (values[3] != NULL) {
1635                         if (req->id2name != NULL)
1636                                 free(req->id2name);
1637                         req->id2name = strdup(values[3]);
1638                         if (req->id2name == NULL) {
1639                                 idmapdlog(LOG_ERR, "Out of memory");
1640                                 retcode = IDMAP_ERR_MEMORY;
1641                         }
1642                 }
1643 
1644                 req->id1.idtype = strncmp(values[5], "0", 2) ?
1645                     IDMAP_USID : IDMAP_GSID;
1646 
1647                 if (req->flag & IDMAP_REQ_FLG_MAPPING_INFO) {
1648                         res->info.src = IDMAP_MAP_SRC_CACHE;
1649                         res->info.how.map_type = strtoul(values[6], &end, 10);
1650                         switch (res->info.how.map_type) {
1651                         case IDMAP_MAP_TYPE_DS_AD:
1652                                 res->info.how.idmap_how_u.ad.dn =
1653                                     strdup(values[7]);
1654                                 res->info.how.idmap_how_u.ad.attr =
1655                                     strdup(values[8]);
1656                                 res->info.how.idmap_how_u.ad.value =
1657                                     strdup(values[9]);
1658                                 break;
1659 
1660                         case IDMAP_MAP_TYPE_DS_NLDAP:
1661                                 res->info.how.idmap_how_u.nldap.dn =
1662                                     strdup(values[7]);
1663                                 res->info.how.idmap_how_u.nldap.attr =
1664                                     strdup(values[8]);
1665                                 res->info.how.idmap_how_u.nldap.value =
1666                                     strdup(values[9]);
1667                                 break;
1668 
1669                         case IDMAP_MAP_TYPE_RULE_BASED:
1670                                 res->info.how.idmap_how_u.rule.windomain =
1671                                     strdup(values[10]);
1672                                 res->info.how.idmap_how_u.rule.winname =
1673                                     strdup(values[11]);
1674                                 res->info.how.idmap_how_u.rule.unixname =
1675                                     strdup(values[12]);
1676                                 res->info.how.idmap_how_u.rule.is_nt4 =
1677                                     strtoul(values[13], &end, 1);
1678                                 res->info.how.idmap_how_u.rule.is_user =
1679                                     is_user;
1680                                 res->info.how.idmap_how_u.rule.is_wuser =
1681                                     strtoul(values[5], &end, 1);
1682                                 break;
1683 
1684                         case IDMAP_MAP_TYPE_EPHEMERAL:
1685                                 break;
1686 
1687                         case IDMAP_MAP_TYPE_LOCAL_SID:
1688                                 break;
1689 
1690                         case IDMAP_MAP_TYPE_KNOWN_SID:
1691                                 break;
1692 
1693                         case IDMAP_MAP_TYPE_IDMU:
1694                                 res->info.how.idmap_how_u.idmu.dn =
1695                                     strdup(values[7]);
1696                                 res->info.how.idmap_how_u.idmu.attr =
1697                                     strdup(values[8]);
1698                                 res->info.how.idmap_how_u.idmu.value =
1699                                     strdup(values[9]);
1700                                 break;
1701 
1702                         default:
1703                                 /* Unknown mapping type */
1704                                 assert(FALSE);
1705                         }
1706                 }
1707         }
1708         if (vm != NULL)
1709                 (void) sqlite_finalize(vm, NULL);
1710         return (retcode);
1711 }
1712 
1713 /*
1714  * Previous versions used two enumerations for representing types.
1715  * One of those has largely been eliminated, but was used in the
1716  * name cache table and so during an upgrade might still be visible.
1717  * In addition, the test suite prepopulates the cache with these values.
1718  *
1719  * This function translates those old values into the new values.
1720  *
1721  * This code deliberately does not use symbolic values for the legacy
1722  * values.  This is the *only* place where they should be used.
1723  */
1724 static
1725 idmap_id_type
1726 xlate_legacy_type(int type)
1727 {
1728         switch (type) {
1729         case -1004:     /* _IDMAP_T_USER */
1730                 return (IDMAP_USID);
1731         case -1005:     /* _IDMAP_T_GROUP */
1732                 return (IDMAP_GSID);
1733         default:
1734                 return (type);
1735         }
1736         NOTE(NOTREACHED)
1737 }
1738 
1739 static
1740 idmap_retcode
1741 lookup_cache_sid2name(sqlite *cache, const char *sidprefix, idmap_rid_t rid,
1742                 char **canonname, char **canondomain, idmap_id_type *type)
1743 {
1744         char            *end;
1745         char            *sql = NULL;
1746         const char      **values;
1747         sqlite_vm       *vm = NULL;
1748         int             ncol;
1749         time_t          curtime;
1750         idmap_retcode   retcode = IDMAP_SUCCESS;
1751 
1752         /* Get current time */
1753         errno = 0;
1754         if ((curtime = time(NULL)) == (time_t)-1) {
1755                 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
1756                     strerror(errno));
1757                 retcode = IDMAP_ERR_INTERNAL;
1758                 goto out;
1759         }
1760 
1761         /* SQL to lookup the cache */
1762         sql = sqlite_mprintf("SELECT canon_name, domain, type "
1763             "FROM name_cache WHERE "
1764             "sidprefix = %Q AND rid = %u AND "
1765             "(expiration = 0 OR expiration ISNULL OR "
1766             "expiration > %d);",
1767             sidprefix, rid, curtime);
1768         if (sql == NULL) {
1769                 idmapdlog(LOG_ERR, "Out of memory");
1770                 retcode = IDMAP_ERR_MEMORY;
1771                 goto out;
1772         }
1773         retcode = sql_compile_n_step_once(cache, sql, &vm, &ncol, 3, &values);
1774         sqlite_freemem(sql);
1775 
1776         if (retcode == IDMAP_SUCCESS) {
1777                 if (type != NULL) {
1778                         if (values[2] == NULL) {
1779                                 retcode = IDMAP_ERR_CACHE;
1780                                 goto out;
1781                         }
1782                         *type = xlate_legacy_type(strtol(values[2], &end, 10));
1783                 }
1784 
1785                 if (canonname != NULL && values[0] != NULL) {
1786                         if ((*canonname = strdup(values[0])) == NULL) {
1787                                 idmapdlog(LOG_ERR, "Out of memory");
1788                                 retcode = IDMAP_ERR_MEMORY;
1789                                 goto out;
1790                         }
1791                 }
1792 
1793                 if (canondomain != NULL && values[1] != NULL) {
1794                         if ((*canondomain = strdup(values[1])) == NULL) {
1795                                 if (canonname != NULL) {
1796                                         free(*canonname);
1797                                         *canonname = NULL;
1798                                 }
1799                                 idmapdlog(LOG_ERR, "Out of memory");
1800                                 retcode = IDMAP_ERR_MEMORY;
1801                                 goto out;
1802                         }
1803                 }
1804         }
1805 
1806 out:
1807         if (vm != NULL)
1808                 (void) sqlite_finalize(vm, NULL);
1809         return (retcode);
1810 }
1811 
1812 /*
1813  * Given SID, find winname using name_cache OR
1814  * Given winname, find SID using name_cache.
1815  * Used when mapping win to unix i.e. req->id1 is windows id and
1816  * req->id2 is unix id
1817  */
1818 static
1819 idmap_retcode
1820 lookup_name_cache(sqlite *cache, idmap_mapping *req, idmap_id_res *res)
1821 {
1822         idmap_id_type   type = -1;
1823         idmap_retcode   retcode;
1824         char            *sidprefix = NULL;
1825         idmap_rid_t     rid;
1826         char            *name = NULL, *domain = NULL;
1827 
1828         /* Done if we've both sid and winname */
1829         if (req->id1.idmap_id_u.sid.prefix != NULL && req->id1name != NULL) {
1830                 /* Don't bother TRACE()ing, too boring */
1831                 return (IDMAP_SUCCESS);
1832         }
1833 
1834         if (req->id1.idmap_id_u.sid.prefix != NULL) {
1835                 /* Lookup sid to winname */
1836                 retcode = lookup_cache_sid2name(cache,
1837                     req->id1.idmap_id_u.sid.prefix,
1838                     req->id1.idmap_id_u.sid.rid, &name, &domain, &type);
1839         } else {
1840                 /* Lookup winame to sid */
1841                 retcode = lookup_cache_name2sid(cache, req->id1name,
1842                     req->id1domain, &name, &sidprefix, &rid, &type);
1843         }
1844 
1845         if (retcode != IDMAP_SUCCESS) {
1846                 if (retcode == IDMAP_ERR_NOTFOUND) {
1847                         TRACE(req, res, "Not found in name cache");
1848                 } else {
1849                         TRACE(req, res, "Name cache lookup error=%d", retcode);
1850                 }
1851                 free(name);
1852                 free(domain);
1853                 free(sidprefix);
1854                 return (retcode);
1855         }
1856 
1857         req->id1.idtype = type;
1858 
1859         req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
1860 
1861         /*
1862          * If we found canonical names or domain, use them instead of
1863          * the existing values.
1864          */
1865         if (name != NULL) {
1866                 free(req->id1name);
1867                 req->id1name = name;
1868         }
1869         if (domain != NULL) {
1870                 free(req->id1domain);
1871                 req->id1domain = domain;
1872         }
1873 
1874         if (req->id1.idmap_id_u.sid.prefix == NULL) {
1875                 req->id1.idmap_id_u.sid.prefix = sidprefix;
1876                 req->id1.idmap_id_u.sid.rid = rid;
1877         }
1878 
1879         TRACE(req, res, "Found in name cache");
1880         return (retcode);
1881 }
1882 
1883 
1884 
1885 static int
1886 ad_lookup_batch_int(lookup_state_t *state, idmap_mapping_batch *batch,
1887                 idmap_ids_res *result, adutils_ad_t *dir, int how_local,
1888                 int *num_processed)
1889 {
1890         idmap_retcode   retcode;
1891         int             i,  num_queued, is_wuser, is_user;
1892         int             next_request;
1893         int             retries = 0, esidtype;
1894         char            **unixname;
1895         idmap_mapping   *req;
1896         idmap_id_res    *res;
1897         idmap_query_state_t     *qs = NULL;
1898         idmap_how       *how;
1899         char            **dn, **attr, **value;
1900 
1901         *num_processed = 0;
1902 
1903         /*
1904          * Since req->id2.idtype is unused, we will use it here
1905          * to retrieve the value of sid_type. But it needs to be
1906          * reset to IDMAP_NONE before we return to prevent xdr
1907          * from mis-interpreting req->id2 when it tries to free
1908          * the input argument. Other option is to allocate an
1909          * array of integers and use it instead for the batched
1910          * call. But why un-necessarily allocate memory. That may
1911          * be an option if req->id2.idtype cannot be re-used in
1912          * future.
1913          *
1914          * Similarly, we use req->id2.idmap_id_u.uid to return
1915          * uidNumber or gidNumber supplied by IDMU, and reset it
1916          * back to IDMAP_SENTINEL_PID when we're done.  Note that
1917          * the query always puts the result in req->id2.idmap_id_u.uid,
1918          * not .gid.
1919          */
1920 retry:
1921         retcode = idmap_lookup_batch_start(dir, state->ad_nqueries,
1922             state->directory_based_mapping,
1923             state->defdom,
1924             &qs);
1925         if (retcode != IDMAP_SUCCESS) {
1926                 if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
1927                     retries++ < ADUTILS_DEF_NUM_RETRIES)
1928                         goto retry;
1929                 degrade_svc(1, "failed to create batch for AD lookup");
1930                         goto out;
1931         }
1932         num_queued = 0;
1933 
1934         restore_svc();
1935 
1936         if (how_local & FOREST_IS_LOCAL) {
1937                 /*
1938                  * Directory based name mapping is only performed within the
1939                  * joined forest.  We don't trust other "trusted"
1940                  * forests to provide DS-based name mapping information because
1941                  * AD's definition of "cross-forest trust" does not encompass
1942                  * this sort of behavior.
1943                  */
1944                 idmap_lookup_batch_set_unixattr(qs,
1945                     state->ad_unixuser_attr, state->ad_unixgroup_attr);
1946         }
1947 
1948         for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
1949                 req = &batch->idmap_mapping_batch_val[i];
1950                 res = &result->ids.ids_val[i];
1951                 how = &res->info.how;
1952 
1953                 retcode = IDMAP_SUCCESS;
1954                 req->id2.idtype = IDMAP_NONE;
1955                 req->id2.idmap_id_u.uid = IDMAP_SENTINEL_PID;
1956 
1957                 /* Skip if no AD lookup required */
1958                 if (!(req->direction & _IDMAP_F_LOOKUP_AD))
1959                         continue;
1960 
1961                 /* Skip if we've already tried and gotten a "not found" */
1962                 if (req->direction & _IDMAP_F_LOOKUP_OTHER_AD)
1963                         continue;
1964 
1965                 /* Skip if we've already either succeeded or failed */
1966                 if (res->retcode != IDMAP_ERR_RETRIABLE_NET_ERR)
1967                         continue;
1968 
1969                 if (IS_ID_SID(req->id1)) {
1970 
1971                         /* win2unix request: */
1972 
1973                         posix_id_t *pid = NULL;
1974                         unixname = dn = attr = value = NULL;
1975                         esidtype = IDMAP_SID;
1976                         if (state->directory_based_mapping ==
1977                             DIRECTORY_MAPPING_NAME &&
1978                             req->id2name == NULL) {
1979                                 if (res->id.idtype == IDMAP_UID &&
1980                                     AD_OR_MIXED(state->nm_siduid)) {
1981                                         esidtype = IDMAP_USID;
1982                                         unixname = &req->id2name;
1983                                 } else if (res->id.idtype == IDMAP_GID &&
1984                                     AD_OR_MIXED(state->nm_sidgid)) {
1985                                         esidtype = IDMAP_GSID;
1986                                         unixname = &req->id2name;
1987                                 } else if (AD_OR_MIXED(state->nm_siduid) ||
1988                                     AD_OR_MIXED(state->nm_sidgid)) {
1989                                         unixname = &req->id2name;
1990                                 }
1991 
1992                                 if (unixname != NULL) {
1993                                         /*
1994                                          * Get how info for DS-based name
1995                                          * mapping only if AD or MIXED
1996                                          * mode is enabled.
1997                                          */
1998                                         idmap_how_clear(&res->info.how);
1999                                         res->info.src = IDMAP_MAP_SRC_NEW;
2000                                         how->map_type = IDMAP_MAP_TYPE_DS_AD;
2001                                         dn = &how->idmap_how_u.ad.dn;
2002                                         attr = &how->idmap_how_u.ad.attr;
2003                                         value = &how->idmap_how_u.ad.value;
2004                                 }
2005                         } else if (state->directory_based_mapping ==
2006                             DIRECTORY_MAPPING_IDMU &&
2007                             (how_local & DOMAIN_IS_LOCAL)) {
2008                                 /*
2009                                  * Ensure that we only do IDMU processing
2010                                  * when querying the domain we've joined.
2011                                  */
2012                                 pid = &req->id2.idmap_id_u.uid;
2013                                 /*
2014                                  * Get how info for IDMU based mapping.
2015                                  */
2016                                 idmap_how_clear(&res->info.how);
2017                                 res->info.src = IDMAP_MAP_SRC_NEW;
2018                                 how->map_type = IDMAP_MAP_TYPE_IDMU;
2019                                 dn = &how->idmap_how_u.idmu.dn;
2020                                 attr = &how->idmap_how_u.idmu.attr;
2021                                 value = &how->idmap_how_u.idmu.value;
2022                         }
2023 
2024                         if (req->id1.idmap_id_u.sid.prefix != NULL) {
2025                                 /* Lookup AD by SID */
2026                                 retcode = idmap_sid2name_batch_add1(
2027                                     qs, req->id1.idmap_id_u.sid.prefix,
2028                                     &req->id1.idmap_id_u.sid.rid, esidtype,
2029                                     dn, attr, value,
2030                                     (req->id1name == NULL) ?
2031                                     &req->id1name : NULL,
2032                                     (req->id1domain == NULL) ?
2033                                     &req->id1domain : NULL,
2034                                     &req->id2.idtype, unixname,
2035                                     pid,
2036                                     &res->retcode);
2037                                 if (retcode == IDMAP_SUCCESS)
2038                                         num_queued++;
2039                         } else {
2040                                 /* Lookup AD by winname */
2041                                 assert(req->id1name != NULL);
2042                                 retcode = idmap_name2sid_batch_add1(
2043                                     qs, req->id1name, req->id1domain,
2044                                     esidtype,
2045                                     dn, attr, value,
2046                                     &req->id1name,
2047                                     &req->id1.idmap_id_u.sid.prefix,
2048                                     &req->id1.idmap_id_u.sid.rid,
2049                                     &req->id2.idtype, unixname,
2050                                     pid,
2051                                     &res->retcode);
2052                                 if (retcode == IDMAP_SUCCESS)
2053                                         num_queued++;
2054                         }
2055 
2056                 } else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
2057 
2058                         /* unix2win request: */
2059 
2060                         if (res->id.idmap_id_u.sid.prefix != NULL &&
2061                             req->id2name != NULL) {
2062                                 /* Already have SID and winname. done */
2063                                 res->retcode = IDMAP_SUCCESS;
2064                                 continue;
2065                         }
2066 
2067                         if (res->id.idmap_id_u.sid.prefix != NULL) {
2068                                 /*
2069                                  * SID but no winname -- lookup AD by
2070                                  * SID to get winname.
2071                                  * how info is not needed here because
2072                                  * we are not retrieving unixname from
2073                                  * AD.
2074                                  */
2075 
2076                                 retcode = idmap_sid2name_batch_add1(
2077                                     qs, res->id.idmap_id_u.sid.prefix,
2078                                     &res->id.idmap_id_u.sid.rid,
2079                                     IDMAP_POSIXID,
2080                                     NULL, NULL, NULL,
2081                                     &req->id2name,
2082                                     &req->id2domain, &req->id2.idtype,
2083                                     NULL, NULL, &res->retcode);
2084                                 if (retcode == IDMAP_SUCCESS)
2085                                         num_queued++;
2086                         } else if (req->id2name != NULL) {
2087                                 /*
2088                                  * winname but no SID -- lookup AD by
2089                                  * winname to get SID.
2090                                  * how info is not needed here because
2091                                  * we are not retrieving unixname from
2092                                  * AD.
2093                                  */
2094                                 retcode = idmap_name2sid_batch_add1(
2095                                     qs, req->id2name, req->id2domain,
2096                                     IDMAP_POSIXID,
2097                                     NULL, NULL, NULL, NULL,
2098                                     &res->id.idmap_id_u.sid.prefix,
2099                                     &res->id.idmap_id_u.sid.rid,
2100                                     &req->id2.idtype, NULL,
2101                                     NULL,
2102                                     &res->retcode);
2103                                 if (retcode == IDMAP_SUCCESS)
2104                                         num_queued++;
2105                         } else if (state->directory_based_mapping ==
2106                             DIRECTORY_MAPPING_IDMU &&
2107                             (how_local & DOMAIN_IS_LOCAL)) {
2108                                 assert(req->id1.idmap_id_u.uid !=
2109                                     IDMAP_SENTINEL_PID);
2110                                 is_user = IS_ID_UID(req->id1);
2111                                 if (res->id.idtype == IDMAP_USID)
2112                                         is_wuser = 1;
2113                                 else if (res->id.idtype == IDMAP_GSID)
2114                                         is_wuser = 0;
2115                                 else
2116                                         is_wuser = is_user;
2117 
2118                                 /* IDMU can't do diagonal mappings */
2119                                 if (is_user != is_wuser)
2120                                         continue;
2121 
2122                                 idmap_how_clear(&res->info.how);
2123                                 res->info.src = IDMAP_MAP_SRC_NEW;
2124                                 how->map_type = IDMAP_MAP_TYPE_IDMU;
2125                                 retcode = idmap_pid2sid_batch_add1(
2126                                     qs, req->id1.idmap_id_u.uid, is_user,
2127                                     &how->idmap_how_u.ad.dn,
2128                                     &how->idmap_how_u.ad.attr,
2129                                     &how->idmap_how_u.ad.value,
2130                                     &res->id.idmap_id_u.sid.prefix,
2131                                     &res->id.idmap_id_u.sid.rid,
2132                                     &req->id2name, &req->id2domain,
2133                                     &req->id2.idtype, &res->retcode);
2134                                 if (retcode == IDMAP_SUCCESS)
2135                                         num_queued++;
2136                         } else if (req->id1name != NULL) {
2137                                 /*
2138                                  * No SID and no winname but we've unixname.
2139                                  * Lookup AD by unixname to get SID.
2140                                  */
2141                                 is_user = (IS_ID_UID(req->id1)) ? 1 : 0;
2142                                 if (res->id.idtype == IDMAP_USID)
2143                                         is_wuser = 1;
2144                                 else if (res->id.idtype == IDMAP_GSID)
2145                                         is_wuser = 0;
2146                                 else
2147                                         is_wuser = is_user;
2148 
2149                                 idmap_how_clear(&res->info.how);
2150                                 res->info.src = IDMAP_MAP_SRC_NEW;
2151                                 how->map_type = IDMAP_MAP_TYPE_DS_AD;
2152                                 retcode = idmap_unixname2sid_batch_add1(
2153                                     qs, req->id1name, is_user, is_wuser,
2154                                     &how->idmap_how_u.ad.dn,
2155                                     &how->idmap_how_u.ad.attr,
2156                                     &how->idmap_how_u.ad.value,
2157                                     &res->id.idmap_id_u.sid.prefix,
2158                                     &res->id.idmap_id_u.sid.rid,
2159                                     &req->id2name, &req->id2domain,
2160                                     &req->id2.idtype, &res->retcode);
2161                                 if (retcode == IDMAP_SUCCESS)
2162                                         num_queued++;
2163                         }
2164                 }
2165 
2166                 if (retcode == IDMAP_ERR_DOMAIN_NOTFOUND) {
2167                         req->direction |= _IDMAP_F_LOOKUP_OTHER_AD;
2168                         retcode = IDMAP_SUCCESS;
2169                 } else if (retcode != IDMAP_SUCCESS) {
2170                         break;
2171                 }
2172         } /* End of for loop */
2173 
2174         if (retcode == IDMAP_SUCCESS) {
2175                 /* add keeps track if we added an entry to the batch */
2176                 if (num_queued > 0)
2177                         retcode = idmap_lookup_batch_end(&qs);
2178                 else
2179                         idmap_lookup_release_batch(&qs);
2180         } else {
2181                 idmap_lookup_release_batch(&qs);
2182                 num_queued = 0;
2183                 next_request = i + 1;
2184         }
2185 
2186         if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
2187             retries++ < ADUTILS_DEF_NUM_RETRIES)
2188                 goto retry;
2189         else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR)
2190                 degrade_svc(1, "some AD lookups timed out repeatedly");
2191 
2192         if (retcode != IDMAP_SUCCESS) {
2193                 /* Mark any unproccessed requests for an other AD */
2194                 for (i = next_request; i < batch->idmap_mapping_batch_len;
2195                     i++) {
2196                         req = &batch->idmap_mapping_batch_val[i];
2197                         req->direction |= _IDMAP_F_LOOKUP_OTHER_AD;
2198 
2199                 }
2200         }
2201 
2202         if (retcode != IDMAP_SUCCESS)
2203                 idmapdlog(LOG_NOTICE, "Failed to batch AD lookup requests");
2204 
2205 out:
2206         /*
2207          * This loop does the following:
2208          * 1. Reset _IDMAP_F_LOOKUP_AD flag from the request.
2209          * 2. Reset req->id2.idtype to IDMAP_NONE
2210          * 3. If batch_start or batch_add failed then set the status
2211          *    of each request marked for AD lookup to that error.
2212          * 4. Evaluate the type of the AD object (i.e. user or group)
2213          *    and update the idtype in request.
2214          */
2215         for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
2216                 idmap_id_type type;
2217                 uid_t posix_id;
2218 
2219                 req = &batch->idmap_mapping_batch_val[i];
2220                 type = req->id2.idtype;
2221                 req->id2.idtype = IDMAP_NONE;
2222                 posix_id = req->id2.idmap_id_u.uid;
2223                 req->id2.idmap_id_u.uid = IDMAP_SENTINEL_PID;
2224                 res = &result->ids.ids_val[i];
2225 
2226                 /*
2227                  * If it didn't need AD lookup, ignore it.
2228                  */
2229                 if (!(req->direction & _IDMAP_F_LOOKUP_AD))
2230                         continue;
2231 
2232                 /*
2233                  * If we deferred it this time, reset for the next
2234                  * AD server.
2235                  */
2236                 if (req->direction & _IDMAP_F_LOOKUP_OTHER_AD) {
2237                         req->direction &= ~_IDMAP_F_LOOKUP_OTHER_AD;
2238                         continue;
2239                 }
2240 
2241                 /* Count number processed */
2242                 (*num_processed)++;
2243 
2244                 /* Reset AD lookup flag */
2245                 req->direction &= ~(_IDMAP_F_LOOKUP_AD);
2246 
2247                 /*
2248                  * If batch_start or batch_add failed then set the
2249                  * status of each request marked for AD lookup to
2250                  * that error.
2251                  */
2252                 if (retcode != IDMAP_SUCCESS) {
2253                         res->retcode = retcode;
2254                         continue;
2255                 }
2256 
2257                 if (res->retcode == IDMAP_ERR_NOTFOUND) {
2258                         /* Nothing found - remove the preset info */
2259                         idmap_how_clear(&res->info.how);
2260                 }
2261 
2262                 if (IS_ID_SID(req->id1)) {
2263                         if (res->retcode == IDMAP_ERR_NOTFOUND) {
2264                                 TRACE(req, res, "Not found in AD");
2265                                 continue;
2266                         }
2267                         if (res->retcode != IDMAP_SUCCESS) {
2268                                 TRACE(req, res, "AD lookup error=%d",
2269                                     res->retcode);
2270                                 continue;
2271                         }
2272                         /* Evaluate result type */
2273                         switch (type) {
2274                         case IDMAP_USID:
2275                                 if (res->id.idtype == IDMAP_POSIXID)
2276                                         res->id.idtype = IDMAP_UID;
2277                                 /*
2278                                  * We found a user.  If we got information
2279                                  * from IDMU and we were expecting a user,
2280                                  * copy the id.
2281                                  */
2282                                 if (posix_id != IDMAP_SENTINEL_PID &&
2283                                     res->id.idtype == IDMAP_UID) {
2284                                         res->id.idmap_id_u.uid = posix_id;
2285                                         res->direction = IDMAP_DIRECTION_BI;
2286                                         res->info.how.map_type =
2287                                             IDMAP_MAP_TYPE_IDMU;
2288                                         res->info.src = IDMAP_MAP_SRC_NEW;
2289                                 }
2290                                 req->id1.idtype = IDMAP_USID;
2291                                 break;
2292 
2293                         case IDMAP_GSID:
2294                                 if (res->id.idtype == IDMAP_POSIXID)
2295                                         res->id.idtype = IDMAP_GID;
2296                                 /*
2297                                  * We found a group.  If we got information
2298                                  * from IDMU and we were expecting a group,
2299                                  * copy the id.
2300                                  */
2301                                 if (posix_id != IDMAP_SENTINEL_PID &&
2302                                     res->id.idtype == IDMAP_GID) {
2303                                         res->id.idmap_id_u.gid = posix_id;
2304                                         res->direction = IDMAP_DIRECTION_BI;
2305                                         res->info.how.map_type =
2306                                             IDMAP_MAP_TYPE_IDMU;
2307                                         res->info.src = IDMAP_MAP_SRC_NEW;
2308                                 }
2309                                 req->id1.idtype = IDMAP_GSID;
2310                                 break;
2311 
2312                         default:
2313                                 res->retcode = IDMAP_ERR_SID;
2314                                 break;
2315                         }
2316                         TRACE(req, res, "Found in AD");
2317                         if (res->retcode == IDMAP_SUCCESS &&
2318                             req->id1name != NULL &&
2319                             (req->id2name == NULL ||
2320                             res->id.idmap_id_u.uid == IDMAP_SENTINEL_PID) &&
2321                             NLDAP_MODE(res->id.idtype, state)) {
2322                                 req->direction |= _IDMAP_F_LOOKUP_NLDAP;
2323                                 state->nldap_nqueries++;
2324                         }
2325                 } else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
2326                         if (res->retcode != IDMAP_SUCCESS) {
2327                                 if ((!(IDMAP_FATAL_ERROR(res->retcode))) &&
2328                                     res->id.idmap_id_u.sid.prefix == NULL &&
2329                                     req->id2name == NULL) {
2330                                         /*
2331                                          * If AD lookup by unixname or pid
2332                                          * failed with non fatal error
2333                                          * then clear the error (ie set
2334                                          * res->retcode to success).
2335                                          * This allows the next pass to
2336                                          * process other mapping
2337                                          * mechanisms for this request.
2338                                          */
2339                                         if (res->retcode ==
2340                                             IDMAP_ERR_NOTFOUND) {
2341                                                 /* This is not an error */
2342                                                 res->retcode = IDMAP_SUCCESS;
2343                                                 TRACE(req, res,
2344                                                     "Not found in AD");
2345                                         } else {
2346                                                 TRACE(req, res,
2347                                                 "AD lookup error (ignored)");
2348                                                 res->retcode = IDMAP_SUCCESS;
2349                                         }
2350                                 } else {
2351                                         TRACE(req, res, "AD lookup error");
2352                                 }
2353                                 continue;
2354                         }
2355                         /* Evaluate result type */
2356                         switch (type) {
2357                         case IDMAP_USID:
2358                         case IDMAP_GSID:
2359                                 if (res->id.idtype == IDMAP_SID)
2360                                         res->id.idtype = type;
2361                                 break;
2362 
2363                         default:
2364                                 res->retcode = IDMAP_ERR_SID;
2365                                 break;
2366                         }
2367                         TRACE(req, res, "Found in AD");
2368                 }
2369         }
2370 
2371         return (retcode);
2372 }
2373 
2374 
2375 
2376 /*
2377  * Batch AD lookups
2378  */
2379 idmap_retcode
2380 ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch,
2381                 idmap_ids_res *result)
2382 {
2383         idmap_retcode   retcode;
2384         int             i, j;
2385         idmap_mapping   *req;
2386         idmap_id_res    *res;
2387         int             num_queries;
2388         int             num_processed;
2389 
2390         if (state->ad_nqueries == 0)
2391                 return (IDMAP_SUCCESS);
2392 
2393         for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
2394                 req = &batch->idmap_mapping_batch_val[i];
2395                 res = &result->ids.ids_val[i];
2396 
2397                 /* Skip if not marked for AD lookup or already in error. */
2398                 if (!(req->direction & _IDMAP_F_LOOKUP_AD) ||
2399                     res->retcode != IDMAP_SUCCESS)
2400                         continue;
2401 
2402                 /* Init status */
2403                 res->retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
2404         }
2405 
2406         RDLOCK_CONFIG();
2407         num_queries = state->ad_nqueries;
2408 
2409         if (_idmapdstate.num_gcs == 0 && _idmapdstate.num_dcs == 0) {
2410                 /* Case of no ADs */
2411                 retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY;
2412                 for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
2413                         req = &batch->idmap_mapping_batch_val[i];
2414                         res = &result->ids.ids_val[i];
2415                         if (!(req->direction & _IDMAP_F_LOOKUP_AD))
2416                                 continue;
2417                         req->direction &= ~(_IDMAP_F_LOOKUP_AD);
2418                         res->retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY;
2419                 }
2420                 goto out;
2421         }
2422 
2423         if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU) {
2424                 for (i = 0; i < _idmapdstate.num_dcs && num_queries > 0; i++) {
2425 
2426                         retcode = ad_lookup_batch_int(state, batch,
2427                             result, _idmapdstate.dcs[i],
2428                             i == 0 ? DOMAIN_IS_LOCAL|FOREST_IS_LOCAL : 0,
2429                             &num_processed);
2430                         num_queries -= num_processed;
2431 
2432                 }
2433         }
2434 
2435         for (i = 0; i < _idmapdstate.num_gcs && num_queries > 0; i++) {
2436 
2437                 retcode = ad_lookup_batch_int(state, batch, result,
2438                     _idmapdstate.gcs[i],
2439                     i == 0 ? FOREST_IS_LOCAL : 0,
2440                     &num_processed);
2441                 num_queries -= num_processed;
2442 
2443         }
2444 
2445         /*
2446          * There are no more ADs to try.  Return errors for any
2447          * remaining requests.
2448          */
2449         if (num_queries > 0) {
2450                 for (j = 0; j < batch->idmap_mapping_batch_len; j++) {
2451                         req = &batch->idmap_mapping_batch_val[j];
2452                         res = &result->ids.ids_val[j];
2453                         if (!(req->direction & _IDMAP_F_LOOKUP_AD))
2454                                 continue;
2455                         req->direction &= ~(_IDMAP_F_LOOKUP_AD);
2456                         res->retcode = IDMAP_ERR_DOMAIN_NOTFOUND;
2457                 }
2458         }
2459 
2460 out:
2461         UNLOCK_CONFIG();
2462 
2463         /* AD lookups done. Reset state->ad_nqueries and return */
2464         state->ad_nqueries = 0;
2465         return (retcode);
2466 }
2467 
2468 /*
2469  * Convention when processing win2unix requests:
2470  *
2471  * Windows identity:
2472  * req->id1name =
2473  *              winname if given otherwise winname found will be placed
2474  *              here.
2475  * req->id1domain =
2476  *              windomain if given otherwise windomain found will be
2477  *              placed here.
2478  * req->id1.idtype =
2479  *              Either IDMAP_SID/USID/GSID. If this is IDMAP_SID then it'll
2480  *              be set to IDMAP_USID/GSID depending upon whether the
2481  *              given SID is user or group respectively. The user/group-ness
2482  *              is determined either when looking up well-known SIDs table OR
2483  *              if the SID is found in namecache OR by ad_lookup_batch().
2484  * req->id1..sid.[prefix, rid] =
2485  *              SID if given otherwise SID found will be placed here.
2486  *
2487  * Unix identity:
2488  * req->id2name =
2489  *              unixname found will be placed here.
2490  * req->id2domain =
2491  *              NOT USED
2492  * res->id.idtype =
2493  *              Target type initialized from req->id2.idtype. If
2494  *              it is IDMAP_POSIXID then actual type (IDMAP_UID/GID) found
2495  *              will be placed here.
2496  * res->id..[uid or gid] =
2497  *              UID/GID found will be placed here.
2498  *
2499  * Others:
2500  * res->retcode =
2501  *              Return status for this request will be placed here.
2502  * res->direction =
2503  *              Direction found will be placed here. Direction
2504  *              meaning whether the resultant mapping is valid
2505  *              only from win2unix or bi-directional.
2506  * req->direction =
2507  *              INTERNAL USE. Used by idmapd to set various
2508  *              flags (_IDMAP_F_xxxx) to aid in processing
2509  *              of the request.
2510  * req->id2.idtype =
2511  *              INTERNAL USE. Initially this is the requested target
2512  *              type and is used to initialize res->id.idtype.
2513  *              ad_lookup_batch() uses this field temporarily to store
2514  *              sid_type obtained by the batched AD lookups and after
2515  *              use resets it to IDMAP_NONE to prevent xdr from
2516  *              mis-interpreting the contents of req->id2.
2517  * req->id2.idmap_id_u.uid =
2518  *              INTERNAL USE.  If the AD lookup finds IDMU data
2519  *              (uidNumber or gidNumber, depending on the type of
2520  *              the entry), it's left here.
2521  */
2522 
2523 /*
2524  * This function does the following:
2525  * 1. Lookup well-known SIDs table.
2526  * 2. Check if the given SID is a local-SID and if so extract UID/GID from it.
2527  * 3. Lookup cache.
2528  * 4. Check if the client does not want new mapping to be allocated
2529  *    in which case this pass is the final pass.
2530  * 5. Set AD lookup flag if it determines that the next stage needs
2531  *    to do AD lookup.
2532  */
2533 idmap_retcode
2534 sid2pid_first_pass(lookup_state_t *state, idmap_mapping *req,
2535                 idmap_id_res *res)
2536 {
2537         idmap_retcode   retcode;
2538         int             wksid;
2539 
2540         /* Initialize result */
2541         res->id.idtype = req->id2.idtype;
2542         res->id.idmap_id_u.uid = IDMAP_SENTINEL_PID;
2543         res->direction = IDMAP_DIRECTION_UNDEF;
2544         wksid = 0;
2545 
2546         if (EMPTY_STRING(req->id1.idmap_id_u.sid.prefix)) {
2547                 /* They have to give us *something* to work with! */
2548                 if (req->id1name == NULL) {
2549                         retcode = IDMAP_ERR_ARG;
2550                         goto out;
2551                 }
2552 
2553                 /* sanitize sidprefix */
2554                 free(req->id1.idmap_id_u.sid.prefix);
2555                 req->id1.idmap_id_u.sid.prefix = NULL;
2556 
2557                 /* Allow for a fully-qualified name in the "name" parameter */
2558                 if (req->id1domain == NULL) {
2559                         char *p;
2560                         p = strchr(req->id1name, '@');
2561                         if (p != NULL) {
2562                                 char *q;
2563                                 q = req->id1name;
2564                                 req->id1name = uu_strndup(q, p - req->id1name);
2565                                 req->id1domain = strdup(p+1);
2566                                 free(q);
2567                                 if (req->id1name == NULL ||
2568                                     req->id1domain == NULL) {
2569                                         retcode = IDMAP_ERR_MEMORY;
2570                                         goto out;
2571                                 }
2572                         }
2573                 }
2574         }
2575 
2576         /* Lookup well-known SIDs table */
2577         retcode = lookup_wksids_sid2pid(req, res, &wksid);
2578         if (retcode == IDMAP_SUCCESS) {
2579                 /* Found a well-known account with a hardwired mapping */
2580                 TRACE(req, res, "Hardwired mapping");
2581                 goto out;
2582         } else if (retcode != IDMAP_ERR_NOTFOUND) {
2583                 TRACE(req, res,
2584                     "Well-known account lookup failed, code %d", retcode);
2585                 goto out;
2586         }
2587 
2588         if (wksid) {
2589                 /* Found a well-known account, but no mapping */
2590                 TRACE(req, res, "Well-known account");
2591         } else {
2592                 TRACE(req, res, "Not a well-known account");
2593 
2594                 /* Check if this is a localsid */
2595                 retcode = lookup_localsid2pid(req, res);
2596                 if (retcode == IDMAP_SUCCESS) {
2597                         TRACE(req, res, "Local SID");
2598                         goto out;
2599                 } else if (retcode != IDMAP_ERR_NOTFOUND) {
2600                         TRACE(req, res,
2601                             "Local SID lookup error=%d", retcode);
2602                         goto out;
2603                 }
2604                 TRACE(req, res, "Not a local SID");
2605 
2606                 if (ALLOW_WK_OR_LOCAL_SIDS_ONLY(req)) {
2607                         retcode = IDMAP_ERR_NONE_GENERATED;
2608                         goto out;
2609                 }
2610         }
2611 
2612         /*
2613          * If this is a name-based request and we don't have a domain,
2614          * use the default domain.  Note that the well-known identity
2615          * cases will have supplied a SID prefix already, and that we
2616          * don't (yet?) support looking up a local user through a Windows
2617          * style name.
2618          */
2619         if (req->id1.idmap_id_u.sid.prefix == NULL &&
2620             req->id1name != NULL && req->id1domain == NULL) {
2621                 if (state->defdom == NULL) {
2622                         retcode = IDMAP_ERR_DOMAIN_NOTFOUND;
2623                         goto out;
2624                 }
2625                 req->id1domain = strdup(state->defdom);
2626                 if (req->id1domain == NULL) {
2627                         retcode = IDMAP_ERR_MEMORY;
2628                         goto out;
2629                 }
2630                 TRACE(req, res, "Added default domain");
2631         }
2632 
2633         /* Lookup cache */
2634         retcode = lookup_cache_sid2pid(state->cache, req, res);
2635         if (retcode == IDMAP_SUCCESS) {
2636                 TRACE(req, res, "Found in mapping cache");
2637                 goto out;
2638         } else if (retcode != IDMAP_ERR_NOTFOUND) {
2639                 TRACE(req, res, "Mapping cache lookup error=%d", retcode);
2640                 goto out;
2641         }
2642         TRACE(req, res, "Not found in mapping cache");
2643 
2644         if (DO_NOT_ALLOC_NEW_ID_MAPPING(req) || AVOID_NAMESERVICE(req)) {
2645                 retcode = IDMAP_ERR_NONE_GENERATED;
2646                 goto out;
2647         }
2648 
2649         /*
2650          * Failed to find non-expired entry in cache. Next step is
2651          * to determine if this request needs to be batched for AD lookup.
2652          *
2653          * At this point we have either sid or winname or both. If we don't
2654          * have both then lookup name_cache for the sid or winname
2655          * whichever is missing. If not found then this request will be
2656          * batched for AD lookup.
2657          */
2658         retcode = lookup_name_cache(state->cache, req, res);
2659         if (retcode == IDMAP_SUCCESS) {
2660                 if (res->id.idtype == IDMAP_POSIXID) {
2661                         if (req->id1.idtype == IDMAP_USID)
2662                                 res->id.idtype = IDMAP_UID;
2663                         else
2664                                 res->id.idtype = IDMAP_GID;
2665                 }
2666         } else if (retcode != IDMAP_ERR_NOTFOUND)
2667                 goto out;
2668 
2669         if (_idmapdstate.cfg->pgcfg.use_lsa &&
2670             _idmapdstate.cfg->pgcfg.domain_name != NULL) {
2671                 /*
2672                  * If we don't have both name and SID, try looking up the
2673                  * entry with LSA.
2674                  */
2675                 if (req->id1.idmap_id_u.sid.prefix != NULL &&
2676                     req->id1name == NULL) {
2677 
2678                         retcode = lookup_lsa_by_sid(
2679                             req->id1.idmap_id_u.sid.prefix,
2680                             req->id1.idmap_id_u.sid.rid,
2681                             &req->id1name, &req->id1domain, &req->id1.idtype);
2682                         if (retcode == IDMAP_SUCCESS) {
2683                                 TRACE(req, res, "Found with LSA");
2684                         } else if (retcode == IDMAP_ERR_NOTFOUND) {
2685                                 TRACE(req, res, "Not found with LSA");
2686                         } else {
2687                                 TRACE(req, res, "LSA error %d", retcode);
2688                                 goto out;
2689                         }
2690 
2691                 } else  if (req->id1name != NULL &&
2692                     req->id1.idmap_id_u.sid.prefix == NULL) {
2693                         char *canonname;
2694                         char *canondomain;
2695 
2696                         retcode = lookup_lsa_by_name(
2697                             req->id1name, req->id1domain,
2698                             &req->id1.idmap_id_u.sid.prefix,
2699                             &req->id1.idmap_id_u.sid.rid,
2700                             &canonname, &canondomain,
2701                             &req->id1.idtype);
2702                         if (retcode == IDMAP_SUCCESS) {
2703                                 free(req->id1name);
2704                                 req->id1name = canonname;
2705                                 free(req->id1domain);
2706                                 req->id1domain = canondomain;
2707                                 TRACE(req, res, "Found with LSA");
2708                         } else if (retcode == IDMAP_ERR_NOTFOUND) {
2709                                 TRACE(req, res, "Not found with LSA");
2710                         } else {
2711                                 TRACE(req, res, "LSA error %d", retcode);
2712                                 goto out;
2713                         }
2714                 }
2715         }
2716 
2717         /*
2718          * Set the flag to indicate that we are not done yet so that
2719          * subsequent passes considers this request for name-based
2720          * mapping and ephemeral mapping.
2721          */
2722         state->sid2pid_done = FALSE;
2723         req->direction |= _IDMAP_F_NOTDONE;
2724 
2725         /*
2726          * Even if we have both sid and winname, we still may need to batch
2727          * this request for AD lookup if we don't have unixname and
2728          * directory-based name mapping (AD or mixed) is enabled.
2729          * We avoid AD lookup for well-known SIDs because they don't have
2730          * regular AD objects.
2731          */
2732         if (retcode != IDMAP_SUCCESS ||
2733             (!wksid && req->id2name == NULL &&
2734             AD_OR_MIXED_MODE(res->id.idtype, state)) ||
2735             (!wksid && res->id.idmap_id_u.uid == IDMAP_SENTINEL_PID &&
2736             state->directory_based_mapping == DIRECTORY_MAPPING_IDMU)) {
2737                 retcode = IDMAP_SUCCESS;
2738                 req->direction |= _IDMAP_F_LOOKUP_AD;
2739                 state->ad_nqueries++;
2740         } else if (NLDAP_MODE(res->id.idtype, state)) {
2741                 req->direction |= _IDMAP_F_LOOKUP_NLDAP;
2742                 state->nldap_nqueries++;
2743         }
2744 
2745 
2746 out:
2747         res->retcode = idmap_stat4prot(retcode);
2748         /*
2749          * If we are done and there was an error then set fallback pid
2750          * in the result.
2751          */
2752         if (ARE_WE_DONE(req->direction) && res->retcode != IDMAP_SUCCESS)
2753                 res->id.idmap_id_u.uid = UID_NOBODY;
2754         return (retcode);
2755 }
2756 
2757 /*
2758  * Generate SID using the following convention
2759  *      <machine-sid-prefix>-<1000 + uid>
2760  *      <machine-sid-prefix>-<2^31 + gid>
2761  */
2762 static
2763 idmap_retcode
2764 generate_localsid(idmap_mapping *req, idmap_id_res *res, int is_user,
2765                 int fallback)
2766 {
2767         free(res->id.idmap_id_u.sid.prefix);
2768         res->id.idmap_id_u.sid.prefix = NULL;
2769 
2770         /*
2771          * Diagonal mapping for localSIDs not supported because of the
2772          * way we generate localSIDs.
2773          */
2774         if (is_user && res->id.idtype == IDMAP_GSID)
2775                 return (IDMAP_ERR_NOTGROUP);
2776         if (!is_user && res->id.idtype == IDMAP_USID)
2777                 return (IDMAP_ERR_NOTUSER);
2778 
2779         /* Skip 1000 UIDs */
2780         if (is_user &&
2781             req->id1.idmap_id_u.uid + LOCALRID_UID_MIN > LOCALRID_UID_MAX)
2782                 return (IDMAP_ERR_NOMAPPING);
2783 
2784         RDLOCK_CONFIG();
2785         /*
2786          * machine_sid is never NULL because if it is we won't be here.
2787          * No need to assert because strdup(NULL) will core anyways.
2788          */
2789         res->id.idmap_id_u.sid.prefix =
2790             strdup(_idmapdstate.cfg->pgcfg.machine_sid);
2791         if (res->id.idmap_id_u.sid.prefix == NULL) {
2792                 UNLOCK_CONFIG();
2793                 idmapdlog(LOG_ERR, "Out of memory");
2794                 return (IDMAP_ERR_MEMORY);
2795         }
2796         UNLOCK_CONFIG();
2797         res->id.idmap_id_u.sid.rid =
2798             (is_user) ? req->id1.idmap_id_u.uid + LOCALRID_UID_MIN :
2799             req->id1.idmap_id_u.gid + LOCALRID_GID_MIN;
2800         res->direction = IDMAP_DIRECTION_BI;
2801         if (res->id.idtype == IDMAP_SID)
2802                 res->id.idtype = is_user ? IDMAP_USID : IDMAP_GSID;
2803 
2804         if (!fallback) {
2805                 res->info.how.map_type = IDMAP_MAP_TYPE_LOCAL_SID;
2806                 res->info.src = IDMAP_MAP_SRC_ALGORITHMIC;
2807         }
2808 
2809         /*
2810          * Don't update name_cache because local sids don't have
2811          * valid windows names.
2812          */
2813         req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
2814         return (IDMAP_SUCCESS);
2815 }
2816 
2817 static
2818 idmap_retcode
2819 lookup_localsid2pid(idmap_mapping *req, idmap_id_res *res)
2820 {
2821         char            *sidprefix;
2822         uint32_t        rid;
2823         int             s;
2824 
2825         /*
2826          * If the sidprefix == localsid then UID = last RID - 1000 or
2827          * GID = last RID - 2^31.
2828          */
2829         if ((sidprefix = req->id1.idmap_id_u.sid.prefix) == NULL)
2830                 /* This means we are looking up by winname */
2831                 return (IDMAP_ERR_NOTFOUND);
2832         rid = req->id1.idmap_id_u.sid.rid;
2833 
2834         RDLOCK_CONFIG();
2835         s = (_idmapdstate.cfg->pgcfg.machine_sid) ?
2836             strcasecmp(sidprefix, _idmapdstate.cfg->pgcfg.machine_sid) : 1;
2837         UNLOCK_CONFIG();
2838 
2839         /*
2840          * If the given sidprefix does not match machine_sid then this is
2841          * not a local SID.
2842          */
2843         if (s != 0)
2844                 return (IDMAP_ERR_NOTFOUND);
2845 
2846         switch (res->id.idtype) {
2847         case IDMAP_UID:
2848                 if (rid < LOCALRID_UID_MIN || rid > LOCALRID_UID_MAX)
2849                         return (IDMAP_ERR_ARG);
2850                 res->id.idmap_id_u.uid = rid - LOCALRID_UID_MIN;
2851                 break;
2852         case IDMAP_GID:
2853                 if (rid < LOCALRID_GID_MIN)
2854                         return (IDMAP_ERR_ARG);
2855                 res->id.idmap_id_u.gid = rid - LOCALRID_GID_MIN;
2856                 break;
2857         case IDMAP_POSIXID:
2858                 if (rid >= LOCALRID_GID_MIN) {
2859                         res->id.idmap_id_u.gid = rid - LOCALRID_GID_MIN;
2860                         res->id.idtype = IDMAP_GID;
2861                 } else if (rid >= LOCALRID_UID_MIN) {
2862                         res->id.idmap_id_u.uid = rid - LOCALRID_UID_MIN;
2863                         res->id.idtype = IDMAP_UID;
2864                 } else {
2865                         return (IDMAP_ERR_ARG);
2866                 }
2867                 break;
2868         default:
2869                 return (IDMAP_ERR_NOTSUPPORTED);
2870         }
2871         res->info.how.map_type = IDMAP_MAP_TYPE_LOCAL_SID;
2872         res->info.src = IDMAP_MAP_SRC_ALGORITHMIC;
2873         return (IDMAP_SUCCESS);
2874 }
2875 
2876 /*
2877  * Name service lookup by unixname to get pid
2878  */
2879 static
2880 idmap_retcode
2881 ns_lookup_byname(const char *name, const char *lower_name, idmap_id *id)
2882 {
2883         struct passwd   pwd, *pwdp;
2884         struct group    grp, *grpp;
2885         char            *buf;
2886         static size_t   pwdbufsiz = 0;
2887         static size_t   grpbufsiz = 0;
2888 
2889         switch (id->idtype) {
2890         case IDMAP_UID:
2891                 if (pwdbufsiz == 0)
2892                         pwdbufsiz = sysconf(_SC_GETPW_R_SIZE_MAX);
2893                 buf = alloca(pwdbufsiz);
2894                 pwdp = getpwnam_r(name, &pwd, buf, pwdbufsiz);
2895                 if (pwdp == NULL && errno == 0 && lower_name != NULL &&
2896                     name != lower_name && strcmp(name, lower_name) != 0)
2897                         pwdp = getpwnam_r(lower_name, &pwd, buf, pwdbufsiz);
2898                 if (pwdp == NULL) {
2899                         if (errno == 0)
2900                                 return (IDMAP_ERR_NOTFOUND);
2901                         else
2902                                 return (IDMAP_ERR_INTERNAL);
2903                 }
2904                 id->idmap_id_u.uid = pwd.pw_uid;
2905                 break;
2906         case IDMAP_GID:
2907                 if (grpbufsiz == 0)
2908                         grpbufsiz = sysconf(_SC_GETGR_R_SIZE_MAX);
2909                 buf = alloca(grpbufsiz);
2910                 grpp = getgrnam_r(name, &grp, buf, grpbufsiz);
2911                 if (grpp == NULL && errno == 0 && lower_name != NULL &&
2912                     name != lower_name && strcmp(name, lower_name) != 0)
2913                         grpp = getgrnam_r(lower_name, &grp, buf, grpbufsiz);
2914                 if (grpp == NULL) {
2915                         if (errno == 0)
2916                                 return (IDMAP_ERR_NOTFOUND);
2917                         else
2918                                 return (IDMAP_ERR_INTERNAL);
2919                 }
2920                 id->idmap_id_u.gid = grp.gr_gid;
2921                 break;
2922         default:
2923                 return (IDMAP_ERR_ARG);
2924         }
2925         return (IDMAP_SUCCESS);
2926 }
2927 
2928 
2929 /*
2930  * Name service lookup by pid to get unixname
2931  */
2932 static
2933 idmap_retcode
2934 ns_lookup_bypid(uid_t pid, int is_user, char **unixname)
2935 {
2936         struct passwd   pwd;
2937         struct group    grp;
2938         char            *buf;
2939         static size_t   pwdbufsiz = 0;
2940         static size_t   grpbufsiz = 0;
2941 
2942         if (is_user) {
2943                 if (pwdbufsiz == 0)
2944                         pwdbufsiz = sysconf(_SC_GETPW_R_SIZE_MAX);
2945                 buf = alloca(pwdbufsiz);
2946                 errno = 0;
2947                 if (getpwuid_r(pid, &pwd, buf, pwdbufsiz) == NULL) {
2948                         if (errno == 0)
2949                                 return (IDMAP_ERR_NOTFOUND);
2950                         else
2951                                 return (IDMAP_ERR_INTERNAL);
2952                 }
2953                 *unixname = strdup(pwd.pw_name);
2954         } else {
2955                 if (grpbufsiz == 0)
2956                         grpbufsiz = sysconf(_SC_GETGR_R_SIZE_MAX);
2957                 buf = alloca(grpbufsiz);
2958                 errno = 0;
2959                 if (getgrgid_r(pid, &grp, buf, grpbufsiz) == NULL) {
2960                         if (errno == 0)
2961                                 return (IDMAP_ERR_NOTFOUND);
2962                         else
2963                                 return (IDMAP_ERR_INTERNAL);
2964                 }
2965                 *unixname = strdup(grp.gr_name);
2966         }
2967         if (*unixname == NULL)
2968                 return (IDMAP_ERR_MEMORY);
2969         return (IDMAP_SUCCESS);
2970 }
2971 
2972 /*
2973  * Name-based mapping
2974  *
2975  * Case 1: If no rule matches do ephemeral
2976  *
2977  * Case 2: If rule matches and unixname is "" then return no mapping.
2978  *
2979  * Case 3: If rule matches and unixname is specified then lookup name
2980  *  service using the unixname. If unixname not found then return no mapping.
2981  *
2982  * Case 4: If rule matches and unixname is * then lookup name service
2983  *  using winname as the unixname. If unixname not found then process
2984  *  other rules using the lookup order. If no other rule matches then do
2985  *  ephemeral. Otherwise, based on the matched rule do Case 2 or 3 or 4.
2986  *  This allows us to specify a fallback unixname per _domain_ or no mapping
2987  *  instead of the default behaviour of doing ephemeral mapping.
2988  *
2989  * Example 1:
2990  * *@sfbay == *
2991  * If looking up windows users foo@sfbay and foo does not exists in
2992  * the name service then foo@sfbay will be mapped to an ephemeral id.
2993  *
2994  * Example 2:
2995  * *@sfbay == *
2996  * *@sfbay => guest
2997  * If looking up windows users foo@sfbay and foo does not exists in
2998  * the name service then foo@sfbay will be mapped to guest.
2999  *
3000  * Example 3:
3001  * *@sfbay == *
3002  * *@sfbay => ""
3003  * If looking up windows users foo@sfbay and foo does not exists in
3004  * the name service then we will return no mapping for foo@sfbay.
3005  *
3006  */
3007 static
3008 idmap_retcode
3009 name_based_mapping_sid2pid(lookup_state_t *state,
3010                 idmap_mapping *req, idmap_id_res *res)
3011 {
3012         const char      *unixname, *windomain;
3013         char            *sql = NULL, *errmsg = NULL, *lower_winname = NULL;
3014         idmap_retcode   retcode;
3015         char            *end, *lower_unixname, *winname;
3016         const char      **values;
3017         sqlite_vm       *vm = NULL;
3018         int             ncol, r, is_user, is_wuser;
3019         idmap_namerule  *rule = &res->info.how.idmap_how_u.rule;
3020         int             direction;
3021         const char      *me = "name_based_mapping_sid2pid";
3022 
3023         assert(req->id1name != NULL); /* We have winname */
3024         assert(req->id2name == NULL); /* We don't have unixname */
3025 
3026         winname = req->id1name;
3027         windomain = req->id1domain;
3028 
3029         switch (req->id1.idtype) {
3030         case IDMAP_USID:
3031                 is_wuser = 1;
3032                 break;
3033         case IDMAP_GSID:
3034                 is_wuser = 0;
3035                 break;
3036         default:
3037                 idmapdlog(LOG_ERR, "%s: Unable to determine if the "
3038                     "given Windows id is user or group.", me);
3039                 return (IDMAP_ERR_INTERNAL);
3040         }
3041 
3042         switch (res->id.idtype) {
3043         case IDMAP_UID:
3044                 is_user = 1;
3045                 break;
3046         case IDMAP_GID:
3047                 is_user = 0;
3048                 break;
3049         case IDMAP_POSIXID:
3050                 is_user = is_wuser;
3051                 res->id.idtype = is_user ? IDMAP_UID : IDMAP_GID;
3052                 break;
3053         }
3054 
3055         if (windomain == NULL)
3056                 windomain = "";
3057 
3058         if ((lower_winname = tolower_u8(winname)) == NULL)
3059                 lower_winname = winname;    /* hope for the best */
3060         sql = sqlite_mprintf(
3061             "SELECT unixname, u2w_order, winname_display, windomain, is_nt4 "
3062             "FROM namerules WHERE "
3063             "w2u_order > 0 AND is_user = %d AND is_wuser = %d AND "
3064             "(winname = %Q OR winname = '*') AND "
3065             "(windomain = %Q OR windomain = '*') "
3066             "ORDER BY w2u_order ASC;",
3067             is_user, is_wuser, lower_winname, windomain);
3068         if (sql == NULL) {
3069                 idmapdlog(LOG_ERR, "Out of memory");
3070                 retcode = IDMAP_ERR_MEMORY;
3071                 goto out;
3072         }
3073 
3074         if (sqlite_compile(state->db, sql, NULL, &vm, &errmsg) != SQLITE_OK) {
3075                 retcode = IDMAP_ERR_INTERNAL;
3076                 idmapdlog(LOG_ERR, "%s: database error (%s)", me,
3077                     CHECK_NULL(errmsg));
3078                 sqlite_freemem(errmsg);
3079                 goto out;
3080         }
3081 
3082         for (;;) {
3083                 r = sqlite_step(vm, &ncol, &values, NULL);
3084                 assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
3085 
3086                 if (r == SQLITE_ROW) {
3087                         if (ncol < 5) {
3088                                 retcode = IDMAP_ERR_INTERNAL;
3089                                 goto out;
3090                         }
3091 
3092                         TRACE(req, res, "Matching rule: %s@%s -> %s",
3093                             values[2] == NULL ? "(null)" : values[2],
3094                             values[3] == NULL ? "(null)" : values[3],
3095                             values[0] == NULL ? "(null)" : values[0]);
3096 
3097                         if (values[0] == NULL) {
3098                                 retcode = IDMAP_ERR_INTERNAL;
3099                                 goto out;
3100                         }
3101 
3102                         if (values[1] != NULL)
3103                                 direction =
3104                                     (strtol(values[1], &end, 10) == 0)?
3105                                     IDMAP_DIRECTION_W2U:IDMAP_DIRECTION_BI;
3106                         else
3107                                 direction = IDMAP_DIRECTION_W2U;
3108 
3109                         if (EMPTY_NAME(values[0])) {
3110                                 TRACE(req, res, "Mapping inhibited");
3111                                 idmap_namerule_set(rule, values[3], values[2],
3112                                     values[0], is_user, is_wuser,
3113                                     strtol(values[4], &end, 10),
3114                                     direction);
3115                                 retcode = IDMAP_ERR_NOMAPPING;
3116                                 goto out;
3117                         }
3118 
3119                         if (values[0][0] == '*') {
3120                                 unixname = winname;
3121                                 lower_unixname = lower_winname;
3122                         } else {
3123                                 unixname = values[0];
3124                                 lower_unixname = NULL;
3125                         }
3126 
3127                         retcode = ns_lookup_byname(unixname, lower_unixname,
3128                             &res->id);
3129                         if (retcode == IDMAP_SUCCESS) {
3130                                 break;
3131                         } else if (retcode == IDMAP_ERR_NOTFOUND) {
3132                                 if (values[0][0] == '*') {
3133                                         TRACE(req, res,
3134                                             "%s not found, continuing",
3135                                             unixname);
3136                                         /* Case 4 */
3137                                         continue;
3138                                 } else {
3139                                         TRACE(req, res,
3140                                             "%s not found, error", unixname);
3141                                         /* Case 3 */
3142                                         idmap_namerule_set(rule, values[3],
3143                                             values[2], values[0], is_user,
3144                                             is_wuser,
3145                                             strtol(values[4], &end, 10),
3146                                             direction);
3147                                         retcode = IDMAP_ERR_NOMAPPING;
3148                                 }
3149                         } else {
3150                                 TRACE(req, res, "Looking up %s error=%d",
3151                                     unixname, retcode);
3152                         }
3153                         goto out;
3154                 } else if (r == SQLITE_DONE) {
3155                         TRACE(req, res, "No matching rule");
3156                         retcode = IDMAP_ERR_NOTFOUND;
3157                         goto out;
3158                 } else {
3159                         (void) sqlite_finalize(vm, &errmsg);
3160                         vm = NULL;
3161                         idmapdlog(LOG_ERR, "%s: database error (%s)", me,
3162                             CHECK_NULL(errmsg));
3163                         sqlite_freemem(errmsg);
3164                         retcode = IDMAP_ERR_INTERNAL;
3165                         goto out;
3166                 }
3167         }
3168 
3169         /* Found */
3170 
3171         if (values[1] != NULL)
3172                 res->direction =
3173                     (strtol(values[1], &end, 10) == 0)?
3174                     IDMAP_DIRECTION_W2U:IDMAP_DIRECTION_BI;
3175         else
3176                 res->direction = IDMAP_DIRECTION_W2U;
3177 
3178         req->id2name = strdup(unixname);
3179         if (req->id2name == NULL) {
3180                 retcode = IDMAP_ERR_MEMORY;
3181                 goto out;
3182         }
3183         TRACE(req, res, "UNIX name found");
3184 
3185         idmap_namerule_set(rule, values[3], values[2],
3186             values[0], is_user, is_wuser, strtol(values[4], &end, 10),
3187             res->direction);
3188 
3189 out:
3190         if (retcode != IDMAP_SUCCESS &&
3191             retcode != IDMAP_ERR_NOTFOUND &&
3192             retcode != IDMAP_ERR_NOMAPPING) {
3193                 TRACE(req, res, "Rule processing error, code=%d", retcode);
3194         }
3195 
3196         if (sql != NULL)
3197                 sqlite_freemem(sql);
3198 
3199         if (retcode != IDMAP_ERR_NOTFOUND) {
3200                 res->info.how.map_type = IDMAP_MAP_TYPE_RULE_BASED;
3201                 res->info.src = IDMAP_MAP_SRC_NEW;
3202         }
3203 
3204         if (lower_winname != NULL && lower_winname != winname)
3205                 free(lower_winname);
3206         if (vm != NULL)
3207                 (void) sqlite_finalize(vm, NULL);
3208         return (retcode);
3209 }
3210 
3211 static
3212 int
3213 get_next_eph_uid(uid_t *next_uid)
3214 {
3215         uid_t uid;
3216         gid_t gid;
3217         int err;
3218 
3219         *next_uid = (uid_t)-1;
3220         uid = _idmapdstate.next_uid++;
3221         if (uid >= _idmapdstate.limit_uid) {
3222                 if ((err = allocids(0, 8192, &uid, 0, &gid)) != 0)
3223                         return (err);
3224 
3225                 _idmapdstate.limit_uid = uid + 8192;
3226                 _idmapdstate.next_uid = uid;
3227         }
3228         *next_uid = uid;
3229 
3230         return (0);
3231 }
3232 
3233 static
3234 int
3235 get_next_eph_gid(gid_t *next_gid)
3236 {
3237         uid_t uid;
3238         gid_t gid;
3239         int err;
3240 
3241         *next_gid = (uid_t)-1;
3242         gid = _idmapdstate.next_gid++;
3243         if (gid >= _idmapdstate.limit_gid) {
3244                 if ((err = allocids(0, 0, &uid, 8192, &gid)) != 0)
3245                         return (err);
3246 
3247                 _idmapdstate.limit_gid = gid + 8192;
3248                 _idmapdstate.next_gid = gid;
3249         }
3250         *next_gid = gid;
3251 
3252         return (0);
3253 }
3254 
3255 static
3256 int
3257 gethash(const char *str, uint32_t num, uint_t htsize)
3258 {
3259         uint_t  hval, i, len;
3260 
3261         if (str == NULL)
3262                 return (0);
3263         for (len = strlen(str), hval = 0, i = 0; i < len; i++) {
3264                 hval += str[i];
3265                 hval += (hval << 10);
3266                 hval ^= (hval >> 6);
3267         }
3268         for (str = (const char *)&num, i = 0; i < sizeof (num); i++) {
3269                 hval += str[i];
3270                 hval += (hval << 10);
3271                 hval ^= (hval >> 6);
3272         }
3273         hval += (hval << 3);
3274         hval ^= (hval >> 11);
3275         hval += (hval << 15);
3276         return (hval % htsize);
3277 }
3278 
3279 static
3280 int
3281 get_from_sid_history(lookup_state_t *state, const char *prefix, uint32_t rid,
3282                 uid_t *pid)
3283 {
3284         uint_t          next, key;
3285         uint_t          htsize = state->sid_history_size;
3286         idmap_sid       *sid;
3287 
3288         next = gethash(prefix, rid, htsize);
3289         while (next != htsize) {
3290                 key = state->sid_history[next].key;
3291                 if (key == htsize)
3292                         return (0);
3293                 sid = &state->batch->idmap_mapping_batch_val[key].id1.
3294                     idmap_id_u.sid;
3295                 if (sid->rid == rid && strcmp(sid->prefix, prefix) == 0) {
3296                         *pid = state->result->ids.ids_val[key].id.
3297                             idmap_id_u.uid;
3298                         return (1);
3299                 }
3300                 next = state->sid_history[next].next;
3301         }
3302         return (0);
3303 }
3304 
3305 static
3306 void
3307 add_to_sid_history(lookup_state_t *state, const char *prefix, uint32_t rid)
3308 {
3309         uint_t          hash, next;
3310         uint_t          htsize = state->sid_history_size;
3311 
3312         hash = next = gethash(prefix, rid, htsize);
3313         while (state->sid_history[next].key != htsize) {
3314                 next++;
3315                 next %= htsize;
3316         }
3317         state->sid_history[next].key = state->curpos;
3318         if (hash == next)
3319                 return;
3320         state->sid_history[next].next = state->sid_history[hash].next;
3321         state->sid_history[hash].next = next;
3322 }
3323 
3324 void
3325 cleanup_lookup_state(lookup_state_t *state)
3326 {
3327         free(state->sid_history);
3328         free(state->ad_unixuser_attr);
3329         free(state->ad_unixgroup_attr);
3330         free(state->nldap_winname_attr);
3331         free(state->defdom);
3332 }
3333 
3334 /* ARGSUSED */
3335 static
3336 idmap_retcode
3337 dynamic_ephemeral_mapping(lookup_state_t *state,
3338                 idmap_mapping *req, idmap_id_res *res)
3339 {
3340 
3341         uid_t           next_pid;
3342 
3343         res->direction = IDMAP_DIRECTION_BI;
3344 
3345         if (IDMAP_ID_IS_EPHEMERAL(res->id.idmap_id_u.uid)) {
3346                 res->info.how.map_type = IDMAP_MAP_TYPE_EPHEMERAL;
3347                 res->info.src = IDMAP_MAP_SRC_CACHE;
3348                 return (IDMAP_SUCCESS);
3349         }
3350 
3351         if (state->sid_history != NULL &&
3352             get_from_sid_history(state, req->id1.idmap_id_u.sid.prefix,
3353             req->id1.idmap_id_u.sid.rid, &next_pid)) {
3354                 res->id.idmap_id_u.uid = next_pid;
3355                 res->info.how.map_type = IDMAP_MAP_TYPE_EPHEMERAL;
3356                 res->info.src = IDMAP_MAP_SRC_NEW;
3357                 return (IDMAP_SUCCESS);
3358         }
3359 
3360         if (res->id.idtype == IDMAP_UID) {
3361                 if (get_next_eph_uid(&next_pid) != 0)
3362                         return (IDMAP_ERR_INTERNAL);
3363                 res->id.idmap_id_u.uid = next_pid;
3364         } else {
3365                 if (get_next_eph_gid(&next_pid) != 0)
3366                         return (IDMAP_ERR_INTERNAL);
3367                 res->id.idmap_id_u.gid = next_pid;
3368         }
3369 
3370         res->info.how.map_type = IDMAP_MAP_TYPE_EPHEMERAL;
3371         res->info.src = IDMAP_MAP_SRC_NEW;
3372         if (state->sid_history != NULL)
3373                 add_to_sid_history(state, req->id1.idmap_id_u.sid.prefix,
3374                     req->id1.idmap_id_u.sid.rid);
3375 
3376         return (IDMAP_SUCCESS);
3377 }
3378 
3379 idmap_retcode
3380 sid2pid_second_pass(lookup_state_t *state,
3381                 idmap_mapping *req, idmap_id_res *res)
3382 {
3383         idmap_retcode   retcode;
3384         idmap_retcode   retcode2;
3385 
3386         /* Check if second pass is needed */
3387         if (ARE_WE_DONE(req->direction))
3388                 return (res->retcode);
3389 
3390         /* Get status from previous pass */
3391         retcode = res->retcode;
3392         if (retcode != IDMAP_SUCCESS && state->eph_map_unres_sids &&
3393             !EMPTY_STRING(req->id1.idmap_id_u.sid.prefix) &&
3394             EMPTY_STRING(req->id1name)) {
3395                 /*
3396                  * We are asked to map an unresolvable SID to a UID or
3397                  * GID, but, which?  We'll treat all unresolvable SIDs
3398                  * as users unless the caller specified which of a UID
3399                  * or GID they want.
3400                  */
3401                 if (req->id1.idtype == IDMAP_SID)
3402                         req->id1.idtype = IDMAP_USID;
3403                 if (res->id.idtype == IDMAP_POSIXID) {
3404                         res->id.idtype = IDMAP_UID;
3405                         TRACE(req, res, "Assume unresolvable SID is user");
3406                 } else if (res->id.idtype == IDMAP_UID) {
3407                         TRACE(req, res, "Must map unresolvable SID to user");
3408                 } else if (res->id.idtype == IDMAP_GID) {
3409                         TRACE(req, res, "Must map unresolvable SID to group");
3410                 }
3411                 goto do_eph;
3412         }
3413         if (retcode != IDMAP_SUCCESS)
3414                 goto out;
3415 
3416         /*
3417          * There are two ways we might get here with a Posix ID:
3418          * - It could be from an expired ephemeral cache entry.
3419          * - It could be from IDMU.
3420          * If it's from IDMU, we need to look up the name, for name-based
3421          * requests and the cache.
3422          */
3423         if (!IDMAP_ID_IS_EPHEMERAL(res->id.idmap_id_u.uid) &&
3424             res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
3425                 if (req->id2name == NULL) {
3426                         /*
3427                          * If the lookup fails, go ahead anyway.
3428                          * The general UNIX rule is that it's OK to
3429                          * have a UID or GID that isn't in the
3430                          * name service.
3431                          */
3432                         retcode2 = ns_lookup_bypid(res->id.idmap_id_u.uid,
3433                             res->id.idtype == IDMAP_UID, &req->id2name);
3434                         if (IDMAP_ERROR(retcode2)) {
3435                                 TRACE(req, res,
3436                                     "Getting UNIX name, error=%d (ignored)",
3437                                     retcode2);
3438                         } else {
3439                                 TRACE(req, res, "Found UNIX name");
3440                         }
3441                 }
3442                 goto out;
3443         }
3444 
3445         /*
3446          * If directory-based name mapping is enabled then the unixname
3447          * may already have been retrieved from the AD object (AD-mode or
3448          * mixed-mode) or from native LDAP object (nldap-mode) -- done.
3449          */
3450         if (req->id2name != NULL) {
3451                 assert(res->id.idtype != IDMAP_POSIXID);
3452                 if (AD_MODE(res->id.idtype, state))
3453                         res->direction = IDMAP_DIRECTION_BI;
3454                 else if (NLDAP_MODE(res->id.idtype, state))
3455                         res->direction = IDMAP_DIRECTION_BI;
3456                 else if (MIXED_MODE(res->id.idtype, state))
3457                         res->direction = IDMAP_DIRECTION_W2U;
3458 
3459                 /*
3460                  * Special case: (1) If the ad_unixuser_attr and
3461                  * ad_unixgroup_attr uses the same attribute
3462                  * name and (2) if this is a diagonal mapping
3463                  * request and (3) the unixname has been retrieved
3464                  * from the AD object -- then we ignore it and fallback
3465                  * to name-based mapping rules and ephemeral mapping
3466                  *
3467                  * Example:
3468                  *  Properties:
3469                  *    config/ad_unixuser_attr = "unixname"
3470                  *    config/ad_unixgroup_attr = "unixname"
3471                  *  AD user object:
3472                  *    dn: cn=bob ...
3473                  *    objectclass: user
3474                  *    sam: bob
3475                  *    unixname: bob1234
3476                  *  AD group object:
3477                  *    dn: cn=winadmins ...
3478                  *    objectclass: group
3479                  *    sam: winadmins
3480                  *    unixname: unixadmins
3481                  *
3482                  *  In this example whether "unixname" refers to a unixuser
3483                  *  or unixgroup depends upon the AD object.
3484                  *
3485                  * $idmap show -c winname:bob gid
3486                  *    AD lookup by "samAccountName=bob" for
3487                  *    "ad_unixgroup_attr (i.e unixname)" for directory-based
3488                  *    mapping would get "bob1234" which is not what we want.
3489                  *    Now why not getgrnam_r("bob1234") and use it if it
3490                  *    is indeed a unixgroup? That's because Unix can have
3491                  *    users and groups with the same name and we clearly
3492                  *    don't know the intention of the admin here.
3493                  *    Therefore we ignore this and fallback to name-based
3494                  *    mapping rules or ephemeral mapping.
3495                  */
3496                 if ((AD_MODE(res->id.idtype, state) ||
3497                     MIXED_MODE(res->id.idtype, state)) &&
3498                     state->ad_unixuser_attr != NULL &&
3499                     state->ad_unixgroup_attr != NULL &&
3500                     strcasecmp(state->ad_unixuser_attr,
3501                     state->ad_unixgroup_attr) == 0 &&
3502                     ((req->id1.idtype == IDMAP_USID &&
3503                     res->id.idtype == IDMAP_GID) ||
3504                     (req->id1.idtype == IDMAP_GSID &&
3505                     res->id.idtype == IDMAP_UID))) {
3506                         TRACE(req, res, "Ignoring UNIX name found in AD");
3507                         free(req->id2name);
3508                         req->id2name = NULL;
3509                         res->id.idmap_id_u.uid = IDMAP_SENTINEL_PID;
3510                         /* fallback */
3511                 } else {
3512                         if (res->id.idmap_id_u.uid == IDMAP_SENTINEL_PID) {
3513                                 retcode = ns_lookup_byname(req->id2name,
3514                                     NULL, &res->id);
3515                                 if (retcode != IDMAP_SUCCESS) {
3516                                         /*
3517                                          * If ns_lookup_byname() fails that
3518                                          * means the unixname (req->id2name),
3519                                          * which was obtained from the AD
3520                                          * object by directory-based mapping,
3521                                          * is not a valid Unix user/group and
3522                                          * therefore we return the error to the
3523                                          * client instead of doing rule-based
3524                                          * mapping or ephemeral mapping. This
3525                                          * way the client can detect the issue.
3526                                          */
3527                                         TRACE(req, res,
3528                                             "UNIX lookup error=%d", retcode);
3529                                         goto out;
3530                                 }
3531                                 TRACE(req, res, "UNIX lookup");
3532                         }
3533                         goto out;
3534                 }
3535         }
3536 
3537         /* Free any mapping info from Directory based mapping */
3538         if (res->info.how.map_type != IDMAP_MAP_TYPE_UNKNOWN)
3539                 idmap_how_clear(&res->info.how);
3540 
3541         /*
3542          * If we don't have unixname then evaluate local name-based
3543          * mapping rules.
3544          */
3545         retcode = name_based_mapping_sid2pid(state, req, res);
3546         if (retcode == IDMAP_SUCCESS) {
3547                 TRACE(req, res, "Rule-based mapping");
3548                 goto out;
3549         } else if (retcode != IDMAP_ERR_NOTFOUND) {
3550                 TRACE(req, res, "Rule-based mapping error=%d", retcode);
3551                 goto out;
3552         }
3553 
3554 do_eph:
3555         /* If not found, do ephemeral mapping */
3556         retcode = dynamic_ephemeral_mapping(state, req, res);
3557         if (retcode == IDMAP_SUCCESS) {
3558                 TRACE(req, res, "Ephemeral mapping");
3559                 goto out;
3560         } else if (retcode != IDMAP_ERR_NOTFOUND) {
3561                 TRACE(req, res, "Ephemeral mapping error=%d", retcode);
3562                 goto out;
3563         }
3564 
3565 out:
3566         res->retcode = idmap_stat4prot(retcode);
3567         if (res->retcode != IDMAP_SUCCESS) {
3568                 req->direction = _IDMAP_F_DONE;
3569                 res->id.idmap_id_u.uid = UID_NOBODY;
3570         }
3571         if (!ARE_WE_DONE(req->direction))
3572                 state->sid2pid_done = FALSE;
3573         return (retcode);
3574 }
3575 
3576 idmap_retcode
3577 update_cache_pid2sid(lookup_state_t *state,
3578                 idmap_mapping *req, idmap_id_res *res)
3579 {
3580         char            *sql = NULL;
3581         idmap_retcode   retcode;
3582         idmap_retcode   retcode2;
3583         char            *map_dn = NULL;
3584         char            *map_attr = NULL;
3585         char            *map_value = NULL;
3586         char            *map_windomain = NULL;
3587         char            *map_winname = NULL;
3588         char            *map_unixname = NULL;
3589         int             map_is_nt4 = FALSE;
3590 
3591         /* Check if we need to cache anything */
3592         if (ARE_WE_DONE(req->direction))
3593                 return (IDMAP_SUCCESS);
3594 
3595         /* We don't cache negative entries */
3596         if (res->retcode != IDMAP_SUCCESS)
3597                 return (IDMAP_SUCCESS);
3598 
3599         assert(res->direction != IDMAP_DIRECTION_UNDEF);
3600         assert(req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID);
3601         assert(res->id.idtype != IDMAP_SID);
3602 
3603         /*
3604          * If we've gotten to this point and we *still* don't know the
3605          * unixname, well, we'd like to have it now for the cache.
3606          *
3607          * If we truly always need it for the cache, we should probably
3608          * look it up once at the beginning, rather than "at need" in
3609          * several places as is now done.  However, it's not really clear
3610          * that we *do* need it in the cache; there's a decent argument
3611          * that the cache should contain only SIDs and PIDs, so we'll
3612          * leave our options open by doing it "at need" here too.
3613          *
3614          * If we can't find it... c'est la vie.
3615          */
3616         if (req->id1name == NULL) {
3617                 retcode2 = ns_lookup_bypid(req->id1.idmap_id_u.uid,
3618                     req->id1.idtype == IDMAP_UID, &req->id1name);
3619                 if (retcode2 == IDMAP_SUCCESS)
3620                         TRACE(req, res, "Found UNIX name");
3621                 else
3622                         TRACE(req, res, "Getting UNIX name error=%d", retcode2);
3623         }
3624 
3625         assert(res->info.how.map_type != IDMAP_MAP_TYPE_UNKNOWN);
3626         switch (res->info.how.map_type) {
3627         case IDMAP_MAP_TYPE_DS_AD:
3628                 map_dn = res->info.how.idmap_how_u.ad.dn;
3629                 map_attr = res->info.how.idmap_how_u.ad.attr;
3630                 map_value = res->info.how.idmap_how_u.ad.value;
3631                 break;
3632 
3633         case IDMAP_MAP_TYPE_DS_NLDAP:
3634                 map_dn = res->info.how.idmap_how_u.nldap.dn;
3635                 map_attr = res->info.how.idmap_how_u.nldap.attr;
3636                 map_value = res->info.how.idmap_how_u.nldap.value;
3637                 break;
3638 
3639         case IDMAP_MAP_TYPE_RULE_BASED:
3640                 map_windomain = res->info.how.idmap_how_u.rule.windomain;
3641                 map_winname = res->info.how.idmap_how_u.rule.winname;
3642                 map_unixname = res->info.how.idmap_how_u.rule.unixname;
3643                 map_is_nt4 = res->info.how.idmap_how_u.rule.is_nt4;
3644                 break;
3645 
3646         case IDMAP_MAP_TYPE_EPHEMERAL:
3647                 break;
3648 
3649         case IDMAP_MAP_TYPE_LOCAL_SID:
3650                 break;
3651 
3652         case IDMAP_MAP_TYPE_IDMU:
3653                 map_dn = res->info.how.idmap_how_u.idmu.dn;
3654                 map_attr = res->info.how.idmap_how_u.idmu.attr;
3655                 map_value = res->info.how.idmap_how_u.idmu.value;
3656                 break;
3657 
3658         default:
3659                 /* Don't cache other mapping types */
3660                 assert(FALSE);
3661         }
3662 
3663         /*
3664          * Using NULL for u2w instead of 0 so that our trigger allows
3665          * the same pid to be the destination in multiple entries
3666          */
3667         sql = sqlite_mprintf("INSERT OR REPLACE into idmap_cache "
3668             "(sidprefix, rid, windomain, canon_winname, pid, unixname, "
3669             "is_user, is_wuser, expiration, w2u, u2w, "
3670             "map_type, map_dn, map_attr, map_value, map_windomain, "
3671             "map_winname, map_unixname, map_is_nt4) "
3672             "VALUES(%Q, %u, %Q, %Q, %u, %Q, %d, %d, "
3673             "strftime('%%s','now') + %u, %q, 1, "
3674             "%d, %Q, %Q, %Q, %Q, %Q, %Q, %d); ",
3675             res->id.idmap_id_u.sid.prefix, res->id.idmap_id_u.sid.rid,
3676             req->id2domain, req->id2name, req->id1.idmap_id_u.uid,
3677             req->id1name, (req->id1.idtype == IDMAP_UID) ? 1 : 0,
3678             (res->id.idtype == IDMAP_USID) ? 1 : 0,
3679             state->id_cache_timeout,
3680             (res->direction == 0) ? "1" : NULL,
3681             res->info.how.map_type, map_dn, map_attr, map_value,
3682             map_windomain, map_winname, map_unixname, map_is_nt4);
3683 
3684         if (sql == NULL) {
3685                 retcode = IDMAP_ERR_INTERNAL;
3686                 idmapdlog(LOG_ERR, "Out of memory");
3687                 goto out;
3688         }
3689 
3690         retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3691         if (retcode != IDMAP_SUCCESS)
3692                 goto out;
3693 
3694         state->pid2sid_done = FALSE;
3695         sqlite_freemem(sql);
3696         sql = NULL;
3697 
3698         /* Check if we need to update namecache */
3699         if (req->direction & _IDMAP_F_DONT_UPDATE_NAMECACHE)
3700                 goto out;
3701 
3702         if (req->id2name == NULL)
3703                 goto out;
3704 
3705         sql = sqlite_mprintf("INSERT OR REPLACE into name_cache "
3706             "(sidprefix, rid, canon_name, domain, type, expiration) "
3707             "VALUES(%Q, %u, %Q, %Q, %d, strftime('%%s','now') + %u); ",
3708             res->id.idmap_id_u.sid.prefix, res->id.idmap_id_u.sid.rid,
3709             req->id2name, req->id2domain,
3710             res->id.idtype, state->name_cache_timeout);
3711 
3712         if (sql == NULL) {
3713                 retcode = IDMAP_ERR_INTERNAL;
3714                 idmapdlog(LOG_ERR, "Out of memory");
3715                 goto out;
3716         }
3717 
3718         retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3719 
3720 out:
3721         if (sql != NULL)
3722                 sqlite_freemem(sql);
3723         return (retcode);
3724 }
3725 
3726 idmap_retcode
3727 update_cache_sid2pid(lookup_state_t *state,
3728                 idmap_mapping *req, idmap_id_res *res)
3729 {
3730         char            *sql = NULL;
3731         idmap_retcode   retcode;
3732         int             is_eph_user;
3733         char            *map_dn = NULL;
3734         char            *map_attr = NULL;
3735         char            *map_value = NULL;
3736         char            *map_windomain = NULL;
3737         char            *map_winname = NULL;
3738         char            *map_unixname = NULL;
3739         int             map_is_nt4 = FALSE;
3740 
3741         /* Check if we need to cache anything */
3742         if (ARE_WE_DONE(req->direction))
3743                 return (IDMAP_SUCCESS);
3744 
3745         /* We don't cache negative entries */
3746         if (res->retcode != IDMAP_SUCCESS)
3747                 return (IDMAP_SUCCESS);
3748 
3749         if (req->direction & _IDMAP_F_EXP_EPH_UID)
3750                 is_eph_user = 1;
3751         else if (req->direction & _IDMAP_F_EXP_EPH_GID)
3752                 is_eph_user = 0;
3753         else
3754                 is_eph_user = -1;
3755 
3756         if (is_eph_user >= 0 &&
3757             !IDMAP_ID_IS_EPHEMERAL(res->id.idmap_id_u.uid)) {
3758                 sql = sqlite_mprintf("UPDATE idmap_cache "
3759                     "SET w2u = 0 WHERE "
3760                     "sidprefix = %Q AND rid = %u AND w2u = 1 AND "
3761                     "pid >= 2147483648 AND is_user = %d;",
3762                     req->id1.idmap_id_u.sid.prefix,
3763                     req->id1.idmap_id_u.sid.rid,
3764                     is_eph_user);
3765                 if (sql == NULL) {
3766                         retcode = IDMAP_ERR_INTERNAL;
3767                         idmapdlog(LOG_ERR, "Out of memory");
3768                         goto out;
3769                 }
3770 
3771                 retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3772                 if (retcode != IDMAP_SUCCESS)
3773                         goto out;
3774 
3775                 sqlite_freemem(sql);
3776                 sql = NULL;
3777         }
3778 
3779         assert(res->direction != IDMAP_DIRECTION_UNDEF);
3780         assert(res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID);
3781 
3782         switch (res->info.how.map_type) {
3783         case IDMAP_MAP_TYPE_DS_AD:
3784                 map_dn = res->info.how.idmap_how_u.ad.dn;
3785                 map_attr = res->info.how.idmap_how_u.ad.attr;
3786                 map_value = res->info.how.idmap_how_u.ad.value;
3787                 break;
3788 
3789         case IDMAP_MAP_TYPE_DS_NLDAP:
3790                 map_dn = res->info.how.idmap_how_u.nldap.dn;
3791                 map_attr = res->info.how.idmap_how_u.ad.attr;
3792                 map_value = res->info.how.idmap_how_u.nldap.value;
3793                 break;
3794 
3795         case IDMAP_MAP_TYPE_RULE_BASED:
3796                 map_windomain = res->info.how.idmap_how_u.rule.windomain;
3797                 map_winname = res->info.how.idmap_how_u.rule.winname;
3798                 map_unixname = res->info.how.idmap_how_u.rule.unixname;
3799                 map_is_nt4 = res->info.how.idmap_how_u.rule.is_nt4;
3800                 break;
3801 
3802         case IDMAP_MAP_TYPE_EPHEMERAL:
3803                 break;
3804 
3805         case IDMAP_MAP_TYPE_IDMU:
3806                 map_dn = res->info.how.idmap_how_u.idmu.dn;
3807                 map_attr = res->info.how.idmap_how_u.idmu.attr;
3808                 map_value = res->info.how.idmap_how_u.idmu.value;
3809                 break;
3810 
3811         default:
3812                 /* Don't cache other mapping types */
3813                 assert(FALSE);
3814         }
3815 
3816         sql = sqlite_mprintf("INSERT OR REPLACE into idmap_cache "
3817             "(sidprefix, rid, windomain, canon_winname, pid, unixname, "
3818             "is_user, is_wuser, expiration, w2u, u2w, "
3819             "map_type, map_dn, map_attr, map_value, map_windomain, "
3820             "map_winname, map_unixname, map_is_nt4) "
3821             "VALUES(%Q, %u, %Q, %Q, %u, %Q, %d, %d, "
3822             "strftime('%%s','now') + %u, 1, %q, "
3823             "%d, %Q, %Q, %Q, %Q, %Q, %Q, %d);",
3824             req->id1.idmap_id_u.sid.prefix, req->id1.idmap_id_u.sid.rid,
3825             (req->id1domain != NULL) ? req->id1domain : "", req->id1name,
3826             res->id.idmap_id_u.uid, req->id2name,
3827             (res->id.idtype == IDMAP_UID) ? 1 : 0,
3828             (req->id1.idtype == IDMAP_USID) ? 1 : 0,
3829             state->id_cache_timeout,
3830             (res->direction == 0) ? "1" : NULL,
3831             res->info.how.map_type, map_dn, map_attr, map_value,
3832             map_windomain, map_winname, map_unixname, map_is_nt4);
3833 
3834         if (sql == NULL) {
3835                 retcode = IDMAP_ERR_INTERNAL;
3836                 idmapdlog(LOG_ERR, "Out of memory");
3837                 goto out;
3838         }
3839 
3840         retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3841         if (retcode != IDMAP_SUCCESS)
3842                 goto out;
3843 
3844         state->sid2pid_done = FALSE;
3845         sqlite_freemem(sql);
3846         sql = NULL;
3847 
3848         /* Check if we need to update namecache */
3849         if (req->direction & _IDMAP_F_DONT_UPDATE_NAMECACHE)
3850                 goto out;
3851 
3852         if (EMPTY_STRING(req->id1name))
3853                 goto out;
3854 
3855         sql = sqlite_mprintf("INSERT OR REPLACE into name_cache "
3856             "(sidprefix, rid, canon_name, domain, type, expiration) "
3857             "VALUES(%Q, %u, %Q, %Q, %d, strftime('%%s','now') + %u); ",
3858             req->id1.idmap_id_u.sid.prefix, req->id1.idmap_id_u.sid.rid,
3859             req->id1name, req->id1domain,
3860             req->id1.idtype, state->name_cache_timeout);
3861 
3862         if (sql == NULL) {
3863                 retcode = IDMAP_ERR_INTERNAL;
3864                 idmapdlog(LOG_ERR, "Out of memory");
3865                 goto out;
3866         }
3867 
3868         retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3869 
3870 out:
3871         if (sql != NULL)
3872                 sqlite_freemem(sql);
3873         return (retcode);
3874 }
3875 
3876 static
3877 idmap_retcode
3878 lookup_cache_pid2sid(sqlite *cache, idmap_mapping *req, idmap_id_res *res,
3879                 int is_user)
3880 {
3881         char            *end;
3882         char            *sql = NULL;
3883         const char      **values;
3884         sqlite_vm       *vm = NULL;
3885         int             ncol;
3886         idmap_retcode   retcode = IDMAP_SUCCESS;
3887         time_t          curtime;
3888         idmap_id_type   idtype;
3889 
3890         /* Current time */
3891         errno = 0;
3892         if ((curtime = time(NULL)) == (time_t)-1) {
3893                 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
3894                     strerror(errno));
3895                 retcode = IDMAP_ERR_INTERNAL;
3896                 goto out;
3897         }
3898 
3899         /* SQL to lookup the cache by pid or by unixname */
3900         if (req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
3901                 sql = sqlite_mprintf("SELECT sidprefix, rid, "
3902                     "canon_winname, windomain, w2u, is_wuser, "
3903                     "map_type, map_dn, map_attr, map_value, map_windomain, "
3904                     "map_winname, map_unixname, map_is_nt4 "
3905                     "FROM idmap_cache WHERE "
3906                     "pid = %u AND u2w = 1 AND is_user = %d AND "
3907                     "(pid >= 2147483648 OR "
3908                     "(expiration = 0 OR expiration ISNULL OR "
3909                     "expiration > %d));",
3910                     req->id1.idmap_id_u.uid, is_user, curtime);
3911         } else if (req->id1name != NULL) {
3912                 sql = sqlite_mprintf("SELECT sidprefix, rid, "
3913                     "canon_winname, windomain, w2u, is_wuser, "
3914                     "map_type, map_dn, map_attr, map_value, map_windomain, "
3915                     "map_winname, map_unixname, map_is_nt4 "
3916                     "FROM idmap_cache WHERE "
3917                     "unixname = %Q AND u2w = 1 AND is_user = %d AND "
3918                     "(pid >= 2147483648 OR "
3919                     "(expiration = 0 OR expiration ISNULL OR "
3920                     "expiration > %d));",
3921                     req->id1name, is_user, curtime);
3922         } else {
3923                 retcode = IDMAP_ERR_ARG;
3924                 goto out;
3925         }
3926 
3927         if (sql == NULL) {
3928                 idmapdlog(LOG_ERR, "Out of memory");
3929                 retcode = IDMAP_ERR_MEMORY;
3930                 goto out;
3931         }
3932         retcode = sql_compile_n_step_once(
3933             cache, sql, &vm, &ncol, 14, &values);
3934         sqlite_freemem(sql);
3935 
3936         if (retcode == IDMAP_ERR_NOTFOUND)
3937                 goto out;
3938         else if (retcode == IDMAP_SUCCESS) {
3939                 /* sanity checks */
3940                 if (values[0] == NULL || values[1] == NULL) {
3941                         retcode = IDMAP_ERR_CACHE;
3942                         goto out;
3943                 }
3944 
3945                 switch (res->id.idtype) {
3946                 case IDMAP_SID:
3947                 case IDMAP_USID:
3948                 case IDMAP_GSID:
3949                         idtype = strtol(values[5], &end, 10) == 1
3950                             ? IDMAP_USID : IDMAP_GSID;
3951 
3952                         if (res->id.idtype == IDMAP_USID &&
3953                             idtype != IDMAP_USID) {
3954                                 retcode = IDMAP_ERR_NOTUSER;
3955                                 goto out;
3956                         } else if (res->id.idtype == IDMAP_GSID &&
3957                             idtype != IDMAP_GSID) {
3958                                 retcode = IDMAP_ERR_NOTGROUP;
3959                                 goto out;
3960                         }
3961                         res->id.idtype = idtype;
3962 
3963                         res->id.idmap_id_u.sid.rid =
3964                             strtoul(values[1], &end, 10);
3965                         res->id.idmap_id_u.sid.prefix = strdup(values[0]);
3966                         if (res->id.idmap_id_u.sid.prefix == NULL) {
3967                                 idmapdlog(LOG_ERR, "Out of memory");
3968                                 retcode = IDMAP_ERR_MEMORY;
3969                                 goto out;
3970                         }
3971 
3972                         if (values[4] != NULL)
3973                                 res->direction =
3974                                     (strtol(values[4], &end, 10) == 0)?
3975                                     IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
3976                         else
3977                                 res->direction = IDMAP_DIRECTION_U2W;
3978 
3979                         if (values[2] == NULL)
3980                                 break;
3981                         req->id2name = strdup(values[2]);
3982                         if (req->id2name == NULL) {
3983                                 idmapdlog(LOG_ERR, "Out of memory");
3984                                 retcode = IDMAP_ERR_MEMORY;
3985                                 goto out;
3986                         }
3987 
3988                         if (values[3] == NULL)
3989                                 break;
3990                         req->id2domain = strdup(values[3]);
3991                         if (req->id2domain == NULL) {
3992                                 idmapdlog(LOG_ERR, "Out of memory");
3993                                 retcode = IDMAP_ERR_MEMORY;
3994                                 goto out;
3995                         }
3996 
3997                         break;
3998                 default:
3999                         retcode = IDMAP_ERR_NOTSUPPORTED;
4000                         break;
4001                 }
4002                 if (req->flag & IDMAP_REQ_FLG_MAPPING_INFO) {
4003                         res->info.src = IDMAP_MAP_SRC_CACHE;
4004                         res->info.how.map_type = strtoul(values[6], &end, 10);
4005                         switch (res->info.how.map_type) {
4006                         case IDMAP_MAP_TYPE_DS_AD:
4007                                 res->info.how.idmap_how_u.ad.dn =
4008                                     strdup(values[7]);
4009                                 res->info.how.idmap_how_u.ad.attr =
4010                                     strdup(values[8]);
4011                                 res->info.how.idmap_how_u.ad.value =
4012                                     strdup(values[9]);
4013                                 break;
4014 
4015                         case IDMAP_MAP_TYPE_DS_NLDAP:
4016                                 res->info.how.idmap_how_u.nldap.dn =
4017                                     strdup(values[7]);
4018                                 res->info.how.idmap_how_u.nldap.attr =
4019                                     strdup(values[8]);
4020                                 res->info.how.idmap_how_u.nldap.value =
4021                                     strdup(values[9]);
4022                                 break;
4023 
4024                         case IDMAP_MAP_TYPE_RULE_BASED:
4025                                 res->info.how.idmap_how_u.rule.windomain =
4026                                     strdup(values[10]);
4027                                 res->info.how.idmap_how_u.rule.winname =
4028                                     strdup(values[11]);
4029                                 res->info.how.idmap_how_u.rule.unixname =
4030                                     strdup(values[12]);
4031                                 res->info.how.idmap_how_u.rule.is_nt4 =
4032                                     strtoul(values[13], &end, 10);
4033                                 res->info.how.idmap_how_u.rule.is_user =
4034                                     is_user;
4035                                 res->info.how.idmap_how_u.rule.is_wuser =
4036                                     strtol(values[5], &end, 10);
4037                                 break;
4038 
4039                         case IDMAP_MAP_TYPE_EPHEMERAL:
4040                                 break;
4041 
4042                         case IDMAP_MAP_TYPE_LOCAL_SID:
4043                                 break;
4044 
4045                         case IDMAP_MAP_TYPE_KNOWN_SID:
4046                                 break;
4047 
4048                         case IDMAP_MAP_TYPE_IDMU:
4049                                 res->info.how.idmap_how_u.idmu.dn =
4050                                     strdup(values[7]);
4051                                 res->info.how.idmap_how_u.idmu.attr =
4052                                     strdup(values[8]);
4053                                 res->info.how.idmap_how_u.idmu.value =
4054                                     strdup(values[9]);
4055                                 break;
4056 
4057                         default:
4058                                 /* Unknown mapping type */
4059                                 assert(FALSE);
4060                         }
4061                 }
4062         }
4063 
4064 out:
4065         if (vm != NULL)
4066                 (void) sqlite_finalize(vm, NULL);
4067         return (retcode);
4068 }
4069 
4070 /*
4071  * Given:
4072  * cache        sqlite handle
4073  * name         Windows user name
4074  * domain       Windows domain name
4075  *
4076  * Return:  Error code
4077  *
4078  * *canonname   Canonical name (if canonname is non-NULL) [1]
4079  * *sidprefix   SID prefix [1]
4080  * *rid         RID
4081  * *type        Type of name
4082  *
4083  * [1] malloc'ed, NULL on error
4084  */
4085 static
4086 idmap_retcode
4087 lookup_cache_name2sid(
4088     sqlite *cache,
4089     const char *name,
4090     const char *domain,
4091     char **canonname,
4092     char **sidprefix,
4093     idmap_rid_t *rid,
4094     idmap_id_type *type)
4095 {
4096         char            *end, *lower_name;
4097         char            *sql;
4098         const char      **values;
4099         sqlite_vm       *vm = NULL;
4100         int             ncol;
4101         time_t          curtime;
4102         idmap_retcode   retcode;
4103 
4104         *sidprefix = NULL;
4105         if (canonname != NULL)
4106                 *canonname = NULL;
4107 
4108         /* Get current time */
4109         errno = 0;
4110         if ((curtime = time(NULL)) == (time_t)-1) {
4111                 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
4112                     strerror(errno));
4113                 retcode = IDMAP_ERR_INTERNAL;
4114                 goto out;
4115         }
4116 
4117         /* SQL to lookup the cache */
4118         if ((lower_name = tolower_u8(name)) == NULL)
4119                 lower_name = (char *)name;
4120         sql = sqlite_mprintf("SELECT sidprefix, rid, type, canon_name "
4121             "FROM name_cache WHERE name = %Q AND domain = %Q AND "
4122             "(expiration = 0 OR expiration ISNULL OR "
4123             "expiration > %d);", lower_name, domain, curtime);
4124         if (lower_name != name)
4125                 free(lower_name);
4126         if (sql == NULL) {
4127                 idmapdlog(LOG_ERR, "Out of memory");
4128                 retcode = IDMAP_ERR_MEMORY;
4129                 goto out;
4130         }
4131         retcode = sql_compile_n_step_once(cache, sql, &vm, &ncol, 4, &values);
4132 
4133         sqlite_freemem(sql);
4134 
4135         if (retcode != IDMAP_SUCCESS)
4136                 goto out;
4137 
4138         if (type != NULL) {
4139                 if (values[2] == NULL) {
4140                         retcode = IDMAP_ERR_CACHE;
4141                         goto out;
4142                 }
4143                 *type = xlate_legacy_type(strtol(values[2], &end, 10));
4144         }
4145 
4146         if (values[0] == NULL || values[1] == NULL) {
4147                 retcode = IDMAP_ERR_CACHE;
4148                 goto out;
4149         }
4150 
4151         if (canonname != NULL) {
4152                 assert(values[3] != NULL);
4153                 *canonname = strdup(values[3]);
4154                 if (*canonname == NULL) {
4155                         idmapdlog(LOG_ERR, "Out of memory");
4156                         retcode = IDMAP_ERR_MEMORY;
4157                         goto out;
4158                 }
4159         }
4160 
4161         *sidprefix = strdup(values[0]);
4162         if (*sidprefix == NULL) {
4163                 idmapdlog(LOG_ERR, "Out of memory");
4164                 retcode = IDMAP_ERR_MEMORY;
4165                 goto out;
4166         }
4167         *rid = strtoul(values[1], &end, 10);
4168 
4169         retcode = IDMAP_SUCCESS;
4170 
4171 out:
4172         if (vm != NULL)
4173                 (void) sqlite_finalize(vm, NULL);
4174 
4175         if (retcode != IDMAP_SUCCESS) {
4176                 free(*sidprefix);
4177                 *sidprefix = NULL;
4178                 if (canonname != NULL) {
4179                         free(*canonname);
4180                         *canonname = NULL;
4181                 }
4182         }
4183         return (retcode);
4184 }
4185 
4186 static
4187 idmap_retcode
4188 ad_lookup_by_winname(lookup_state_t *state,
4189                 const char *name, const char *domain, int esidtype,
4190                 char **dn, char **attr, char **value, char **canonname,
4191                 char **sidprefix, idmap_rid_t *rid, idmap_id_type *wintype,
4192                 char **unixname)
4193 {
4194         int                     retries;
4195         idmap_query_state_t     *qs = NULL;
4196         idmap_retcode           rc, retcode;
4197         int                     i;
4198         int                     found_ad = 0;
4199 
4200         RDLOCK_CONFIG();
4201         if (_idmapdstate.num_gcs > 0) {
4202                 for (i = 0; i < _idmapdstate.num_gcs && !found_ad; i++) {
4203                         retries = 0;
4204 retry:
4205                         retcode = idmap_lookup_batch_start(
4206                             _idmapdstate.gcs[i],
4207                             1,
4208                             _idmapdstate.cfg->pgcfg.directory_based_mapping,
4209                             _idmapdstate.cfg->pgcfg.default_domain,
4210                             &qs);
4211                         if (retcode != IDMAP_SUCCESS) {
4212                                 if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
4213                                     retries++ < ADUTILS_DEF_NUM_RETRIES)
4214                                         goto retry;
4215                                 degrade_svc(1, "failed to create request for "
4216                                     "AD lookup by winname");
4217                                 return (retcode);
4218                         }
4219 
4220                         restore_svc();
4221 
4222                         if (state != NULL && i == 0) {
4223                                 /*
4224                                  * Directory based name mapping is only
4225                                  * performed within the joined forest (i == 0).
4226                                  * We don't trust other "trusted" forests to
4227                                  * provide DS-based name mapping information
4228                                  * because AD's definition of "cross-forest
4229                                  * trust" does not encompass this sort of
4230                                  * behavior.
4231                                  */
4232                                 idmap_lookup_batch_set_unixattr(qs,
4233                                     state->ad_unixuser_attr,
4234                                     state->ad_unixgroup_attr);
4235                         }
4236 
4237                         retcode = idmap_name2sid_batch_add1(qs, name, domain,
4238                             esidtype, dn, attr, value, canonname, sidprefix,
4239                             rid, wintype, unixname, NULL, &rc);
4240                         if (retcode == IDMAP_ERR_DOMAIN_NOTFOUND) {
4241                                 idmap_lookup_release_batch(&qs);
4242                                 continue;
4243                         }
4244                         found_ad = 1;
4245                         if (retcode != IDMAP_SUCCESS)
4246                                 idmap_lookup_release_batch(&qs);
4247                         else
4248                                 retcode = idmap_lookup_batch_end(&qs);
4249 
4250                         if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
4251                             retries++ < ADUTILS_DEF_NUM_RETRIES)
4252                                 goto retry;
4253                         else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR)
4254                                 degrade_svc(1,
4255                                     "some AD lookups timed out repeatedly");
4256                 }
4257         } else {
4258                 /* No AD case */
4259                 retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY;
4260         }
4261         UNLOCK_CONFIG();
4262 
4263         if (retcode != IDMAP_SUCCESS) {
4264                 idmapdlog(LOG_NOTICE,
4265                     "AD lookup of winname %s@%s failed, error code %d",
4266                     name == NULL ? "(null)" : name,
4267                     domain == NULL ? "(null)" : domain,
4268                     retcode);
4269                 return (retcode);
4270         }
4271         return (rc);
4272 }
4273 
4274 /*
4275  * Given:
4276  * cache        sqlite handle to cache
4277  * name         Windows user name
4278  * domain       Windows domain name
4279  * local_only   if true, don't try AD lookups
4280  *
4281  * Returns: Error code
4282  *
4283  * *canonname   Canonical name (if non-NULL) [1]
4284  * *canondomain Canonical domain (if non-NULL) [1]
4285  * *sidprefix   SID prefix [1]
4286  * *rid         RID
4287  * *req         Request (direction is updated)
4288  *
4289  * [1] malloc'ed, NULL on error
4290  */
4291 idmap_retcode
4292 lookup_name2sid(
4293     sqlite *cache,
4294     const char *name,
4295     const char *domain,
4296     int want_wuser,
4297     char **canonname,
4298     char **canondomain,
4299     char **sidprefix,
4300     idmap_rid_t *rid,
4301     idmap_id_type *type,
4302     idmap_mapping *req,
4303     int local_only)
4304 {
4305         idmap_retcode   retcode;
4306 
4307         *sidprefix = NULL;
4308         if (canonname != NULL)
4309                 *canonname = NULL;
4310         if (canondomain != NULL)
4311                 *canondomain = NULL;
4312 
4313         /* Lookup well-known SIDs table */
4314         retcode = lookup_wksids_name2sid(name, domain, canonname, canondomain,
4315             sidprefix, rid, type);
4316         if (retcode == IDMAP_SUCCESS) {
4317                 req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
4318                 goto out;
4319         } else if (retcode != IDMAP_ERR_NOTFOUND) {
4320                 return (retcode);
4321         }
4322 
4323         /* Lookup cache */
4324         retcode = lookup_cache_name2sid(cache, name, domain, canonname,
4325             sidprefix, rid, type);
4326         if (retcode == IDMAP_SUCCESS) {
4327                 req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
4328                 goto out;
4329         } else if (retcode != IDMAP_ERR_NOTFOUND) {
4330                 return (retcode);
4331         }
4332 
4333         /*
4334          * The caller may be using this function to determine if this
4335          * request needs to be marked for AD lookup or not
4336          * (i.e. _IDMAP_F_LOOKUP_AD) and therefore may not want this
4337          * function to AD lookup now.
4338          */
4339         if (local_only)
4340                 return (retcode);
4341 
4342         if (_idmapdstate.cfg->pgcfg.use_lsa &&
4343             _idmapdstate.cfg->pgcfg.domain_name != NULL &&
4344             name != NULL && *sidprefix == NULL) {
4345                 retcode = lookup_lsa_by_name(name, domain,
4346                     sidprefix, rid,
4347                     canonname, canondomain,
4348                     type);
4349                 if (retcode == IDMAP_SUCCESS)
4350                         goto out;
4351                 else if (retcode != IDMAP_ERR_NOTFOUND)
4352                         return (retcode);
4353         }
4354 
4355         /* Lookup AD */
4356         retcode = ad_lookup_by_winname(NULL, name, domain, IDMAP_POSIXID,
4357             NULL, NULL, NULL, canonname, sidprefix, rid, type, NULL);
4358         if (retcode != IDMAP_SUCCESS)
4359                 return (retcode);
4360 
4361 out:
4362         /*
4363          * Entry found (cache or Windows lookup)
4364          */
4365         if (want_wuser == 1 && *type != IDMAP_USID)
4366                 retcode = IDMAP_ERR_NOTUSER;
4367         else if (want_wuser == 0 && *type != IDMAP_GSID)
4368                 retcode = IDMAP_ERR_NOTGROUP;
4369         else if (want_wuser == -1) {
4370                 /*
4371                  * Caller wants to know if its user or group
4372                  * Verify that it's one or the other.
4373                  */
4374                 if (*type != IDMAP_USID && *type != IDMAP_GSID)
4375                         retcode = IDMAP_ERR_SID;
4376         }
4377 
4378         if (retcode == IDMAP_SUCCESS) {
4379                 /*
4380                  * If we were asked for a canonical domain and none
4381                  * of the searches have provided one, assume it's the
4382                  * supplied domain.
4383                  */
4384                 if (canondomain != NULL && *canondomain == NULL) {
4385                         *canondomain = strdup(domain);
4386                         if (*canondomain == NULL)
4387                                 retcode = IDMAP_ERR_MEMORY;
4388                 }
4389         }
4390         if (retcode != IDMAP_SUCCESS) {
4391                 free(*sidprefix);
4392                 *sidprefix = NULL;
4393                 if (canonname != NULL) {
4394                         free(*canonname);
4395                         *canonname = NULL;
4396                 }
4397                 if (canondomain != NULL) {
4398                         free(*canondomain);
4399                         *canondomain = NULL;
4400                 }
4401         }
4402         return (retcode);
4403 }
4404 
4405 static
4406 idmap_retcode
4407 name_based_mapping_pid2sid(lookup_state_t *state, const char *unixname,
4408                 int is_user, idmap_mapping *req, idmap_id_res *res)
4409 {
4410         const char      *winname, *windomain;
4411         char            *canonname;
4412         char            *canondomain;
4413         char            *sql = NULL, *errmsg = NULL;
4414         idmap_retcode   retcode;
4415         char            *end;
4416         const char      **values;
4417         sqlite_vm       *vm = NULL;
4418         int             ncol, r;
4419         int             want_wuser;
4420         const char      *me = "name_based_mapping_pid2sid";
4421         idmap_namerule  *rule = &res->info.how.idmap_how_u.rule;
4422         int direction;
4423 
4424         assert(unixname != NULL); /* We have unixname */
4425         assert(req->id2name == NULL); /* We don't have winname */
4426         assert(res->id.idmap_id_u.sid.prefix == NULL); /* No SID either */
4427 
4428         sql = sqlite_mprintf(
4429             "SELECT winname_display, windomain, w2u_order, "
4430             "is_wuser, unixname, is_nt4 "
4431             "FROM namerules WHERE "
4432             "u2w_order > 0 AND is_user = %d AND "
4433             "(unixname = %Q OR unixname = '*') "
4434             "ORDER BY u2w_order ASC;", is_user, unixname);
4435         if (sql == NULL) {
4436                 idmapdlog(LOG_ERR, "Out of memory");
4437                 retcode = IDMAP_ERR_MEMORY;
4438                 goto out;
4439         }
4440 
4441         if (sqlite_compile(state->db, sql, NULL, &vm, &errmsg) != SQLITE_OK) {
4442                 retcode = IDMAP_ERR_INTERNAL;
4443                 idmapdlog(LOG_ERR, "%s: database error (%s)", me,
4444                     CHECK_NULL(errmsg));
4445                 sqlite_freemem(errmsg);
4446                 goto out;
4447         }
4448 
4449         for (;;) {
4450                 r = sqlite_step(vm, &ncol, &values, NULL);
4451                 assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
4452                 if (r == SQLITE_ROW) {
4453                         if (ncol < 6) {
4454                                 retcode = IDMAP_ERR_INTERNAL;
4455                                 goto out;
4456                         }
4457 
4458                         TRACE(req, res, "Matching rule: %s -> %s@%s",
4459                             values[4] == NULL ? "(null)" : values[4],
4460                             values[0] == NULL ? "(null)" : values[0],
4461                             values[1] == NULL ? "(null)" : values[1]);
4462 
4463                         if (values[0] == NULL) {
4464                                 /* values [1] and [2] can be null */
4465                                 retcode = IDMAP_ERR_INTERNAL;
4466                                 goto out;
4467                         }
4468 
4469                         if (values[2] != NULL)
4470                                 direction =
4471                                     (strtol(values[2], &end, 10) == 0)?
4472                                     IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
4473                         else
4474                                 direction = IDMAP_DIRECTION_U2W;
4475 
4476                         if (EMPTY_NAME(values[0])) {
4477                                 idmap_namerule_set(rule, values[1], values[0],
4478                                     values[4], is_user,
4479                                     strtol(values[3], &end, 10),
4480                                     strtol(values[5], &end, 10),
4481                                     direction);
4482                                 TRACE(req, res, "Mapping inhibited");
4483                                 retcode = IDMAP_ERR_NOMAPPING;
4484                                 goto out;
4485                         }
4486 
4487                         if (values[0][0] == '*') {
4488                                 winname = unixname;
4489                         } else {
4490                                 winname = values[0];
4491                         }
4492 
4493                         want_wuser = res->id.idtype == IDMAP_USID ? 1
4494                             : res->id.idtype == IDMAP_GSID ? 0
4495                             : -1;
4496                         if (values[1] != NULL)
4497                                 windomain = values[1];
4498                         else if (state->defdom != NULL) {
4499                                 windomain = state->defdom;
4500                                 TRACE(req, res,
4501                                     "Added default domain %s to rule",
4502                                     windomain);
4503                         } else {
4504                                 idmapdlog(LOG_ERR, "%s: no domain", me);
4505                                 TRACE(req, res,
4506                                     "No domain in rule, and no default domain");
4507                                 retcode = IDMAP_ERR_DOMAIN_NOTFOUND;
4508                                 goto out;
4509                         }
4510 
4511                         retcode = lookup_name2sid(state->cache,
4512                             winname, windomain,
4513                             want_wuser, &canonname, &canondomain,
4514                             &res->id.idmap_id_u.sid.prefix,
4515                             &res->id.idmap_id_u.sid.rid,
4516                             &res->id.idtype, req, 0);
4517 
4518                         if (retcode == IDMAP_SUCCESS) {
4519                                 break;
4520                         } else if (retcode == IDMAP_ERR_NOTFOUND) {
4521                                 if (values[0][0] == '*') {
4522                                         TRACE(req, res,
4523                                             "%s@%s not found, continuing",
4524                                             winname, windomain);
4525                                         continue;
4526                                 } else {
4527                                         TRACE(req, res,
4528                                             "%s@%s not found",
4529                                             winname, windomain);
4530                                         retcode = IDMAP_ERR_NOMAPPING;
4531                                 }
4532                         } else {
4533                                 TRACE(req, res,
4534                                     "Looking up %s@%s error=%d",
4535                                     winname, windomain, retcode);
4536                         }
4537 
4538                         idmap_namerule_set(rule, values[1],
4539                             values[0], values[4], is_user,
4540                             strtol(values[3], &end, 10),
4541                             strtol(values[5], &end, 10),
4542                             direction);
4543 
4544                         goto out;
4545 
4546                 } else if (r == SQLITE_DONE) {
4547                         TRACE(req, res, "No matching rule");
4548                         retcode = IDMAP_ERR_NOTFOUND;
4549                         goto out;
4550                 } else {
4551                         (void) sqlite_finalize(vm, &errmsg);
4552                         vm = NULL;
4553                         idmapdlog(LOG_ERR, "%s: database error (%s)", me,
4554                             CHECK_NULL(errmsg));
4555                         sqlite_freemem(errmsg);
4556                         retcode = IDMAP_ERR_INTERNAL;
4557                         goto out;
4558                 }
4559         }
4560 
4561         if (values[2] != NULL)
4562                 res->direction =
4563                     (strtol(values[2], &end, 10) == 0)?
4564                     IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
4565         else
4566                 res->direction = IDMAP_DIRECTION_U2W;
4567 
4568         req->id2name = canonname;
4569         req->id2domain = canondomain;
4570 
4571         idmap_namerule_set(rule, values[1], values[0], values[4],
4572             is_user, strtol(values[3], &end, 10),
4573             strtol(values[5], &end, 10),
4574             rule->direction);
4575         TRACE(req, res, "Windows name found");
4576 
4577 out:
4578         if (sql != NULL)
4579                 sqlite_freemem(sql);
4580 
4581         if (retcode != IDMAP_ERR_NOTFOUND) {
4582                 res->info.how.map_type = IDMAP_MAP_TYPE_RULE_BASED;
4583                 res->info.src = IDMAP_MAP_SRC_NEW;
4584         }
4585 
4586         if (vm != NULL)
4587                 (void) sqlite_finalize(vm, NULL);
4588         return (retcode);
4589 }
4590 
4591 /*
4592  * Convention when processing unix2win requests:
4593  *
4594  * Unix identity:
4595  * req->id1name =
4596  *              unixname if given otherwise unixname found will be placed
4597  *              here.
4598  * req->id1domain =
4599  *              NOT USED
4600  * req->id1.idtype =
4601  *              Given type (IDMAP_UID or IDMAP_GID)
4602  * req->id1..[uid or gid] =
4603  *              UID/GID if given otherwise UID/GID found will be placed here.
4604  *
4605  * Windows identity:
4606  * req->id2name =
4607  *              winname found will be placed here.
4608  * req->id2domain =
4609  *              windomain found will be placed here.
4610  * res->id.idtype =
4611  *              Target type initialized from req->id2.idtype. If
4612  *              it is IDMAP_SID then actual type (IDMAP_USID/GSID) found
4613  *              will be placed here.
4614  * req->id..sid.[prefix, rid] =
4615  *              SID found will be placed here.
4616  *
4617  * Others:
4618  * res->retcode =
4619  *              Return status for this request will be placed here.
4620  * res->direction =
4621  *              Direction found will be placed here. Direction
4622  *              meaning whether the resultant mapping is valid
4623  *              only from unix2win or bi-directional.
4624  * req->direction =
4625  *              INTERNAL USE. Used by idmapd to set various
4626  *              flags (_IDMAP_F_xxxx) to aid in processing
4627  *              of the request.
4628  * req->id2.idtype =
4629  *              INTERNAL USE. Initially this is the requested target
4630  *              type and is used to initialize res->id.idtype.
4631  *              ad_lookup_batch() uses this field temporarily to store
4632  *              sid_type obtained by the batched AD lookups and after
4633  *              use resets it to IDMAP_NONE to prevent xdr from
4634  *              mis-interpreting the contents of req->id2.
4635  * req->id2..[uid or gid or sid] =
4636  *              NOT USED
4637  */
4638 
4639 /*
4640  * This function does the following:
4641  * 1. Lookup well-known SIDs table.
4642  * 2. Lookup cache.
4643  * 3. Check if the client does not want new mapping to be allocated
4644  *    in which case this pass is the final pass.
4645  * 4. Set AD/NLDAP lookup flags if it determines that the next stage needs
4646  *    to do AD/NLDAP lookup.
4647  */
4648 idmap_retcode
4649 pid2sid_first_pass(lookup_state_t *state, idmap_mapping *req,
4650                 idmap_id_res *res, int is_user)
4651 {
4652         idmap_retcode   retcode;
4653         idmap_retcode   retcode2;
4654         bool_t          gen_localsid_on_err = FALSE;
4655 
4656         /* Initialize result */
4657         res->id.idtype = req->id2.idtype;
4658         res->direction = IDMAP_DIRECTION_UNDEF;
4659 
4660         if (req->id2.idmap_id_u.sid.prefix != NULL) {
4661                 /* sanitize sidprefix */
4662                 free(req->id2.idmap_id_u.sid.prefix);
4663                 req->id2.idmap_id_u.sid.prefix = NULL;
4664         }
4665 
4666         /* Find pid */
4667         if (req->id1.idmap_id_u.uid == IDMAP_SENTINEL_PID) {
4668                 if (req->id1name == NULL) {
4669                         retcode = IDMAP_ERR_ARG;
4670                         goto out;
4671                 }
4672 
4673                 retcode = ns_lookup_byname(req->id1name, NULL, &req->id1);
4674                 if (retcode != IDMAP_SUCCESS) {
4675                         TRACE(req, res, "Getting UNIX ID error=%d", retcode);
4676                         retcode = IDMAP_ERR_NOMAPPING;
4677                         goto out;
4678                 }
4679                 TRACE(req, res, "Found UNIX ID");
4680         }
4681 
4682         /* Lookup in well-known SIDs table */
4683         retcode = lookup_wksids_pid2sid(req, res, is_user);
4684         if (retcode == IDMAP_SUCCESS) {
4685                 TRACE(req, res, "Hardwired mapping");
4686                 goto out;
4687         } else if (retcode != IDMAP_ERR_NOTFOUND) {
4688                 TRACE(req, res,
4689                     "Well-known account lookup error=%d", retcode);
4690                 goto out;
4691         }
4692 
4693         /* Lookup in cache */
4694         retcode = lookup_cache_pid2sid(state->cache, req, res, is_user);
4695         if (retcode == IDMAP_SUCCESS) {
4696                 TRACE(req, res, "Found in mapping cache");
4697                 goto out;
4698         } else if (retcode != IDMAP_ERR_NOTFOUND) {
4699                 TRACE(req, res,
4700                     "Mapping cache lookup error=%d", retcode);
4701                 goto out;
4702         }
4703         TRACE(req, res, "Not found in mapping cache");
4704 
4705         /* Ephemeral ids cannot be allocated during pid2sid */
4706         if (IDMAP_ID_IS_EPHEMERAL(req->id1.idmap_id_u.uid)) {
4707                 retcode = IDMAP_ERR_NOMAPPING;
4708                 TRACE(req, res, "Shouldn't have an ephemeral ID here");
4709                 goto out;
4710         }
4711 
4712         if (DO_NOT_ALLOC_NEW_ID_MAPPING(req)) {
4713                 retcode = IDMAP_ERR_NONE_GENERATED;
4714                 goto out;
4715         }
4716 
4717         if (AVOID_NAMESERVICE(req)) {
4718                 gen_localsid_on_err = TRUE;
4719                 retcode = IDMAP_ERR_NOMAPPING;
4720                 goto out;
4721         }
4722 
4723         /* Set flags for the next stage */
4724         if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU) {
4725                 req->direction |= _IDMAP_F_LOOKUP_AD;
4726                 state->ad_nqueries++;
4727         } else if (AD_MODE(req->id1.idtype, state)) {
4728                 /*
4729                  * If AD-based name mapping is enabled then the next stage
4730                  * will need to lookup AD using unixname to get the
4731                  * corresponding winname.
4732                  */
4733                 if (req->id1name == NULL) {
4734                         /* Get unixname if only pid is given. */
4735                         retcode = ns_lookup_bypid(req->id1.idmap_id_u.uid,
4736                             is_user, &req->id1name);
4737                         if (retcode != IDMAP_SUCCESS) {
4738                                 TRACE(req, res,
4739                                     "Getting UNIX name error=%d", retcode);
4740                                 gen_localsid_on_err = TRUE;
4741                                 goto out;
4742                         }
4743                         TRACE(req, res, "Found UNIX name");
4744                 }
4745                 req->direction |= _IDMAP_F_LOOKUP_AD;
4746                 state->ad_nqueries++;
4747         } else if (NLDAP_OR_MIXED_MODE(req->id1.idtype, state)) {
4748                 /*
4749                  * If native LDAP or mixed mode is enabled for name mapping
4750                  * then the next stage will need to lookup native LDAP using
4751                  * unixname/pid to get the corresponding winname.
4752                  */
4753                 req->direction |= _IDMAP_F_LOOKUP_NLDAP;
4754                 state->nldap_nqueries++;
4755         }
4756 
4757         /*
4758          * Failed to find non-expired entry in cache. Set the flag to
4759          * indicate that we are not done yet.
4760          */
4761         state->pid2sid_done = FALSE;
4762         req->direction |= _IDMAP_F_NOTDONE;
4763         retcode = IDMAP_SUCCESS;
4764 
4765 out:
4766         res->retcode = idmap_stat4prot(retcode);
4767         if (ARE_WE_DONE(req->direction) && res->retcode != IDMAP_SUCCESS) {
4768                 if (gen_localsid_on_err == TRUE) {
4769                         retcode2 = generate_localsid(req, res, is_user, TRUE);
4770                         if (retcode2 == IDMAP_SUCCESS)
4771                                 TRACE(req, res, "Generate local SID");
4772                         else
4773                                 TRACE(req, res,
4774                                     "Generate local SID error=%d", retcode2);
4775                 }
4776         }
4777         return (retcode);
4778 }
4779 
4780 idmap_retcode
4781 pid2sid_second_pass(lookup_state_t *state, idmap_mapping *req,
4782         idmap_id_res *res, int is_user)
4783 {
4784         bool_t          gen_localsid_on_err = TRUE;
4785         idmap_retcode   retcode = IDMAP_SUCCESS;
4786         idmap_retcode   retcode2;
4787 
4788         /* Check if second pass is needed */
4789         if (ARE_WE_DONE(req->direction))
4790                 return (res->retcode);
4791 
4792         /* Get status from previous pass */
4793         retcode = res->retcode;
4794         if (retcode != IDMAP_SUCCESS)
4795                 goto out;
4796 
4797         /*
4798          * If directory-based name mapping is enabled then the winname
4799          * may already have been retrieved from the AD object (AD-mode)
4800          * or from native LDAP object (nldap-mode or mixed-mode).
4801          * Note that if we have winname but no SID then it's an error
4802          * because this implies that the Native LDAP entry contains
4803          * winname which does not exist and it's better that we return
4804          * an error instead of doing rule-based mapping so that the user
4805          * can detect the issue and take appropriate action.
4806          */
4807         if (req->id2name != NULL) {
4808                 /* Return notfound if we've winname but no SID. */
4809                 if (res->id.idmap_id_u.sid.prefix == NULL) {
4810                         TRACE(req, res, "Windows name but no SID");
4811                         retcode = IDMAP_ERR_NOTFOUND;
4812                         goto out;
4813                 }
4814                 if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU)
4815                         res->direction = IDMAP_DIRECTION_BI;
4816                 else if (AD_MODE(req->id1.idtype, state))
4817                         res->direction = IDMAP_DIRECTION_BI;
4818                 else if (NLDAP_MODE(req->id1.idtype, state))
4819                         res->direction = IDMAP_DIRECTION_BI;
4820                 else if (MIXED_MODE(req->id1.idtype, state))
4821                         res->direction = IDMAP_DIRECTION_W2U;
4822                 goto out;
4823         } else if (res->id.idmap_id_u.sid.prefix != NULL) {
4824                 /*
4825                  * We've SID but no winname. This is fine because
4826                  * the caller may have only requested SID.
4827                  */
4828                 goto out;
4829         }
4830 
4831         /* Free any mapping info from Directory based mapping */
4832         if (res->info.how.map_type != IDMAP_MAP_TYPE_UNKNOWN)
4833                 idmap_how_clear(&res->info.how);
4834 
4835         if (req->id1name == NULL) {
4836                 /* Get unixname from name service */
4837                 retcode = ns_lookup_bypid(req->id1.idmap_id_u.uid, is_user,
4838                     &req->id1name);
4839                 if (retcode != IDMAP_SUCCESS) {
4840                         TRACE(req, res,
4841                             "Getting UNIX name error=%d", retcode);
4842                         goto out;
4843                 }
4844                 TRACE(req, res, "Found UNIX name");
4845         } else if (req->id1.idmap_id_u.uid == IDMAP_SENTINEL_PID) {
4846                 /* Get pid from name service */
4847                 retcode = ns_lookup_byname(req->id1name, NULL, &req->id1);
4848                 if (retcode != IDMAP_SUCCESS) {
4849                         TRACE(req, res,
4850                             "Getting UNIX ID error=%d", retcode);
4851                         gen_localsid_on_err = FALSE;
4852                         goto out;
4853                 }
4854                 TRACE(req, res, "Found UNIX ID");
4855         }
4856 
4857         /* Use unixname to evaluate local name-based mapping rules */
4858         retcode = name_based_mapping_pid2sid(state, req->id1name, is_user,
4859             req, res);
4860         if (retcode == IDMAP_ERR_NOTFOUND) {
4861                 retcode = generate_localsid(req, res, is_user, FALSE);
4862                 if (retcode == IDMAP_SUCCESS) {
4863                         TRACE(req, res, "Generated local SID");
4864                 } else {
4865                         TRACE(req, res,
4866                             "Generating local SID error=%d", retcode);
4867                 }
4868                 gen_localsid_on_err = FALSE;
4869         }
4870 
4871 out:
4872         res->retcode = idmap_stat4prot(retcode);
4873         if (res->retcode != IDMAP_SUCCESS) {
4874                 req->direction = _IDMAP_F_DONE;
4875                 free(req->id2name);
4876                 req->id2name = NULL;
4877                 free(req->id2domain);
4878                 req->id2domain = NULL;
4879                 if (gen_localsid_on_err == TRUE) {
4880                         retcode2 = generate_localsid(req, res, is_user, TRUE);
4881                         if (retcode2 == IDMAP_SUCCESS)
4882                                 TRACE(req, res, "Generate local SID");
4883                         else
4884                                 TRACE(req, res,
4885                                     "Generate local SID error=%d", retcode2);
4886                 } else {
4887                         res->id.idtype = is_user ? IDMAP_USID : IDMAP_GSID;
4888                 }
4889         }
4890         if (!ARE_WE_DONE(req->direction))
4891                 state->pid2sid_done = FALSE;
4892         return (retcode);
4893 }
4894 
4895 idmap_retcode
4896 idmap_cache_flush(idmap_flush_op op)
4897 {
4898         idmap_retcode   rc;
4899         sqlite *cache = NULL;
4900         char *sql1;
4901         char *sql2;
4902 
4903         switch (op) {
4904         case IDMAP_FLUSH_EXPIRE:
4905                 sql1 =
4906                     "UPDATE idmap_cache SET expiration=1 WHERE expiration>0;";
4907                 sql2 =
4908                     "UPDATE name_cache SET expiration=1 WHERE expiration>0;";
4909                 break;
4910 
4911         case IDMAP_FLUSH_DELETE:
4912                 sql1 = "DELETE FROM idmap_cache;";
4913                 sql2 = "DELETE FROM name_cache;";
4914                 break;
4915 
4916         default:
4917                 return (IDMAP_ERR_INTERNAL);
4918         }
4919 
4920         rc = get_cache_handle(&cache);
4921         if (rc != IDMAP_SUCCESS)
4922                 return (rc);
4923 
4924         /*
4925          * Note that we flush the idmapd cache first, before the kernel
4926          * cache.  If we did it the other way 'round, a request could come
4927          * in after the kernel cache flush and pull a soon-to-be-flushed
4928          * idmapd cache entry back into the kernel cache.  This way the
4929          * worst that will happen is that a new entry will be added to
4930          * the kernel cache and then immediately flushed.
4931          */
4932 
4933         rc = sql_exec_no_cb(cache, IDMAP_CACHENAME, sql1);
4934         if (rc != IDMAP_SUCCESS)
4935                 return (rc);
4936 
4937         rc = sql_exec_no_cb(cache, IDMAP_CACHENAME, sql2);
4938 
4939         (void) __idmap_flush_kcache();
4940         return (rc);
4941 }