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