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