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