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