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