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