8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / idmap / idmapd / dbutils.c
blobb868242728f6966f295fdbaeb22dfef8e7fe0bcd
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 if (req->id2name != NULL)
1636 free(req->id2name);
1637 req->id2name = strdup(values[3]);
1638 if (req->id2name == NULL) {
1639 idmapdlog(LOG_ERR, "Out of memory");
1640 retcode = IDMAP_ERR_MEMORY;
1644 req->id1.idtype = strncmp(values[5], "0", 2) ?
1645 IDMAP_USID : IDMAP_GSID;
1647 if (req->flag & IDMAP_REQ_FLG_MAPPING_INFO) {
1648 res->info.src = IDMAP_MAP_SRC_CACHE;
1649 res->info.how.map_type = strtoul(values[6], &end, 10);
1650 switch (res->info.how.map_type) {
1651 case IDMAP_MAP_TYPE_DS_AD:
1652 res->info.how.idmap_how_u.ad.dn =
1653 strdup(values[7]);
1654 res->info.how.idmap_how_u.ad.attr =
1655 strdup(values[8]);
1656 res->info.how.idmap_how_u.ad.value =
1657 strdup(values[9]);
1658 break;
1660 case IDMAP_MAP_TYPE_DS_NLDAP:
1661 res->info.how.idmap_how_u.nldap.dn =
1662 strdup(values[7]);
1663 res->info.how.idmap_how_u.nldap.attr =
1664 strdup(values[8]);
1665 res->info.how.idmap_how_u.nldap.value =
1666 strdup(values[9]);
1667 break;
1669 case IDMAP_MAP_TYPE_RULE_BASED:
1670 res->info.how.idmap_how_u.rule.windomain =
1671 strdup(values[10]);
1672 res->info.how.idmap_how_u.rule.winname =
1673 strdup(values[11]);
1674 res->info.how.idmap_how_u.rule.unixname =
1675 strdup(values[12]);
1676 res->info.how.idmap_how_u.rule.is_nt4 =
1677 strtoul(values[13], &end, 1);
1678 res->info.how.idmap_how_u.rule.is_user =
1679 is_user;
1680 res->info.how.idmap_how_u.rule.is_wuser =
1681 strtoul(values[5], &end, 1);
1682 break;
1684 case IDMAP_MAP_TYPE_EPHEMERAL:
1685 break;
1687 case IDMAP_MAP_TYPE_LOCAL_SID:
1688 break;
1690 case IDMAP_MAP_TYPE_KNOWN_SID:
1691 break;
1693 case IDMAP_MAP_TYPE_IDMU:
1694 res->info.how.idmap_how_u.idmu.dn =
1695 strdup(values[7]);
1696 res->info.how.idmap_how_u.idmu.attr =
1697 strdup(values[8]);
1698 res->info.how.idmap_how_u.idmu.value =
1699 strdup(values[9]);
1700 break;
1702 default:
1703 /* Unknown mapping type */
1704 assert(FALSE);
1708 if (vm != NULL)
1709 (void) sqlite_finalize(vm, NULL);
1710 return (retcode);
1714 * Previous versions used two enumerations for representing types.
1715 * One of those has largely been eliminated, but was used in the
1716 * name cache table and so during an upgrade might still be visible.
1717 * In addition, the test suite prepopulates the cache with these values.
1719 * This function translates those old values into the new values.
1721 * This code deliberately does not use symbolic values for the legacy
1722 * values. This is the *only* place where they should be used.
1724 static
1725 idmap_id_type
1726 xlate_legacy_type(int type)
1728 switch (type) {
1729 case -1004: /* _IDMAP_T_USER */
1730 return (IDMAP_USID);
1731 case -1005: /* _IDMAP_T_GROUP */
1732 return (IDMAP_GSID);
1733 default:
1734 return (type);
1736 NOTE(NOTREACHED)
1739 static
1740 idmap_retcode
1741 lookup_cache_sid2name(sqlite *cache, const char *sidprefix, idmap_rid_t rid,
1742 char **canonname, char **canondomain, idmap_id_type *type)
1744 char *end;
1745 char *sql = NULL;
1746 const char **values;
1747 sqlite_vm *vm = NULL;
1748 int ncol;
1749 time_t curtime;
1750 idmap_retcode retcode = IDMAP_SUCCESS;
1752 /* Get current time */
1753 errno = 0;
1754 if ((curtime = time(NULL)) == (time_t)-1) {
1755 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
1756 strerror(errno));
1757 retcode = IDMAP_ERR_INTERNAL;
1758 goto out;
1761 /* SQL to lookup the cache */
1762 sql = sqlite_mprintf("SELECT canon_name, domain, type "
1763 "FROM name_cache WHERE "
1764 "sidprefix = %Q AND rid = %u AND "
1765 "(expiration = 0 OR expiration ISNULL OR "
1766 "expiration > %d);",
1767 sidprefix, rid, curtime);
1768 if (sql == NULL) {
1769 idmapdlog(LOG_ERR, "Out of memory");
1770 retcode = IDMAP_ERR_MEMORY;
1771 goto out;
1773 retcode = sql_compile_n_step_once(cache, sql, &vm, &ncol, 3, &values);
1774 sqlite_freemem(sql);
1776 if (retcode == IDMAP_SUCCESS) {
1777 if (type != NULL) {
1778 if (values[2] == NULL) {
1779 retcode = IDMAP_ERR_CACHE;
1780 goto out;
1782 *type = xlate_legacy_type(strtol(values[2], &end, 10));
1785 if (canonname != NULL && values[0] != NULL) {
1786 if ((*canonname = strdup(values[0])) == NULL) {
1787 idmapdlog(LOG_ERR, "Out of memory");
1788 retcode = IDMAP_ERR_MEMORY;
1789 goto out;
1793 if (canondomain != NULL && values[1] != NULL) {
1794 if ((*canondomain = strdup(values[1])) == NULL) {
1795 if (canonname != NULL) {
1796 free(*canonname);
1797 *canonname = NULL;
1799 idmapdlog(LOG_ERR, "Out of memory");
1800 retcode = IDMAP_ERR_MEMORY;
1801 goto out;
1806 out:
1807 if (vm != NULL)
1808 (void) sqlite_finalize(vm, NULL);
1809 return (retcode);
1813 * Given SID, find winname using name_cache OR
1814 * Given winname, find SID using name_cache.
1815 * Used when mapping win to unix i.e. req->id1 is windows id and
1816 * req->id2 is unix id
1818 static
1819 idmap_retcode
1820 lookup_name_cache(sqlite *cache, idmap_mapping *req, idmap_id_res *res)
1822 idmap_id_type type = -1;
1823 idmap_retcode retcode;
1824 char *sidprefix = NULL;
1825 idmap_rid_t rid;
1826 char *name = NULL, *domain = NULL;
1828 /* Done if we've both sid and winname */
1829 if (req->id1.idmap_id_u.sid.prefix != NULL && req->id1name != NULL) {
1830 /* Don't bother TRACE()ing, too boring */
1831 return (IDMAP_SUCCESS);
1834 if (req->id1.idmap_id_u.sid.prefix != NULL) {
1835 /* Lookup sid to winname */
1836 retcode = lookup_cache_sid2name(cache,
1837 req->id1.idmap_id_u.sid.prefix,
1838 req->id1.idmap_id_u.sid.rid, &name, &domain, &type);
1839 } else {
1840 /* Lookup winame to sid */
1841 retcode = lookup_cache_name2sid(cache, req->id1name,
1842 req->id1domain, &name, &sidprefix, &rid, &type);
1845 if (retcode != IDMAP_SUCCESS) {
1846 if (retcode == IDMAP_ERR_NOTFOUND) {
1847 TRACE(req, res, "Not found in name cache");
1848 } else {
1849 TRACE(req, res, "Name cache lookup error=%d", retcode);
1851 free(name);
1852 free(domain);
1853 free(sidprefix);
1854 return (retcode);
1857 req->id1.idtype = type;
1859 req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
1862 * If we found canonical names or domain, use them instead of
1863 * the existing values.
1865 if (name != NULL) {
1866 free(req->id1name);
1867 req->id1name = name;
1869 if (domain != NULL) {
1870 free(req->id1domain);
1871 req->id1domain = domain;
1874 if (req->id1.idmap_id_u.sid.prefix == NULL) {
1875 req->id1.idmap_id_u.sid.prefix = sidprefix;
1876 req->id1.idmap_id_u.sid.rid = rid;
1879 TRACE(req, res, "Found in name cache");
1880 return (retcode);
1885 static int
1886 ad_lookup_batch_int(lookup_state_t *state, idmap_mapping_batch *batch,
1887 idmap_ids_res *result, adutils_ad_t *dir, int how_local,
1888 int *num_processed)
1890 idmap_retcode retcode;
1891 int i, num_queued, is_wuser, is_user;
1892 int next_request;
1893 int retries = 0, esidtype;
1894 char **unixname;
1895 idmap_mapping *req;
1896 idmap_id_res *res;
1897 idmap_query_state_t *qs = NULL;
1898 idmap_how *how;
1899 char **dn, **attr, **value;
1901 *num_processed = 0;
1904 * Since req->id2.idtype is unused, we will use it here
1905 * to retrieve the value of sid_type. But it needs to be
1906 * reset to IDMAP_NONE before we return to prevent xdr
1907 * from mis-interpreting req->id2 when it tries to free
1908 * the input argument. Other option is to allocate an
1909 * array of integers and use it instead for the batched
1910 * call. But why un-necessarily allocate memory. That may
1911 * be an option if req->id2.idtype cannot be re-used in
1912 * future.
1914 * Similarly, we use req->id2.idmap_id_u.uid to return
1915 * uidNumber or gidNumber supplied by IDMU, and reset it
1916 * back to IDMAP_SENTINEL_PID when we're done. Note that
1917 * the query always puts the result in req->id2.idmap_id_u.uid,
1918 * not .gid.
1920 retry:
1921 retcode = idmap_lookup_batch_start(dir, state->ad_nqueries,
1922 state->directory_based_mapping,
1923 state->defdom,
1924 &qs);
1925 if (retcode != IDMAP_SUCCESS) {
1926 if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
1927 retries++ < ADUTILS_DEF_NUM_RETRIES)
1928 goto retry;
1929 degrade_svc(1, "failed to create batch for AD lookup");
1930 goto out;
1932 num_queued = 0;
1934 restore_svc();
1936 if (how_local & FOREST_IS_LOCAL) {
1938 * Directory based name mapping is only performed within the
1939 * joined forest. We don't trust other "trusted"
1940 * forests to provide DS-based name mapping information because
1941 * AD's definition of "cross-forest trust" does not encompass
1942 * this sort of behavior.
1944 idmap_lookup_batch_set_unixattr(qs,
1945 state->ad_unixuser_attr, state->ad_unixgroup_attr);
1948 for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
1949 req = &batch->idmap_mapping_batch_val[i];
1950 res = &result->ids.ids_val[i];
1951 how = &res->info.how;
1953 retcode = IDMAP_SUCCESS;
1954 req->id2.idtype = IDMAP_NONE;
1955 req->id2.idmap_id_u.uid = IDMAP_SENTINEL_PID;
1957 /* Skip if no AD lookup required */
1958 if (!(req->direction & _IDMAP_F_LOOKUP_AD))
1959 continue;
1961 /* Skip if we've already tried and gotten a "not found" */
1962 if (req->direction & _IDMAP_F_LOOKUP_OTHER_AD)
1963 continue;
1965 /* Skip if we've already either succeeded or failed */
1966 if (res->retcode != IDMAP_ERR_RETRIABLE_NET_ERR)
1967 continue;
1969 if (IS_ID_SID(req->id1)) {
1971 /* win2unix request: */
1973 posix_id_t *pid = NULL;
1974 unixname = dn = attr = value = NULL;
1975 esidtype = IDMAP_SID;
1976 if (state->directory_based_mapping ==
1977 DIRECTORY_MAPPING_NAME &&
1978 req->id2name == NULL) {
1979 if (res->id.idtype == IDMAP_UID &&
1980 AD_OR_MIXED(state->nm_siduid)) {
1981 esidtype = IDMAP_USID;
1982 unixname = &req->id2name;
1983 } else if (res->id.idtype == IDMAP_GID &&
1984 AD_OR_MIXED(state->nm_sidgid)) {
1985 esidtype = IDMAP_GSID;
1986 unixname = &req->id2name;
1987 } else if (AD_OR_MIXED(state->nm_siduid) ||
1988 AD_OR_MIXED(state->nm_sidgid)) {
1989 unixname = &req->id2name;
1992 if (unixname != NULL) {
1994 * Get how info for DS-based name
1995 * mapping only if AD or MIXED
1996 * mode is enabled.
1998 idmap_how_clear(&res->info.how);
1999 res->info.src = IDMAP_MAP_SRC_NEW;
2000 how->map_type = IDMAP_MAP_TYPE_DS_AD;
2001 dn = &how->idmap_how_u.ad.dn;
2002 attr = &how->idmap_how_u.ad.attr;
2003 value = &how->idmap_how_u.ad.value;
2005 } else if (state->directory_based_mapping ==
2006 DIRECTORY_MAPPING_IDMU &&
2007 (how_local & DOMAIN_IS_LOCAL)) {
2009 * Ensure that we only do IDMU processing
2010 * when querying the domain we've joined.
2012 pid = &req->id2.idmap_id_u.uid;
2014 * Get how info for IDMU based mapping.
2016 idmap_how_clear(&res->info.how);
2017 res->info.src = IDMAP_MAP_SRC_NEW;
2018 how->map_type = IDMAP_MAP_TYPE_IDMU;
2019 dn = &how->idmap_how_u.idmu.dn;
2020 attr = &how->idmap_how_u.idmu.attr;
2021 value = &how->idmap_how_u.idmu.value;
2024 if (req->id1.idmap_id_u.sid.prefix != NULL) {
2025 /* Lookup AD by SID */
2026 retcode = idmap_sid2name_batch_add1(
2027 qs, req->id1.idmap_id_u.sid.prefix,
2028 &req->id1.idmap_id_u.sid.rid, esidtype,
2029 dn, attr, value,
2030 (req->id1name == NULL) ?
2031 &req->id1name : NULL,
2032 (req->id1domain == NULL) ?
2033 &req->id1domain : NULL,
2034 &req->id2.idtype, unixname,
2035 pid,
2036 &res->retcode);
2037 if (retcode == IDMAP_SUCCESS)
2038 num_queued++;
2039 } else {
2040 /* Lookup AD by winname */
2041 assert(req->id1name != NULL);
2042 retcode = idmap_name2sid_batch_add1(
2043 qs, req->id1name, req->id1domain,
2044 esidtype,
2045 dn, attr, value,
2046 &req->id1name,
2047 &req->id1.idmap_id_u.sid.prefix,
2048 &req->id1.idmap_id_u.sid.rid,
2049 &req->id2.idtype, unixname,
2050 pid,
2051 &res->retcode);
2052 if (retcode == IDMAP_SUCCESS)
2053 num_queued++;
2056 } else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
2058 /* unix2win request: */
2060 if (res->id.idmap_id_u.sid.prefix != NULL &&
2061 req->id2name != NULL) {
2062 /* Already have SID and winname. done */
2063 res->retcode = IDMAP_SUCCESS;
2064 continue;
2067 if (res->id.idmap_id_u.sid.prefix != NULL) {
2069 * SID but no winname -- lookup AD by
2070 * SID to get winname.
2071 * how info is not needed here because
2072 * we are not retrieving unixname from
2073 * AD.
2076 retcode = idmap_sid2name_batch_add1(
2077 qs, res->id.idmap_id_u.sid.prefix,
2078 &res->id.idmap_id_u.sid.rid,
2079 IDMAP_POSIXID,
2080 NULL, NULL, NULL,
2081 &req->id2name,
2082 &req->id2domain, &req->id2.idtype,
2083 NULL, NULL, &res->retcode);
2084 if (retcode == IDMAP_SUCCESS)
2085 num_queued++;
2086 } else if (req->id2name != NULL) {
2088 * winname but no SID -- lookup AD by
2089 * winname to get SID.
2090 * how info is not needed here because
2091 * we are not retrieving unixname from
2092 * AD.
2094 retcode = idmap_name2sid_batch_add1(
2095 qs, req->id2name, req->id2domain,
2096 IDMAP_POSIXID,
2097 NULL, NULL, NULL, NULL,
2098 &res->id.idmap_id_u.sid.prefix,
2099 &res->id.idmap_id_u.sid.rid,
2100 &req->id2.idtype, NULL,
2101 NULL,
2102 &res->retcode);
2103 if (retcode == IDMAP_SUCCESS)
2104 num_queued++;
2105 } else if (state->directory_based_mapping ==
2106 DIRECTORY_MAPPING_IDMU &&
2107 (how_local & DOMAIN_IS_LOCAL)) {
2108 assert(req->id1.idmap_id_u.uid !=
2109 IDMAP_SENTINEL_PID);
2110 is_user = IS_ID_UID(req->id1);
2111 if (res->id.idtype == IDMAP_USID)
2112 is_wuser = 1;
2113 else if (res->id.idtype == IDMAP_GSID)
2114 is_wuser = 0;
2115 else
2116 is_wuser = is_user;
2118 /* IDMU can't do diagonal mappings */
2119 if (is_user != is_wuser)
2120 continue;
2122 idmap_how_clear(&res->info.how);
2123 res->info.src = IDMAP_MAP_SRC_NEW;
2124 how->map_type = IDMAP_MAP_TYPE_IDMU;
2125 retcode = idmap_pid2sid_batch_add1(
2126 qs, req->id1.idmap_id_u.uid, is_user,
2127 &how->idmap_how_u.ad.dn,
2128 &how->idmap_how_u.ad.attr,
2129 &how->idmap_how_u.ad.value,
2130 &res->id.idmap_id_u.sid.prefix,
2131 &res->id.idmap_id_u.sid.rid,
2132 &req->id2name, &req->id2domain,
2133 &req->id2.idtype, &res->retcode);
2134 if (retcode == IDMAP_SUCCESS)
2135 num_queued++;
2136 } else if (req->id1name != NULL) {
2138 * No SID and no winname but we've unixname.
2139 * Lookup AD by unixname to get SID.
2141 is_user = (IS_ID_UID(req->id1)) ? 1 : 0;
2142 if (res->id.idtype == IDMAP_USID)
2143 is_wuser = 1;
2144 else if (res->id.idtype == IDMAP_GSID)
2145 is_wuser = 0;
2146 else
2147 is_wuser = is_user;
2149 idmap_how_clear(&res->info.how);
2150 res->info.src = IDMAP_MAP_SRC_NEW;
2151 how->map_type = IDMAP_MAP_TYPE_DS_AD;
2152 retcode = idmap_unixname2sid_batch_add1(
2153 qs, req->id1name, is_user, is_wuser,
2154 &how->idmap_how_u.ad.dn,
2155 &how->idmap_how_u.ad.attr,
2156 &how->idmap_how_u.ad.value,
2157 &res->id.idmap_id_u.sid.prefix,
2158 &res->id.idmap_id_u.sid.rid,
2159 &req->id2name, &req->id2domain,
2160 &req->id2.idtype, &res->retcode);
2161 if (retcode == IDMAP_SUCCESS)
2162 num_queued++;
2166 if (retcode == IDMAP_ERR_DOMAIN_NOTFOUND) {
2167 req->direction |= _IDMAP_F_LOOKUP_OTHER_AD;
2168 retcode = IDMAP_SUCCESS;
2169 } else if (retcode != IDMAP_SUCCESS) {
2170 break;
2172 } /* End of for loop */
2174 if (retcode == IDMAP_SUCCESS) {
2175 /* add keeps track if we added an entry to the batch */
2176 if (num_queued > 0)
2177 retcode = idmap_lookup_batch_end(&qs);
2178 else
2179 idmap_lookup_release_batch(&qs);
2180 } else {
2181 idmap_lookup_release_batch(&qs);
2182 num_queued = 0;
2183 next_request = i + 1;
2186 if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
2187 retries++ < ADUTILS_DEF_NUM_RETRIES)
2188 goto retry;
2189 else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR)
2190 degrade_svc(1, "some AD lookups timed out repeatedly");
2192 if (retcode != IDMAP_SUCCESS) {
2193 /* Mark any unproccessed requests for an other AD */
2194 for (i = next_request; i < batch->idmap_mapping_batch_len;
2195 i++) {
2196 req = &batch->idmap_mapping_batch_val[i];
2197 req->direction |= _IDMAP_F_LOOKUP_OTHER_AD;
2202 if (retcode != IDMAP_SUCCESS)
2203 idmapdlog(LOG_NOTICE, "Failed to batch AD lookup requests");
2205 out:
2207 * This loop does the following:
2208 * 1. Reset _IDMAP_F_LOOKUP_AD flag from the request.
2209 * 2. Reset req->id2.idtype to IDMAP_NONE
2210 * 3. If batch_start or batch_add failed then set the status
2211 * of each request marked for AD lookup to that error.
2212 * 4. Evaluate the type of the AD object (i.e. user or group)
2213 * and update the idtype in request.
2215 for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
2216 idmap_id_type type;
2217 uid_t posix_id;
2219 req = &batch->idmap_mapping_batch_val[i];
2220 type = req->id2.idtype;
2221 req->id2.idtype = IDMAP_NONE;
2222 posix_id = req->id2.idmap_id_u.uid;
2223 req->id2.idmap_id_u.uid = IDMAP_SENTINEL_PID;
2224 res = &result->ids.ids_val[i];
2227 * If it didn't need AD lookup, ignore it.
2229 if (!(req->direction & _IDMAP_F_LOOKUP_AD))
2230 continue;
2233 * If we deferred it this time, reset for the next
2234 * AD server.
2236 if (req->direction & _IDMAP_F_LOOKUP_OTHER_AD) {
2237 req->direction &= ~_IDMAP_F_LOOKUP_OTHER_AD;
2238 continue;
2241 /* Count number processed */
2242 (*num_processed)++;
2244 /* Reset AD lookup flag */
2245 req->direction &= ~(_IDMAP_F_LOOKUP_AD);
2248 * If batch_start or batch_add failed then set the
2249 * status of each request marked for AD lookup to
2250 * that error.
2252 if (retcode != IDMAP_SUCCESS) {
2253 res->retcode = retcode;
2254 continue;
2257 if (res->retcode == IDMAP_ERR_NOTFOUND) {
2258 /* Nothing found - remove the preset info */
2259 idmap_how_clear(&res->info.how);
2262 if (IS_ID_SID(req->id1)) {
2263 if (res->retcode == IDMAP_ERR_NOTFOUND) {
2264 TRACE(req, res, "Not found in AD");
2265 continue;
2267 if (res->retcode != IDMAP_SUCCESS) {
2268 TRACE(req, res, "AD lookup error=%d",
2269 res->retcode);
2270 continue;
2272 /* Evaluate result type */
2273 switch (type) {
2274 case IDMAP_USID:
2275 if (res->id.idtype == IDMAP_POSIXID)
2276 res->id.idtype = IDMAP_UID;
2278 * We found a user. If we got information
2279 * from IDMU and we were expecting a user,
2280 * copy the id.
2282 if (posix_id != IDMAP_SENTINEL_PID &&
2283 res->id.idtype == IDMAP_UID) {
2284 res->id.idmap_id_u.uid = posix_id;
2285 res->direction = IDMAP_DIRECTION_BI;
2286 res->info.how.map_type =
2287 IDMAP_MAP_TYPE_IDMU;
2288 res->info.src = IDMAP_MAP_SRC_NEW;
2290 req->id1.idtype = IDMAP_USID;
2291 break;
2293 case IDMAP_GSID:
2294 if (res->id.idtype == IDMAP_POSIXID)
2295 res->id.idtype = IDMAP_GID;
2297 * We found a group. If we got information
2298 * from IDMU and we were expecting a group,
2299 * copy the id.
2301 if (posix_id != IDMAP_SENTINEL_PID &&
2302 res->id.idtype == IDMAP_GID) {
2303 res->id.idmap_id_u.gid = posix_id;
2304 res->direction = IDMAP_DIRECTION_BI;
2305 res->info.how.map_type =
2306 IDMAP_MAP_TYPE_IDMU;
2307 res->info.src = IDMAP_MAP_SRC_NEW;
2309 req->id1.idtype = IDMAP_GSID;
2310 break;
2312 default:
2313 res->retcode = IDMAP_ERR_SID;
2314 break;
2316 TRACE(req, res, "Found in AD");
2317 if (res->retcode == IDMAP_SUCCESS &&
2318 req->id1name != NULL &&
2319 (req->id2name == NULL ||
2320 res->id.idmap_id_u.uid == IDMAP_SENTINEL_PID) &&
2321 NLDAP_MODE(res->id.idtype, state)) {
2322 req->direction |= _IDMAP_F_LOOKUP_NLDAP;
2323 state->nldap_nqueries++;
2325 } else if (IS_ID_UID(req->id1) || IS_ID_GID(req->id1)) {
2326 if (res->retcode != IDMAP_SUCCESS) {
2327 if ((!(IDMAP_FATAL_ERROR(res->retcode))) &&
2328 res->id.idmap_id_u.sid.prefix == NULL &&
2329 req->id2name == NULL) {
2331 * If AD lookup by unixname or pid
2332 * failed with non fatal error
2333 * then clear the error (ie set
2334 * res->retcode to success).
2335 * This allows the next pass to
2336 * process other mapping
2337 * mechanisms for this request.
2339 if (res->retcode ==
2340 IDMAP_ERR_NOTFOUND) {
2341 /* This is not an error */
2342 res->retcode = IDMAP_SUCCESS;
2343 TRACE(req, res,
2344 "Not found in AD");
2345 } else {
2346 TRACE(req, res,
2347 "AD lookup error (ignored)");
2348 res->retcode = IDMAP_SUCCESS;
2350 } else {
2351 TRACE(req, res, "AD lookup error");
2353 continue;
2355 /* Evaluate result type */
2356 switch (type) {
2357 case IDMAP_USID:
2358 case IDMAP_GSID:
2359 if (res->id.idtype == IDMAP_SID)
2360 res->id.idtype = type;
2361 break;
2363 default:
2364 res->retcode = IDMAP_ERR_SID;
2365 break;
2367 TRACE(req, res, "Found in AD");
2371 return (retcode);
2377 * Batch AD lookups
2379 idmap_retcode
2380 ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch,
2381 idmap_ids_res *result)
2383 idmap_retcode retcode;
2384 int i, j;
2385 idmap_mapping *req;
2386 idmap_id_res *res;
2387 int num_queries;
2388 int num_processed;
2390 if (state->ad_nqueries == 0)
2391 return (IDMAP_SUCCESS);
2393 for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
2394 req = &batch->idmap_mapping_batch_val[i];
2395 res = &result->ids.ids_val[i];
2397 /* Skip if not marked for AD lookup or already in error. */
2398 if (!(req->direction & _IDMAP_F_LOOKUP_AD) ||
2399 res->retcode != IDMAP_SUCCESS)
2400 continue;
2402 /* Init status */
2403 res->retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
2406 RDLOCK_CONFIG();
2407 num_queries = state->ad_nqueries;
2409 if (_idmapdstate.num_gcs == 0 && _idmapdstate.num_dcs == 0) {
2410 /* Case of no ADs */
2411 retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY;
2412 for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
2413 req = &batch->idmap_mapping_batch_val[i];
2414 res = &result->ids.ids_val[i];
2415 if (!(req->direction & _IDMAP_F_LOOKUP_AD))
2416 continue;
2417 req->direction &= ~(_IDMAP_F_LOOKUP_AD);
2418 res->retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY;
2420 goto out;
2423 if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU) {
2424 for (i = 0; i < _idmapdstate.num_dcs && num_queries > 0; i++) {
2426 retcode = ad_lookup_batch_int(state, batch,
2427 result, _idmapdstate.dcs[i],
2428 i == 0 ? DOMAIN_IS_LOCAL|FOREST_IS_LOCAL : 0,
2429 &num_processed);
2430 num_queries -= num_processed;
2435 for (i = 0; i < _idmapdstate.num_gcs && num_queries > 0; i++) {
2437 retcode = ad_lookup_batch_int(state, batch, result,
2438 _idmapdstate.gcs[i],
2439 i == 0 ? FOREST_IS_LOCAL : 0,
2440 &num_processed);
2441 num_queries -= num_processed;
2446 * There are no more ADs to try. Return errors for any
2447 * remaining requests.
2449 if (num_queries > 0) {
2450 for (j = 0; j < batch->idmap_mapping_batch_len; j++) {
2451 req = &batch->idmap_mapping_batch_val[j];
2452 res = &result->ids.ids_val[j];
2453 if (!(req->direction & _IDMAP_F_LOOKUP_AD))
2454 continue;
2455 req->direction &= ~(_IDMAP_F_LOOKUP_AD);
2456 res->retcode = IDMAP_ERR_DOMAIN_NOTFOUND;
2460 out:
2461 UNLOCK_CONFIG();
2463 /* AD lookups done. Reset state->ad_nqueries and return */
2464 state->ad_nqueries = 0;
2465 return (retcode);
2469 * Convention when processing win2unix requests:
2471 * Windows identity:
2472 * req->id1name =
2473 * winname if given otherwise winname found will be placed
2474 * here.
2475 * req->id1domain =
2476 * windomain if given otherwise windomain found will be
2477 * placed here.
2478 * req->id1.idtype =
2479 * Either IDMAP_SID/USID/GSID. If this is IDMAP_SID then it'll
2480 * be set to IDMAP_USID/GSID depending upon whether the
2481 * given SID is user or group respectively. The user/group-ness
2482 * is determined either when looking up well-known SIDs table OR
2483 * if the SID is found in namecache OR by ad_lookup_batch().
2484 * req->id1..sid.[prefix, rid] =
2485 * SID if given otherwise SID found will be placed here.
2487 * Unix identity:
2488 * req->id2name =
2489 * unixname found will be placed here.
2490 * req->id2domain =
2491 * NOT USED
2492 * res->id.idtype =
2493 * Target type initialized from req->id2.idtype. If
2494 * it is IDMAP_POSIXID then actual type (IDMAP_UID/GID) found
2495 * will be placed here.
2496 * res->id..[uid or gid] =
2497 * UID/GID found will be placed here.
2499 * Others:
2500 * res->retcode =
2501 * Return status for this request will be placed here.
2502 * res->direction =
2503 * Direction found will be placed here. Direction
2504 * meaning whether the resultant mapping is valid
2505 * only from win2unix or bi-directional.
2506 * req->direction =
2507 * INTERNAL USE. Used by idmapd to set various
2508 * flags (_IDMAP_F_xxxx) to aid in processing
2509 * of the request.
2510 * req->id2.idtype =
2511 * INTERNAL USE. Initially this is the requested target
2512 * type and is used to initialize res->id.idtype.
2513 * ad_lookup_batch() uses this field temporarily to store
2514 * sid_type obtained by the batched AD lookups and after
2515 * use resets it to IDMAP_NONE to prevent xdr from
2516 * mis-interpreting the contents of req->id2.
2517 * req->id2.idmap_id_u.uid =
2518 * INTERNAL USE. If the AD lookup finds IDMU data
2519 * (uidNumber or gidNumber, depending on the type of
2520 * the entry), it's left here.
2524 * This function does the following:
2525 * 1. Lookup well-known SIDs table.
2526 * 2. Check if the given SID is a local-SID and if so extract UID/GID from it.
2527 * 3. Lookup cache.
2528 * 4. Check if the client does not want new mapping to be allocated
2529 * in which case this pass is the final pass.
2530 * 5. Set AD lookup flag if it determines that the next stage needs
2531 * to do AD lookup.
2533 idmap_retcode
2534 sid2pid_first_pass(lookup_state_t *state, idmap_mapping *req,
2535 idmap_id_res *res)
2537 idmap_retcode retcode;
2538 int wksid;
2540 /* Initialize result */
2541 res->id.idtype = req->id2.idtype;
2542 res->id.idmap_id_u.uid = IDMAP_SENTINEL_PID;
2543 res->direction = IDMAP_DIRECTION_UNDEF;
2544 wksid = 0;
2546 if (EMPTY_STRING(req->id1.idmap_id_u.sid.prefix)) {
2547 /* They have to give us *something* to work with! */
2548 if (req->id1name == NULL) {
2549 retcode = IDMAP_ERR_ARG;
2550 goto out;
2553 /* sanitize sidprefix */
2554 free(req->id1.idmap_id_u.sid.prefix);
2555 req->id1.idmap_id_u.sid.prefix = NULL;
2557 /* Allow for a fully-qualified name in the "name" parameter */
2558 if (req->id1domain == NULL) {
2559 char *p;
2560 p = strchr(req->id1name, '@');
2561 if (p != NULL) {
2562 char *q;
2563 q = req->id1name;
2564 req->id1name = uu_strndup(q, p - req->id1name);
2565 req->id1domain = strdup(p+1);
2566 free(q);
2567 if (req->id1name == NULL ||
2568 req->id1domain == NULL) {
2569 retcode = IDMAP_ERR_MEMORY;
2570 goto out;
2576 /* Lookup well-known SIDs table */
2577 retcode = lookup_wksids_sid2pid(req, res, &wksid);
2578 if (retcode == IDMAP_SUCCESS) {
2579 /* Found a well-known account with a hardwired mapping */
2580 TRACE(req, res, "Hardwired mapping");
2581 goto out;
2582 } else if (retcode != IDMAP_ERR_NOTFOUND) {
2583 TRACE(req, res,
2584 "Well-known account lookup failed, code %d", retcode);
2585 goto out;
2588 if (wksid) {
2589 /* Found a well-known account, but no mapping */
2590 TRACE(req, res, "Well-known account");
2591 } else {
2592 TRACE(req, res, "Not a well-known account");
2594 /* Check if this is a localsid */
2595 retcode = lookup_localsid2pid(req, res);
2596 if (retcode == IDMAP_SUCCESS) {
2597 TRACE(req, res, "Local SID");
2598 goto out;
2599 } else if (retcode != IDMAP_ERR_NOTFOUND) {
2600 TRACE(req, res,
2601 "Local SID lookup error=%d", retcode);
2602 goto out;
2604 TRACE(req, res, "Not a local SID");
2606 if (ALLOW_WK_OR_LOCAL_SIDS_ONLY(req)) {
2607 retcode = IDMAP_ERR_NONE_GENERATED;
2608 goto out;
2613 * If this is a name-based request and we don't have a domain,
2614 * use the default domain. Note that the well-known identity
2615 * cases will have supplied a SID prefix already, and that we
2616 * don't (yet?) support looking up a local user through a Windows
2617 * style name.
2619 if (req->id1.idmap_id_u.sid.prefix == NULL &&
2620 req->id1name != NULL && req->id1domain == NULL) {
2621 if (state->defdom == NULL) {
2622 retcode = IDMAP_ERR_DOMAIN_NOTFOUND;
2623 goto out;
2625 req->id1domain = strdup(state->defdom);
2626 if (req->id1domain == NULL) {
2627 retcode = IDMAP_ERR_MEMORY;
2628 goto out;
2630 TRACE(req, res, "Added default domain");
2633 /* Lookup cache */
2634 retcode = lookup_cache_sid2pid(state->cache, req, res);
2635 if (retcode == IDMAP_SUCCESS) {
2636 TRACE(req, res, "Found in mapping cache");
2637 goto out;
2638 } else if (retcode != IDMAP_ERR_NOTFOUND) {
2639 TRACE(req, res, "Mapping cache lookup error=%d", retcode);
2640 goto out;
2642 TRACE(req, res, "Not found in mapping cache");
2644 if (DO_NOT_ALLOC_NEW_ID_MAPPING(req) || AVOID_NAMESERVICE(req)) {
2645 retcode = IDMAP_ERR_NONE_GENERATED;
2646 goto out;
2650 * Failed to find non-expired entry in cache. Next step is
2651 * to determine if this request needs to be batched for AD lookup.
2653 * At this point we have either sid or winname or both. If we don't
2654 * have both then lookup name_cache for the sid or winname
2655 * whichever is missing. If not found then this request will be
2656 * batched for AD lookup.
2658 retcode = lookup_name_cache(state->cache, req, res);
2659 if (retcode == IDMAP_SUCCESS) {
2660 if (res->id.idtype == IDMAP_POSIXID) {
2661 if (req->id1.idtype == IDMAP_USID)
2662 res->id.idtype = IDMAP_UID;
2663 else
2664 res->id.idtype = IDMAP_GID;
2666 } else if (retcode != IDMAP_ERR_NOTFOUND)
2667 goto out;
2669 if (_idmapdstate.cfg->pgcfg.use_lsa &&
2670 _idmapdstate.cfg->pgcfg.domain_name != NULL) {
2672 * If we don't have both name and SID, try looking up the
2673 * entry with LSA.
2675 if (req->id1.idmap_id_u.sid.prefix != NULL &&
2676 req->id1name == NULL) {
2678 retcode = lookup_lsa_by_sid(
2679 req->id1.idmap_id_u.sid.prefix,
2680 req->id1.idmap_id_u.sid.rid,
2681 &req->id1name, &req->id1domain, &req->id1.idtype);
2682 if (retcode == IDMAP_SUCCESS) {
2683 TRACE(req, res, "Found with LSA");
2684 } else if (retcode == IDMAP_ERR_NOTFOUND) {
2685 TRACE(req, res, "Not found with LSA");
2686 } else {
2687 TRACE(req, res, "LSA error %d", retcode);
2688 goto out;
2691 } else if (req->id1name != NULL &&
2692 req->id1.idmap_id_u.sid.prefix == NULL) {
2693 char *canonname;
2694 char *canondomain;
2696 retcode = lookup_lsa_by_name(
2697 req->id1name, req->id1domain,
2698 &req->id1.idmap_id_u.sid.prefix,
2699 &req->id1.idmap_id_u.sid.rid,
2700 &canonname, &canondomain,
2701 &req->id1.idtype);
2702 if (retcode == IDMAP_SUCCESS) {
2703 free(req->id1name);
2704 req->id1name = canonname;
2705 free(req->id1domain);
2706 req->id1domain = canondomain;
2707 TRACE(req, res, "Found with LSA");
2708 } else if (retcode == IDMAP_ERR_NOTFOUND) {
2709 TRACE(req, res, "Not found with LSA");
2710 } else {
2711 TRACE(req, res, "LSA error %d", retcode);
2712 goto out;
2718 * Set the flag to indicate that we are not done yet so that
2719 * subsequent passes considers this request for name-based
2720 * mapping and ephemeral mapping.
2722 state->sid2pid_done = FALSE;
2723 req->direction |= _IDMAP_F_NOTDONE;
2726 * Even if we have both sid and winname, we still may need to batch
2727 * this request for AD lookup if we don't have unixname and
2728 * directory-based name mapping (AD or mixed) is enabled.
2729 * We avoid AD lookup for well-known SIDs because they don't have
2730 * regular AD objects.
2732 if (retcode != IDMAP_SUCCESS ||
2733 (!wksid && req->id2name == NULL &&
2734 AD_OR_MIXED_MODE(res->id.idtype, state)) ||
2735 (!wksid && res->id.idmap_id_u.uid == IDMAP_SENTINEL_PID &&
2736 state->directory_based_mapping == DIRECTORY_MAPPING_IDMU)) {
2737 retcode = IDMAP_SUCCESS;
2738 req->direction |= _IDMAP_F_LOOKUP_AD;
2739 state->ad_nqueries++;
2740 } else if (NLDAP_MODE(res->id.idtype, state)) {
2741 req->direction |= _IDMAP_F_LOOKUP_NLDAP;
2742 state->nldap_nqueries++;
2746 out:
2747 res->retcode = idmap_stat4prot(retcode);
2749 * If we are done and there was an error then set fallback pid
2750 * in the result.
2752 if (ARE_WE_DONE(req->direction) && res->retcode != IDMAP_SUCCESS)
2753 res->id.idmap_id_u.uid = UID_NOBODY;
2754 return (retcode);
2758 * Generate SID using the following convention
2759 * <machine-sid-prefix>-<1000 + uid>
2760 * <machine-sid-prefix>-<2^31 + gid>
2762 static
2763 idmap_retcode
2764 generate_localsid(idmap_mapping *req, idmap_id_res *res, int is_user,
2765 int fallback)
2767 free(res->id.idmap_id_u.sid.prefix);
2768 res->id.idmap_id_u.sid.prefix = NULL;
2771 * Diagonal mapping for localSIDs not supported because of the
2772 * way we generate localSIDs.
2774 if (is_user && res->id.idtype == IDMAP_GSID)
2775 return (IDMAP_ERR_NOTGROUP);
2776 if (!is_user && res->id.idtype == IDMAP_USID)
2777 return (IDMAP_ERR_NOTUSER);
2779 /* Skip 1000 UIDs */
2780 if (is_user &&
2781 req->id1.idmap_id_u.uid + LOCALRID_UID_MIN > LOCALRID_UID_MAX)
2782 return (IDMAP_ERR_NOMAPPING);
2784 RDLOCK_CONFIG();
2786 * machine_sid is never NULL because if it is we won't be here.
2787 * No need to assert because strdup(NULL) will core anyways.
2789 res->id.idmap_id_u.sid.prefix =
2790 strdup(_idmapdstate.cfg->pgcfg.machine_sid);
2791 if (res->id.idmap_id_u.sid.prefix == NULL) {
2792 UNLOCK_CONFIG();
2793 idmapdlog(LOG_ERR, "Out of memory");
2794 return (IDMAP_ERR_MEMORY);
2796 UNLOCK_CONFIG();
2797 res->id.idmap_id_u.sid.rid =
2798 (is_user) ? req->id1.idmap_id_u.uid + LOCALRID_UID_MIN :
2799 req->id1.idmap_id_u.gid + LOCALRID_GID_MIN;
2800 res->direction = IDMAP_DIRECTION_BI;
2801 if (res->id.idtype == IDMAP_SID)
2802 res->id.idtype = is_user ? IDMAP_USID : IDMAP_GSID;
2804 if (!fallback) {
2805 res->info.how.map_type = IDMAP_MAP_TYPE_LOCAL_SID;
2806 res->info.src = IDMAP_MAP_SRC_ALGORITHMIC;
2810 * Don't update name_cache because local sids don't have
2811 * valid windows names.
2813 req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
2814 return (IDMAP_SUCCESS);
2817 static
2818 idmap_retcode
2819 lookup_localsid2pid(idmap_mapping *req, idmap_id_res *res)
2821 char *sidprefix;
2822 uint32_t rid;
2823 int s;
2826 * If the sidprefix == localsid then UID = last RID - 1000 or
2827 * GID = last RID - 2^31.
2829 if ((sidprefix = req->id1.idmap_id_u.sid.prefix) == NULL)
2830 /* This means we are looking up by winname */
2831 return (IDMAP_ERR_NOTFOUND);
2832 rid = req->id1.idmap_id_u.sid.rid;
2834 RDLOCK_CONFIG();
2835 s = (_idmapdstate.cfg->pgcfg.machine_sid) ?
2836 strcasecmp(sidprefix, _idmapdstate.cfg->pgcfg.machine_sid) : 1;
2837 UNLOCK_CONFIG();
2840 * If the given sidprefix does not match machine_sid then this is
2841 * not a local SID.
2843 if (s != 0)
2844 return (IDMAP_ERR_NOTFOUND);
2846 switch (res->id.idtype) {
2847 case IDMAP_UID:
2848 if (rid < LOCALRID_UID_MIN || rid > LOCALRID_UID_MAX)
2849 return (IDMAP_ERR_ARG);
2850 res->id.idmap_id_u.uid = rid - LOCALRID_UID_MIN;
2851 break;
2852 case IDMAP_GID:
2853 if (rid < LOCALRID_GID_MIN)
2854 return (IDMAP_ERR_ARG);
2855 res->id.idmap_id_u.gid = rid - LOCALRID_GID_MIN;
2856 break;
2857 case IDMAP_POSIXID:
2858 if (rid >= LOCALRID_GID_MIN) {
2859 res->id.idmap_id_u.gid = rid - LOCALRID_GID_MIN;
2860 res->id.idtype = IDMAP_GID;
2861 } else if (rid >= LOCALRID_UID_MIN) {
2862 res->id.idmap_id_u.uid = rid - LOCALRID_UID_MIN;
2863 res->id.idtype = IDMAP_UID;
2864 } else {
2865 return (IDMAP_ERR_ARG);
2867 break;
2868 default:
2869 return (IDMAP_ERR_NOTSUPPORTED);
2871 res->info.how.map_type = IDMAP_MAP_TYPE_LOCAL_SID;
2872 res->info.src = IDMAP_MAP_SRC_ALGORITHMIC;
2873 return (IDMAP_SUCCESS);
2877 * Name service lookup by unixname to get pid
2879 static
2880 idmap_retcode
2881 ns_lookup_byname(const char *name, const char *lower_name, idmap_id *id)
2883 struct passwd pwd, *pwdp;
2884 struct group grp, *grpp;
2885 char *buf;
2886 static size_t pwdbufsiz = 0;
2887 static size_t grpbufsiz = 0;
2889 switch (id->idtype) {
2890 case IDMAP_UID:
2891 if (pwdbufsiz == 0)
2892 pwdbufsiz = sysconf(_SC_GETPW_R_SIZE_MAX);
2893 buf = alloca(pwdbufsiz);
2894 pwdp = getpwnam_r(name, &pwd, buf, pwdbufsiz);
2895 if (pwdp == NULL && errno == 0 && lower_name != NULL &&
2896 name != lower_name && strcmp(name, lower_name) != 0)
2897 pwdp = getpwnam_r(lower_name, &pwd, buf, pwdbufsiz);
2898 if (pwdp == NULL) {
2899 if (errno == 0)
2900 return (IDMAP_ERR_NOTFOUND);
2901 else
2902 return (IDMAP_ERR_INTERNAL);
2904 id->idmap_id_u.uid = pwd.pw_uid;
2905 break;
2906 case IDMAP_GID:
2907 if (grpbufsiz == 0)
2908 grpbufsiz = sysconf(_SC_GETGR_R_SIZE_MAX);
2909 buf = alloca(grpbufsiz);
2910 grpp = getgrnam_r(name, &grp, buf, grpbufsiz);
2911 if (grpp == NULL && errno == 0 && lower_name != NULL &&
2912 name != lower_name && strcmp(name, lower_name) != 0)
2913 grpp = getgrnam_r(lower_name, &grp, buf, grpbufsiz);
2914 if (grpp == NULL) {
2915 if (errno == 0)
2916 return (IDMAP_ERR_NOTFOUND);
2917 else
2918 return (IDMAP_ERR_INTERNAL);
2920 id->idmap_id_u.gid = grp.gr_gid;
2921 break;
2922 default:
2923 return (IDMAP_ERR_ARG);
2925 return (IDMAP_SUCCESS);
2930 * Name service lookup by pid to get unixname
2932 static
2933 idmap_retcode
2934 ns_lookup_bypid(uid_t pid, int is_user, char **unixname)
2936 struct passwd pwd;
2937 struct group grp;
2938 char *buf;
2939 static size_t pwdbufsiz = 0;
2940 static size_t grpbufsiz = 0;
2942 if (is_user) {
2943 if (pwdbufsiz == 0)
2944 pwdbufsiz = sysconf(_SC_GETPW_R_SIZE_MAX);
2945 buf = alloca(pwdbufsiz);
2946 errno = 0;
2947 if (getpwuid_r(pid, &pwd, buf, pwdbufsiz) == NULL) {
2948 if (errno == 0)
2949 return (IDMAP_ERR_NOTFOUND);
2950 else
2951 return (IDMAP_ERR_INTERNAL);
2953 *unixname = strdup(pwd.pw_name);
2954 } else {
2955 if (grpbufsiz == 0)
2956 grpbufsiz = sysconf(_SC_GETGR_R_SIZE_MAX);
2957 buf = alloca(grpbufsiz);
2958 errno = 0;
2959 if (getgrgid_r(pid, &grp, buf, grpbufsiz) == NULL) {
2960 if (errno == 0)
2961 return (IDMAP_ERR_NOTFOUND);
2962 else
2963 return (IDMAP_ERR_INTERNAL);
2965 *unixname = strdup(grp.gr_name);
2967 if (*unixname == NULL)
2968 return (IDMAP_ERR_MEMORY);
2969 return (IDMAP_SUCCESS);
2973 * Name-based mapping
2975 * Case 1: If no rule matches do ephemeral
2977 * Case 2: If rule matches and unixname is "" then return no mapping.
2979 * Case 3: If rule matches and unixname is specified then lookup name
2980 * service using the unixname. If unixname not found then return no mapping.
2982 * Case 4: If rule matches and unixname is * then lookup name service
2983 * using winname as the unixname. If unixname not found then process
2984 * other rules using the lookup order. If no other rule matches then do
2985 * ephemeral. Otherwise, based on the matched rule do Case 2 or 3 or 4.
2986 * This allows us to specify a fallback unixname per _domain_ or no mapping
2987 * instead of the default behaviour of doing ephemeral mapping.
2989 * Example 1:
2990 * *@sfbay == *
2991 * If looking up windows users foo@sfbay and foo does not exists in
2992 * the name service then foo@sfbay will be mapped to an ephemeral id.
2994 * Example 2:
2995 * *@sfbay == *
2996 * *@sfbay => guest
2997 * If looking up windows users foo@sfbay and foo does not exists in
2998 * the name service then foo@sfbay will be mapped to guest.
3000 * Example 3:
3001 * *@sfbay == *
3002 * *@sfbay => ""
3003 * If looking up windows users foo@sfbay and foo does not exists in
3004 * the name service then we will return no mapping for foo@sfbay.
3007 static
3008 idmap_retcode
3009 name_based_mapping_sid2pid(lookup_state_t *state,
3010 idmap_mapping *req, idmap_id_res *res)
3012 const char *unixname, *windomain;
3013 char *sql = NULL, *errmsg = NULL, *lower_winname = NULL;
3014 idmap_retcode retcode;
3015 char *end, *lower_unixname, *winname;
3016 const char **values;
3017 sqlite_vm *vm = NULL;
3018 int ncol, r, is_user, is_wuser;
3019 idmap_namerule *rule = &res->info.how.idmap_how_u.rule;
3020 int direction;
3021 const char *me = "name_based_mapping_sid2pid";
3023 assert(req->id1name != NULL); /* We have winname */
3024 assert(req->id2name == NULL); /* We don't have unixname */
3026 winname = req->id1name;
3027 windomain = req->id1domain;
3029 switch (req->id1.idtype) {
3030 case IDMAP_USID:
3031 is_wuser = 1;
3032 break;
3033 case IDMAP_GSID:
3034 is_wuser = 0;
3035 break;
3036 default:
3037 idmapdlog(LOG_ERR, "%s: Unable to determine if the "
3038 "given Windows id is user or group.", me);
3039 return (IDMAP_ERR_INTERNAL);
3042 switch (res->id.idtype) {
3043 case IDMAP_UID:
3044 is_user = 1;
3045 break;
3046 case IDMAP_GID:
3047 is_user = 0;
3048 break;
3049 case IDMAP_POSIXID:
3050 is_user = is_wuser;
3051 res->id.idtype = is_user ? IDMAP_UID : IDMAP_GID;
3052 break;
3055 if (windomain == NULL)
3056 windomain = "";
3058 if ((lower_winname = tolower_u8(winname)) == NULL)
3059 lower_winname = winname; /* hope for the best */
3060 sql = sqlite_mprintf(
3061 "SELECT unixname, u2w_order, winname_display, windomain, is_nt4 "
3062 "FROM namerules WHERE "
3063 "w2u_order > 0 AND is_user = %d AND is_wuser = %d AND "
3064 "(winname = %Q OR winname = '*') AND "
3065 "(windomain = %Q OR windomain = '*') "
3066 "ORDER BY w2u_order ASC;",
3067 is_user, is_wuser, lower_winname, windomain);
3068 if (sql == NULL) {
3069 idmapdlog(LOG_ERR, "Out of memory");
3070 retcode = IDMAP_ERR_MEMORY;
3071 goto out;
3074 if (sqlite_compile(state->db, sql, NULL, &vm, &errmsg) != SQLITE_OK) {
3075 retcode = IDMAP_ERR_INTERNAL;
3076 idmapdlog(LOG_ERR, "%s: database error (%s)", me,
3077 CHECK_NULL(errmsg));
3078 sqlite_freemem(errmsg);
3079 goto out;
3082 for (;;) {
3083 r = sqlite_step(vm, &ncol, &values, NULL);
3084 assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
3086 if (r == SQLITE_ROW) {
3087 if (ncol < 5) {
3088 retcode = IDMAP_ERR_INTERNAL;
3089 goto out;
3092 TRACE(req, res, "Matching rule: %s@%s -> %s",
3093 values[2] == NULL ? "(null)" : values[2],
3094 values[3] == NULL ? "(null)" : values[3],
3095 values[0] == NULL ? "(null)" : values[0]);
3097 if (values[0] == NULL) {
3098 retcode = IDMAP_ERR_INTERNAL;
3099 goto out;
3102 if (values[1] != NULL)
3103 direction =
3104 (strtol(values[1], &end, 10) == 0)?
3105 IDMAP_DIRECTION_W2U:IDMAP_DIRECTION_BI;
3106 else
3107 direction = IDMAP_DIRECTION_W2U;
3109 if (EMPTY_NAME(values[0])) {
3110 TRACE(req, res, "Mapping inhibited");
3111 idmap_namerule_set(rule, values[3], values[2],
3112 values[0], is_user, is_wuser,
3113 strtol(values[4], &end, 10),
3114 direction);
3115 retcode = IDMAP_ERR_NOMAPPING;
3116 goto out;
3119 if (values[0][0] == '*') {
3120 unixname = winname;
3121 lower_unixname = lower_winname;
3122 } else {
3123 unixname = values[0];
3124 lower_unixname = NULL;
3127 retcode = ns_lookup_byname(unixname, lower_unixname,
3128 &res->id);
3129 if (retcode == IDMAP_SUCCESS) {
3130 break;
3131 } else if (retcode == IDMAP_ERR_NOTFOUND) {
3132 if (values[0][0] == '*') {
3133 TRACE(req, res,
3134 "%s not found, continuing",
3135 unixname);
3136 /* Case 4 */
3137 continue;
3138 } else {
3139 TRACE(req, res,
3140 "%s not found, error", unixname);
3141 /* Case 3 */
3142 idmap_namerule_set(rule, values[3],
3143 values[2], values[0], is_user,
3144 is_wuser,
3145 strtol(values[4], &end, 10),
3146 direction);
3147 retcode = IDMAP_ERR_NOMAPPING;
3149 } else {
3150 TRACE(req, res, "Looking up %s error=%d",
3151 unixname, retcode);
3153 goto out;
3154 } else if (r == SQLITE_DONE) {
3155 TRACE(req, res, "No matching rule");
3156 retcode = IDMAP_ERR_NOTFOUND;
3157 goto out;
3158 } else {
3159 (void) sqlite_finalize(vm, &errmsg);
3160 vm = NULL;
3161 idmapdlog(LOG_ERR, "%s: database error (%s)", me,
3162 CHECK_NULL(errmsg));
3163 sqlite_freemem(errmsg);
3164 retcode = IDMAP_ERR_INTERNAL;
3165 goto out;
3169 /* Found */
3171 if (values[1] != NULL)
3172 res->direction =
3173 (strtol(values[1], &end, 10) == 0)?
3174 IDMAP_DIRECTION_W2U:IDMAP_DIRECTION_BI;
3175 else
3176 res->direction = IDMAP_DIRECTION_W2U;
3178 req->id2name = strdup(unixname);
3179 if (req->id2name == NULL) {
3180 retcode = IDMAP_ERR_MEMORY;
3181 goto out;
3183 TRACE(req, res, "UNIX name found");
3185 idmap_namerule_set(rule, values[3], values[2],
3186 values[0], is_user, is_wuser, strtol(values[4], &end, 10),
3187 res->direction);
3189 out:
3190 if (retcode != IDMAP_SUCCESS &&
3191 retcode != IDMAP_ERR_NOTFOUND &&
3192 retcode != IDMAP_ERR_NOMAPPING) {
3193 TRACE(req, res, "Rule processing error, code=%d", retcode);
3196 if (sql != NULL)
3197 sqlite_freemem(sql);
3199 if (retcode != IDMAP_ERR_NOTFOUND) {
3200 res->info.how.map_type = IDMAP_MAP_TYPE_RULE_BASED;
3201 res->info.src = IDMAP_MAP_SRC_NEW;
3204 if (lower_winname != NULL && lower_winname != winname)
3205 free(lower_winname);
3206 if (vm != NULL)
3207 (void) sqlite_finalize(vm, NULL);
3208 return (retcode);
3211 static
3213 get_next_eph_uid(uid_t *next_uid)
3215 uid_t uid;
3216 gid_t gid;
3217 int err;
3219 *next_uid = (uid_t)-1;
3220 uid = _idmapdstate.next_uid++;
3221 if (uid >= _idmapdstate.limit_uid) {
3222 if ((err = allocids(0, 8192, &uid, 0, &gid)) != 0)
3223 return (err);
3225 _idmapdstate.limit_uid = uid + 8192;
3226 _idmapdstate.next_uid = uid;
3228 *next_uid = uid;
3230 return (0);
3233 static
3235 get_next_eph_gid(gid_t *next_gid)
3237 uid_t uid;
3238 gid_t gid;
3239 int err;
3241 *next_gid = (uid_t)-1;
3242 gid = _idmapdstate.next_gid++;
3243 if (gid >= _idmapdstate.limit_gid) {
3244 if ((err = allocids(0, 0, &uid, 8192, &gid)) != 0)
3245 return (err);
3247 _idmapdstate.limit_gid = gid + 8192;
3248 _idmapdstate.next_gid = gid;
3250 *next_gid = gid;
3252 return (0);
3255 static
3257 gethash(const char *str, uint32_t num, uint_t htsize)
3259 uint_t hval, i, len;
3261 if (str == NULL)
3262 return (0);
3263 for (len = strlen(str), hval = 0, i = 0; i < len; i++) {
3264 hval += str[i];
3265 hval += (hval << 10);
3266 hval ^= (hval >> 6);
3268 for (str = (const char *)&num, i = 0; i < sizeof (num); i++) {
3269 hval += str[i];
3270 hval += (hval << 10);
3271 hval ^= (hval >> 6);
3273 hval += (hval << 3);
3274 hval ^= (hval >> 11);
3275 hval += (hval << 15);
3276 return (hval % htsize);
3279 static
3281 get_from_sid_history(lookup_state_t *state, const char *prefix, uint32_t rid,
3282 uid_t *pid)
3284 uint_t next, key;
3285 uint_t htsize = state->sid_history_size;
3286 idmap_sid *sid;
3288 next = gethash(prefix, rid, htsize);
3289 while (next != htsize) {
3290 key = state->sid_history[next].key;
3291 if (key == htsize)
3292 return (0);
3293 sid = &state->batch->idmap_mapping_batch_val[key].id1.
3294 idmap_id_u.sid;
3295 if (sid->rid == rid && strcmp(sid->prefix, prefix) == 0) {
3296 *pid = state->result->ids.ids_val[key].id.
3297 idmap_id_u.uid;
3298 return (1);
3300 next = state->sid_history[next].next;
3302 return (0);
3305 static
3306 void
3307 add_to_sid_history(lookup_state_t *state, const char *prefix, uint32_t rid)
3309 uint_t hash, next;
3310 uint_t htsize = state->sid_history_size;
3312 hash = next = gethash(prefix, rid, htsize);
3313 while (state->sid_history[next].key != htsize) {
3314 next++;
3315 next %= htsize;
3317 state->sid_history[next].key = state->curpos;
3318 if (hash == next)
3319 return;
3320 state->sid_history[next].next = state->sid_history[hash].next;
3321 state->sid_history[hash].next = next;
3324 void
3325 cleanup_lookup_state(lookup_state_t *state)
3327 free(state->sid_history);
3328 free(state->ad_unixuser_attr);
3329 free(state->ad_unixgroup_attr);
3330 free(state->nldap_winname_attr);
3331 free(state->defdom);
3334 /* ARGSUSED */
3335 static
3336 idmap_retcode
3337 dynamic_ephemeral_mapping(lookup_state_t *state,
3338 idmap_mapping *req, idmap_id_res *res)
3341 uid_t next_pid;
3343 res->direction = IDMAP_DIRECTION_BI;
3345 if (IDMAP_ID_IS_EPHEMERAL(res->id.idmap_id_u.uid)) {
3346 res->info.how.map_type = IDMAP_MAP_TYPE_EPHEMERAL;
3347 res->info.src = IDMAP_MAP_SRC_CACHE;
3348 return (IDMAP_SUCCESS);
3351 if (state->sid_history != NULL &&
3352 get_from_sid_history(state, req->id1.idmap_id_u.sid.prefix,
3353 req->id1.idmap_id_u.sid.rid, &next_pid)) {
3354 res->id.idmap_id_u.uid = next_pid;
3355 res->info.how.map_type = IDMAP_MAP_TYPE_EPHEMERAL;
3356 res->info.src = IDMAP_MAP_SRC_NEW;
3357 return (IDMAP_SUCCESS);
3360 if (res->id.idtype == IDMAP_UID) {
3361 if (get_next_eph_uid(&next_pid) != 0)
3362 return (IDMAP_ERR_INTERNAL);
3363 res->id.idmap_id_u.uid = next_pid;
3364 } else {
3365 if (get_next_eph_gid(&next_pid) != 0)
3366 return (IDMAP_ERR_INTERNAL);
3367 res->id.idmap_id_u.gid = next_pid;
3370 res->info.how.map_type = IDMAP_MAP_TYPE_EPHEMERAL;
3371 res->info.src = IDMAP_MAP_SRC_NEW;
3372 if (state->sid_history != NULL)
3373 add_to_sid_history(state, req->id1.idmap_id_u.sid.prefix,
3374 req->id1.idmap_id_u.sid.rid);
3376 return (IDMAP_SUCCESS);
3379 idmap_retcode
3380 sid2pid_second_pass(lookup_state_t *state,
3381 idmap_mapping *req, idmap_id_res *res)
3383 idmap_retcode retcode;
3384 idmap_retcode retcode2;
3386 /* Check if second pass is needed */
3387 if (ARE_WE_DONE(req->direction))
3388 return (res->retcode);
3390 /* Get status from previous pass */
3391 retcode = res->retcode;
3392 if (retcode != IDMAP_SUCCESS && state->eph_map_unres_sids &&
3393 !EMPTY_STRING(req->id1.idmap_id_u.sid.prefix) &&
3394 EMPTY_STRING(req->id1name)) {
3396 * We are asked to map an unresolvable SID to a UID or
3397 * GID, but, which? We'll treat all unresolvable SIDs
3398 * as users unless the caller specified which of a UID
3399 * or GID they want.
3401 if (req->id1.idtype == IDMAP_SID)
3402 req->id1.idtype = IDMAP_USID;
3403 if (res->id.idtype == IDMAP_POSIXID) {
3404 res->id.idtype = IDMAP_UID;
3405 TRACE(req, res, "Assume unresolvable SID is user");
3406 } else if (res->id.idtype == IDMAP_UID) {
3407 TRACE(req, res, "Must map unresolvable SID to user");
3408 } else if (res->id.idtype == IDMAP_GID) {
3409 TRACE(req, res, "Must map unresolvable SID to group");
3411 goto do_eph;
3413 if (retcode != IDMAP_SUCCESS)
3414 goto out;
3417 * There are two ways we might get here with a Posix ID:
3418 * - It could be from an expired ephemeral cache entry.
3419 * - It could be from IDMU.
3420 * If it's from IDMU, we need to look up the name, for name-based
3421 * requests and the cache.
3423 if (!IDMAP_ID_IS_EPHEMERAL(res->id.idmap_id_u.uid) &&
3424 res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
3425 if (req->id2name == NULL) {
3427 * If the lookup fails, go ahead anyway.
3428 * The general UNIX rule is that it's OK to
3429 * have a UID or GID that isn't in the
3430 * name service.
3432 retcode2 = ns_lookup_bypid(res->id.idmap_id_u.uid,
3433 res->id.idtype == IDMAP_UID, &req->id2name);
3434 if (IDMAP_ERROR(retcode2)) {
3435 TRACE(req, res,
3436 "Getting UNIX name, error=%d (ignored)",
3437 retcode2);
3438 } else {
3439 TRACE(req, res, "Found UNIX name");
3442 goto out;
3446 * If directory-based name mapping is enabled then the unixname
3447 * may already have been retrieved from the AD object (AD-mode or
3448 * mixed-mode) or from native LDAP object (nldap-mode) -- done.
3450 if (req->id2name != NULL) {
3451 assert(res->id.idtype != IDMAP_POSIXID);
3452 if (AD_MODE(res->id.idtype, state))
3453 res->direction = IDMAP_DIRECTION_BI;
3454 else if (NLDAP_MODE(res->id.idtype, state))
3455 res->direction = IDMAP_DIRECTION_BI;
3456 else if (MIXED_MODE(res->id.idtype, state))
3457 res->direction = IDMAP_DIRECTION_W2U;
3460 * Special case: (1) If the ad_unixuser_attr and
3461 * ad_unixgroup_attr uses the same attribute
3462 * name and (2) if this is a diagonal mapping
3463 * request and (3) the unixname has been retrieved
3464 * from the AD object -- then we ignore it and fallback
3465 * to name-based mapping rules and ephemeral mapping
3467 * Example:
3468 * Properties:
3469 * config/ad_unixuser_attr = "unixname"
3470 * config/ad_unixgroup_attr = "unixname"
3471 * AD user object:
3472 * dn: cn=bob ...
3473 * objectclass: user
3474 * sam: bob
3475 * unixname: bob1234
3476 * AD group object:
3477 * dn: cn=winadmins ...
3478 * objectclass: group
3479 * sam: winadmins
3480 * unixname: unixadmins
3482 * In this example whether "unixname" refers to a unixuser
3483 * or unixgroup depends upon the AD object.
3485 * $idmap show -c winname:bob gid
3486 * AD lookup by "samAccountName=bob" for
3487 * "ad_unixgroup_attr (i.e unixname)" for directory-based
3488 * mapping would get "bob1234" which is not what we want.
3489 * Now why not getgrnam_r("bob1234") and use it if it
3490 * is indeed a unixgroup? That's because Unix can have
3491 * users and groups with the same name and we clearly
3492 * don't know the intention of the admin here.
3493 * Therefore we ignore this and fallback to name-based
3494 * mapping rules or ephemeral mapping.
3496 if ((AD_MODE(res->id.idtype, state) ||
3497 MIXED_MODE(res->id.idtype, state)) &&
3498 state->ad_unixuser_attr != NULL &&
3499 state->ad_unixgroup_attr != NULL &&
3500 strcasecmp(state->ad_unixuser_attr,
3501 state->ad_unixgroup_attr) == 0 &&
3502 ((req->id1.idtype == IDMAP_USID &&
3503 res->id.idtype == IDMAP_GID) ||
3504 (req->id1.idtype == IDMAP_GSID &&
3505 res->id.idtype == IDMAP_UID))) {
3506 TRACE(req, res, "Ignoring UNIX name found in AD");
3507 free(req->id2name);
3508 req->id2name = NULL;
3509 res->id.idmap_id_u.uid = IDMAP_SENTINEL_PID;
3510 /* fallback */
3511 } else {
3512 if (res->id.idmap_id_u.uid == IDMAP_SENTINEL_PID) {
3513 retcode = ns_lookup_byname(req->id2name,
3514 NULL, &res->id);
3515 if (retcode != IDMAP_SUCCESS) {
3517 * If ns_lookup_byname() fails that
3518 * means the unixname (req->id2name),
3519 * which was obtained from the AD
3520 * object by directory-based mapping,
3521 * is not a valid Unix user/group and
3522 * therefore we return the error to the
3523 * client instead of doing rule-based
3524 * mapping or ephemeral mapping. This
3525 * way the client can detect the issue.
3527 TRACE(req, res,
3528 "UNIX lookup error=%d", retcode);
3529 goto out;
3531 TRACE(req, res, "UNIX lookup");
3533 goto out;
3537 /* Free any mapping info from Directory based mapping */
3538 if (res->info.how.map_type != IDMAP_MAP_TYPE_UNKNOWN)
3539 idmap_how_clear(&res->info.how);
3542 * If we don't have unixname then evaluate local name-based
3543 * mapping rules.
3545 retcode = name_based_mapping_sid2pid(state, req, res);
3546 if (retcode == IDMAP_SUCCESS) {
3547 TRACE(req, res, "Rule-based mapping");
3548 goto out;
3549 } else if (retcode != IDMAP_ERR_NOTFOUND) {
3550 TRACE(req, res, "Rule-based mapping error=%d", retcode);
3551 goto out;
3554 do_eph:
3555 /* If not found, do ephemeral mapping */
3556 retcode = dynamic_ephemeral_mapping(state, req, res);
3557 if (retcode == IDMAP_SUCCESS) {
3558 TRACE(req, res, "Ephemeral mapping");
3559 goto out;
3560 } else if (retcode != IDMAP_ERR_NOTFOUND) {
3561 TRACE(req, res, "Ephemeral mapping error=%d", retcode);
3562 goto out;
3565 out:
3566 res->retcode = idmap_stat4prot(retcode);
3567 if (res->retcode != IDMAP_SUCCESS) {
3568 req->direction = _IDMAP_F_DONE;
3569 res->id.idmap_id_u.uid = UID_NOBODY;
3571 if (!ARE_WE_DONE(req->direction))
3572 state->sid2pid_done = FALSE;
3573 return (retcode);
3576 idmap_retcode
3577 update_cache_pid2sid(lookup_state_t *state,
3578 idmap_mapping *req, idmap_id_res *res)
3580 char *sql = NULL;
3581 idmap_retcode retcode;
3582 idmap_retcode retcode2;
3583 char *map_dn = NULL;
3584 char *map_attr = NULL;
3585 char *map_value = NULL;
3586 char *map_windomain = NULL;
3587 char *map_winname = NULL;
3588 char *map_unixname = NULL;
3589 int map_is_nt4 = FALSE;
3591 /* Check if we need to cache anything */
3592 if (ARE_WE_DONE(req->direction))
3593 return (IDMAP_SUCCESS);
3595 /* We don't cache negative entries */
3596 if (res->retcode != IDMAP_SUCCESS)
3597 return (IDMAP_SUCCESS);
3599 assert(res->direction != IDMAP_DIRECTION_UNDEF);
3600 assert(req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID);
3601 assert(res->id.idtype != IDMAP_SID);
3604 * If we've gotten to this point and we *still* don't know the
3605 * unixname, well, we'd like to have it now for the cache.
3607 * If we truly always need it for the cache, we should probably
3608 * look it up once at the beginning, rather than "at need" in
3609 * several places as is now done. However, it's not really clear
3610 * that we *do* need it in the cache; there's a decent argument
3611 * that the cache should contain only SIDs and PIDs, so we'll
3612 * leave our options open by doing it "at need" here too.
3614 * If we can't find it... c'est la vie.
3616 if (req->id1name == NULL) {
3617 retcode2 = ns_lookup_bypid(req->id1.idmap_id_u.uid,
3618 req->id1.idtype == IDMAP_UID, &req->id1name);
3619 if (retcode2 == IDMAP_SUCCESS)
3620 TRACE(req, res, "Found UNIX name");
3621 else
3622 TRACE(req, res, "Getting UNIX name error=%d", retcode2);
3625 assert(res->info.how.map_type != IDMAP_MAP_TYPE_UNKNOWN);
3626 switch (res->info.how.map_type) {
3627 case IDMAP_MAP_TYPE_DS_AD:
3628 map_dn = res->info.how.idmap_how_u.ad.dn;
3629 map_attr = res->info.how.idmap_how_u.ad.attr;
3630 map_value = res->info.how.idmap_how_u.ad.value;
3631 break;
3633 case IDMAP_MAP_TYPE_DS_NLDAP:
3634 map_dn = res->info.how.idmap_how_u.nldap.dn;
3635 map_attr = res->info.how.idmap_how_u.nldap.attr;
3636 map_value = res->info.how.idmap_how_u.nldap.value;
3637 break;
3639 case IDMAP_MAP_TYPE_RULE_BASED:
3640 map_windomain = res->info.how.idmap_how_u.rule.windomain;
3641 map_winname = res->info.how.idmap_how_u.rule.winname;
3642 map_unixname = res->info.how.idmap_how_u.rule.unixname;
3643 map_is_nt4 = res->info.how.idmap_how_u.rule.is_nt4;
3644 break;
3646 case IDMAP_MAP_TYPE_EPHEMERAL:
3647 break;
3649 case IDMAP_MAP_TYPE_LOCAL_SID:
3650 break;
3652 case IDMAP_MAP_TYPE_IDMU:
3653 map_dn = res->info.how.idmap_how_u.idmu.dn;
3654 map_attr = res->info.how.idmap_how_u.idmu.attr;
3655 map_value = res->info.how.idmap_how_u.idmu.value;
3656 break;
3658 default:
3659 /* Don't cache other mapping types */
3660 assert(FALSE);
3664 * Using NULL for u2w instead of 0 so that our trigger allows
3665 * the same pid to be the destination in multiple entries
3667 sql = sqlite_mprintf("INSERT OR REPLACE into idmap_cache "
3668 "(sidprefix, rid, windomain, canon_winname, pid, unixname, "
3669 "is_user, is_wuser, expiration, w2u, u2w, "
3670 "map_type, map_dn, map_attr, map_value, map_windomain, "
3671 "map_winname, map_unixname, map_is_nt4) "
3672 "VALUES(%Q, %u, %Q, %Q, %u, %Q, %d, %d, "
3673 "strftime('%%s','now') + %u, %q, 1, "
3674 "%d, %Q, %Q, %Q, %Q, %Q, %Q, %d); ",
3675 res->id.idmap_id_u.sid.prefix, res->id.idmap_id_u.sid.rid,
3676 req->id2domain, req->id2name, req->id1.idmap_id_u.uid,
3677 req->id1name, (req->id1.idtype == IDMAP_UID) ? 1 : 0,
3678 (res->id.idtype == IDMAP_USID) ? 1 : 0,
3679 state->id_cache_timeout,
3680 (res->direction == 0) ? "1" : NULL,
3681 res->info.how.map_type, map_dn, map_attr, map_value,
3682 map_windomain, map_winname, map_unixname, map_is_nt4);
3684 if (sql == NULL) {
3685 retcode = IDMAP_ERR_INTERNAL;
3686 idmapdlog(LOG_ERR, "Out of memory");
3687 goto out;
3690 retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3691 if (retcode != IDMAP_SUCCESS)
3692 goto out;
3694 state->pid2sid_done = FALSE;
3695 sqlite_freemem(sql);
3696 sql = NULL;
3698 /* Check if we need to update namecache */
3699 if (req->direction & _IDMAP_F_DONT_UPDATE_NAMECACHE)
3700 goto out;
3702 if (req->id2name == NULL)
3703 goto out;
3705 sql = sqlite_mprintf("INSERT OR REPLACE into name_cache "
3706 "(sidprefix, rid, canon_name, domain, type, expiration) "
3707 "VALUES(%Q, %u, %Q, %Q, %d, strftime('%%s','now') + %u); ",
3708 res->id.idmap_id_u.sid.prefix, res->id.idmap_id_u.sid.rid,
3709 req->id2name, req->id2domain,
3710 res->id.idtype, state->name_cache_timeout);
3712 if (sql == NULL) {
3713 retcode = IDMAP_ERR_INTERNAL;
3714 idmapdlog(LOG_ERR, "Out of memory");
3715 goto out;
3718 retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3720 out:
3721 if (sql != NULL)
3722 sqlite_freemem(sql);
3723 return (retcode);
3726 idmap_retcode
3727 update_cache_sid2pid(lookup_state_t *state,
3728 idmap_mapping *req, idmap_id_res *res)
3730 char *sql = NULL;
3731 idmap_retcode retcode;
3732 int is_eph_user;
3733 char *map_dn = NULL;
3734 char *map_attr = NULL;
3735 char *map_value = NULL;
3736 char *map_windomain = NULL;
3737 char *map_winname = NULL;
3738 char *map_unixname = NULL;
3739 int map_is_nt4 = FALSE;
3741 /* Check if we need to cache anything */
3742 if (ARE_WE_DONE(req->direction))
3743 return (IDMAP_SUCCESS);
3745 /* We don't cache negative entries */
3746 if (res->retcode != IDMAP_SUCCESS)
3747 return (IDMAP_SUCCESS);
3749 if (req->direction & _IDMAP_F_EXP_EPH_UID)
3750 is_eph_user = 1;
3751 else if (req->direction & _IDMAP_F_EXP_EPH_GID)
3752 is_eph_user = 0;
3753 else
3754 is_eph_user = -1;
3756 if (is_eph_user >= 0 &&
3757 !IDMAP_ID_IS_EPHEMERAL(res->id.idmap_id_u.uid)) {
3758 sql = sqlite_mprintf("UPDATE idmap_cache "
3759 "SET w2u = 0 WHERE "
3760 "sidprefix = %Q AND rid = %u AND w2u = 1 AND "
3761 "pid >= 2147483648 AND is_user = %d;",
3762 req->id1.idmap_id_u.sid.prefix,
3763 req->id1.idmap_id_u.sid.rid,
3764 is_eph_user);
3765 if (sql == NULL) {
3766 retcode = IDMAP_ERR_INTERNAL;
3767 idmapdlog(LOG_ERR, "Out of memory");
3768 goto out;
3771 retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3772 if (retcode != IDMAP_SUCCESS)
3773 goto out;
3775 sqlite_freemem(sql);
3776 sql = NULL;
3779 assert(res->direction != IDMAP_DIRECTION_UNDEF);
3780 assert(res->id.idmap_id_u.uid != IDMAP_SENTINEL_PID);
3782 switch (res->info.how.map_type) {
3783 case IDMAP_MAP_TYPE_DS_AD:
3784 map_dn = res->info.how.idmap_how_u.ad.dn;
3785 map_attr = res->info.how.idmap_how_u.ad.attr;
3786 map_value = res->info.how.idmap_how_u.ad.value;
3787 break;
3789 case IDMAP_MAP_TYPE_DS_NLDAP:
3790 map_dn = res->info.how.idmap_how_u.nldap.dn;
3791 map_attr = res->info.how.idmap_how_u.ad.attr;
3792 map_value = res->info.how.idmap_how_u.nldap.value;
3793 break;
3795 case IDMAP_MAP_TYPE_RULE_BASED:
3796 map_windomain = res->info.how.idmap_how_u.rule.windomain;
3797 map_winname = res->info.how.idmap_how_u.rule.winname;
3798 map_unixname = res->info.how.idmap_how_u.rule.unixname;
3799 map_is_nt4 = res->info.how.idmap_how_u.rule.is_nt4;
3800 break;
3802 case IDMAP_MAP_TYPE_EPHEMERAL:
3803 break;
3805 case IDMAP_MAP_TYPE_IDMU:
3806 map_dn = res->info.how.idmap_how_u.idmu.dn;
3807 map_attr = res->info.how.idmap_how_u.idmu.attr;
3808 map_value = res->info.how.idmap_how_u.idmu.value;
3809 break;
3811 default:
3812 /* Don't cache other mapping types */
3813 assert(FALSE);
3816 sql = sqlite_mprintf("INSERT OR REPLACE into idmap_cache "
3817 "(sidprefix, rid, windomain, canon_winname, pid, unixname, "
3818 "is_user, is_wuser, expiration, w2u, u2w, "
3819 "map_type, map_dn, map_attr, map_value, map_windomain, "
3820 "map_winname, map_unixname, map_is_nt4) "
3821 "VALUES(%Q, %u, %Q, %Q, %u, %Q, %d, %d, "
3822 "strftime('%%s','now') + %u, 1, %q, "
3823 "%d, %Q, %Q, %Q, %Q, %Q, %Q, %d);",
3824 req->id1.idmap_id_u.sid.prefix, req->id1.idmap_id_u.sid.rid,
3825 (req->id1domain != NULL) ? req->id1domain : "", req->id1name,
3826 res->id.idmap_id_u.uid, req->id2name,
3827 (res->id.idtype == IDMAP_UID) ? 1 : 0,
3828 (req->id1.idtype == IDMAP_USID) ? 1 : 0,
3829 state->id_cache_timeout,
3830 (res->direction == 0) ? "1" : NULL,
3831 res->info.how.map_type, map_dn, map_attr, map_value,
3832 map_windomain, map_winname, map_unixname, map_is_nt4);
3834 if (sql == NULL) {
3835 retcode = IDMAP_ERR_INTERNAL;
3836 idmapdlog(LOG_ERR, "Out of memory");
3837 goto out;
3840 retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3841 if (retcode != IDMAP_SUCCESS)
3842 goto out;
3844 state->sid2pid_done = FALSE;
3845 sqlite_freemem(sql);
3846 sql = NULL;
3848 /* Check if we need to update namecache */
3849 if (req->direction & _IDMAP_F_DONT_UPDATE_NAMECACHE)
3850 goto out;
3852 if (EMPTY_STRING(req->id1name))
3853 goto out;
3855 sql = sqlite_mprintf("INSERT OR REPLACE into name_cache "
3856 "(sidprefix, rid, canon_name, domain, type, expiration) "
3857 "VALUES(%Q, %u, %Q, %Q, %d, strftime('%%s','now') + %u); ",
3858 req->id1.idmap_id_u.sid.prefix, req->id1.idmap_id_u.sid.rid,
3859 req->id1name, req->id1domain,
3860 req->id1.idtype, state->name_cache_timeout);
3862 if (sql == NULL) {
3863 retcode = IDMAP_ERR_INTERNAL;
3864 idmapdlog(LOG_ERR, "Out of memory");
3865 goto out;
3868 retcode = sql_exec_no_cb(state->cache, IDMAP_CACHENAME, sql);
3870 out:
3871 if (sql != NULL)
3872 sqlite_freemem(sql);
3873 return (retcode);
3876 static
3877 idmap_retcode
3878 lookup_cache_pid2sid(sqlite *cache, idmap_mapping *req, idmap_id_res *res,
3879 int is_user)
3881 char *end;
3882 char *sql = NULL;
3883 const char **values;
3884 sqlite_vm *vm = NULL;
3885 int ncol;
3886 idmap_retcode retcode = IDMAP_SUCCESS;
3887 time_t curtime;
3888 idmap_id_type idtype;
3890 /* Current time */
3891 errno = 0;
3892 if ((curtime = time(NULL)) == (time_t)-1) {
3893 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
3894 strerror(errno));
3895 retcode = IDMAP_ERR_INTERNAL;
3896 goto out;
3899 /* SQL to lookup the cache by pid or by unixname */
3900 if (req->id1.idmap_id_u.uid != IDMAP_SENTINEL_PID) {
3901 sql = sqlite_mprintf("SELECT sidprefix, rid, "
3902 "canon_winname, windomain, w2u, is_wuser, "
3903 "map_type, map_dn, map_attr, map_value, map_windomain, "
3904 "map_winname, map_unixname, map_is_nt4 "
3905 "FROM idmap_cache WHERE "
3906 "pid = %u AND u2w = 1 AND is_user = %d AND "
3907 "(pid >= 2147483648 OR "
3908 "(expiration = 0 OR expiration ISNULL OR "
3909 "expiration > %d));",
3910 req->id1.idmap_id_u.uid, is_user, curtime);
3911 } else if (req->id1name != NULL) {
3912 sql = sqlite_mprintf("SELECT sidprefix, rid, "
3913 "canon_winname, windomain, w2u, is_wuser, "
3914 "map_type, map_dn, map_attr, map_value, map_windomain, "
3915 "map_winname, map_unixname, map_is_nt4 "
3916 "FROM idmap_cache WHERE "
3917 "unixname = %Q AND u2w = 1 AND is_user = %d AND "
3918 "(pid >= 2147483648 OR "
3919 "(expiration = 0 OR expiration ISNULL OR "
3920 "expiration > %d));",
3921 req->id1name, is_user, curtime);
3922 } else {
3923 retcode = IDMAP_ERR_ARG;
3924 goto out;
3927 if (sql == NULL) {
3928 idmapdlog(LOG_ERR, "Out of memory");
3929 retcode = IDMAP_ERR_MEMORY;
3930 goto out;
3932 retcode = sql_compile_n_step_once(
3933 cache, sql, &vm, &ncol, 14, &values);
3934 sqlite_freemem(sql);
3936 if (retcode == IDMAP_ERR_NOTFOUND)
3937 goto out;
3938 else if (retcode == IDMAP_SUCCESS) {
3939 /* sanity checks */
3940 if (values[0] == NULL || values[1] == NULL) {
3941 retcode = IDMAP_ERR_CACHE;
3942 goto out;
3945 switch (res->id.idtype) {
3946 case IDMAP_SID:
3947 case IDMAP_USID:
3948 case IDMAP_GSID:
3949 idtype = strtol(values[5], &end, 10) == 1
3950 ? IDMAP_USID : IDMAP_GSID;
3952 if (res->id.idtype == IDMAP_USID &&
3953 idtype != IDMAP_USID) {
3954 retcode = IDMAP_ERR_NOTUSER;
3955 goto out;
3956 } else if (res->id.idtype == IDMAP_GSID &&
3957 idtype != IDMAP_GSID) {
3958 retcode = IDMAP_ERR_NOTGROUP;
3959 goto out;
3961 res->id.idtype = idtype;
3963 res->id.idmap_id_u.sid.rid =
3964 strtoul(values[1], &end, 10);
3965 res->id.idmap_id_u.sid.prefix = strdup(values[0]);
3966 if (res->id.idmap_id_u.sid.prefix == NULL) {
3967 idmapdlog(LOG_ERR, "Out of memory");
3968 retcode = IDMAP_ERR_MEMORY;
3969 goto out;
3972 if (values[4] != NULL)
3973 res->direction =
3974 (strtol(values[4], &end, 10) == 0)?
3975 IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
3976 else
3977 res->direction = IDMAP_DIRECTION_U2W;
3979 if (values[2] == NULL)
3980 break;
3981 req->id2name = strdup(values[2]);
3982 if (req->id2name == NULL) {
3983 idmapdlog(LOG_ERR, "Out of memory");
3984 retcode = IDMAP_ERR_MEMORY;
3985 goto out;
3988 if (values[3] == NULL)
3989 break;
3990 req->id2domain = strdup(values[3]);
3991 if (req->id2domain == NULL) {
3992 idmapdlog(LOG_ERR, "Out of memory");
3993 retcode = IDMAP_ERR_MEMORY;
3994 goto out;
3997 break;
3998 default:
3999 retcode = IDMAP_ERR_NOTSUPPORTED;
4000 break;
4002 if (req->flag & IDMAP_REQ_FLG_MAPPING_INFO) {
4003 res->info.src = IDMAP_MAP_SRC_CACHE;
4004 res->info.how.map_type = strtoul(values[6], &end, 10);
4005 switch (res->info.how.map_type) {
4006 case IDMAP_MAP_TYPE_DS_AD:
4007 res->info.how.idmap_how_u.ad.dn =
4008 strdup(values[7]);
4009 res->info.how.idmap_how_u.ad.attr =
4010 strdup(values[8]);
4011 res->info.how.idmap_how_u.ad.value =
4012 strdup(values[9]);
4013 break;
4015 case IDMAP_MAP_TYPE_DS_NLDAP:
4016 res->info.how.idmap_how_u.nldap.dn =
4017 strdup(values[7]);
4018 res->info.how.idmap_how_u.nldap.attr =
4019 strdup(values[8]);
4020 res->info.how.idmap_how_u.nldap.value =
4021 strdup(values[9]);
4022 break;
4024 case IDMAP_MAP_TYPE_RULE_BASED:
4025 res->info.how.idmap_how_u.rule.windomain =
4026 strdup(values[10]);
4027 res->info.how.idmap_how_u.rule.winname =
4028 strdup(values[11]);
4029 res->info.how.idmap_how_u.rule.unixname =
4030 strdup(values[12]);
4031 res->info.how.idmap_how_u.rule.is_nt4 =
4032 strtoul(values[13], &end, 10);
4033 res->info.how.idmap_how_u.rule.is_user =
4034 is_user;
4035 res->info.how.idmap_how_u.rule.is_wuser =
4036 strtol(values[5], &end, 10);
4037 break;
4039 case IDMAP_MAP_TYPE_EPHEMERAL:
4040 break;
4042 case IDMAP_MAP_TYPE_LOCAL_SID:
4043 break;
4045 case IDMAP_MAP_TYPE_KNOWN_SID:
4046 break;
4048 case IDMAP_MAP_TYPE_IDMU:
4049 res->info.how.idmap_how_u.idmu.dn =
4050 strdup(values[7]);
4051 res->info.how.idmap_how_u.idmu.attr =
4052 strdup(values[8]);
4053 res->info.how.idmap_how_u.idmu.value =
4054 strdup(values[9]);
4055 break;
4057 default:
4058 /* Unknown mapping type */
4059 assert(FALSE);
4064 out:
4065 if (vm != NULL)
4066 (void) sqlite_finalize(vm, NULL);
4067 return (retcode);
4071 * Given:
4072 * cache sqlite handle
4073 * name Windows user name
4074 * domain Windows domain name
4076 * Return: Error code
4078 * *canonname Canonical name (if canonname is non-NULL) [1]
4079 * *sidprefix SID prefix [1]
4080 * *rid RID
4081 * *type Type of name
4083 * [1] malloc'ed, NULL on error
4085 static
4086 idmap_retcode
4087 lookup_cache_name2sid(
4088 sqlite *cache,
4089 const char *name,
4090 const char *domain,
4091 char **canonname,
4092 char **sidprefix,
4093 idmap_rid_t *rid,
4094 idmap_id_type *type)
4096 char *end, *lower_name;
4097 char *sql;
4098 const char **values;
4099 sqlite_vm *vm = NULL;
4100 int ncol;
4101 time_t curtime;
4102 idmap_retcode retcode;
4104 *sidprefix = NULL;
4105 if (canonname != NULL)
4106 *canonname = NULL;
4108 /* Get current time */
4109 errno = 0;
4110 if ((curtime = time(NULL)) == (time_t)-1) {
4111 idmapdlog(LOG_ERR, "Failed to get current time (%s)",
4112 strerror(errno));
4113 retcode = IDMAP_ERR_INTERNAL;
4114 goto out;
4117 /* SQL to lookup the cache */
4118 if ((lower_name = tolower_u8(name)) == NULL)
4119 lower_name = (char *)name;
4120 sql = sqlite_mprintf("SELECT sidprefix, rid, type, canon_name "
4121 "FROM name_cache WHERE name = %Q AND domain = %Q AND "
4122 "(expiration = 0 OR expiration ISNULL OR "
4123 "expiration > %d);", lower_name, domain, curtime);
4124 if (lower_name != name)
4125 free(lower_name);
4126 if (sql == NULL) {
4127 idmapdlog(LOG_ERR, "Out of memory");
4128 retcode = IDMAP_ERR_MEMORY;
4129 goto out;
4131 retcode = sql_compile_n_step_once(cache, sql, &vm, &ncol, 4, &values);
4133 sqlite_freemem(sql);
4135 if (retcode != IDMAP_SUCCESS)
4136 goto out;
4138 if (type != NULL) {
4139 if (values[2] == NULL) {
4140 retcode = IDMAP_ERR_CACHE;
4141 goto out;
4143 *type = xlate_legacy_type(strtol(values[2], &end, 10));
4146 if (values[0] == NULL || values[1] == NULL) {
4147 retcode = IDMAP_ERR_CACHE;
4148 goto out;
4151 if (canonname != NULL) {
4152 assert(values[3] != NULL);
4153 *canonname = strdup(values[3]);
4154 if (*canonname == NULL) {
4155 idmapdlog(LOG_ERR, "Out of memory");
4156 retcode = IDMAP_ERR_MEMORY;
4157 goto out;
4161 *sidprefix = strdup(values[0]);
4162 if (*sidprefix == NULL) {
4163 idmapdlog(LOG_ERR, "Out of memory");
4164 retcode = IDMAP_ERR_MEMORY;
4165 goto out;
4167 *rid = strtoul(values[1], &end, 10);
4169 retcode = IDMAP_SUCCESS;
4171 out:
4172 if (vm != NULL)
4173 (void) sqlite_finalize(vm, NULL);
4175 if (retcode != IDMAP_SUCCESS) {
4176 free(*sidprefix);
4177 *sidprefix = NULL;
4178 if (canonname != NULL) {
4179 free(*canonname);
4180 *canonname = NULL;
4183 return (retcode);
4186 static
4187 idmap_retcode
4188 ad_lookup_by_winname(lookup_state_t *state,
4189 const char *name, const char *domain, int esidtype,
4190 char **dn, char **attr, char **value, char **canonname,
4191 char **sidprefix, idmap_rid_t *rid, idmap_id_type *wintype,
4192 char **unixname)
4194 int retries;
4195 idmap_query_state_t *qs = NULL;
4196 idmap_retcode rc, retcode;
4197 int i;
4198 int found_ad = 0;
4200 RDLOCK_CONFIG();
4201 if (_idmapdstate.num_gcs > 0) {
4202 for (i = 0; i < _idmapdstate.num_gcs && !found_ad; i++) {
4203 retries = 0;
4204 retry:
4205 retcode = idmap_lookup_batch_start(
4206 _idmapdstate.gcs[i],
4208 _idmapdstate.cfg->pgcfg.directory_based_mapping,
4209 _idmapdstate.cfg->pgcfg.default_domain,
4210 &qs);
4211 if (retcode != IDMAP_SUCCESS) {
4212 if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
4213 retries++ < ADUTILS_DEF_NUM_RETRIES)
4214 goto retry;
4215 degrade_svc(1, "failed to create request for "
4216 "AD lookup by winname");
4217 return (retcode);
4220 restore_svc();
4222 if (state != NULL && i == 0) {
4224 * Directory based name mapping is only
4225 * performed within the joined forest (i == 0).
4226 * We don't trust other "trusted" forests to
4227 * provide DS-based name mapping information
4228 * because AD's definition of "cross-forest
4229 * trust" does not encompass this sort of
4230 * behavior.
4232 idmap_lookup_batch_set_unixattr(qs,
4233 state->ad_unixuser_attr,
4234 state->ad_unixgroup_attr);
4237 retcode = idmap_name2sid_batch_add1(qs, name, domain,
4238 esidtype, dn, attr, value, canonname, sidprefix,
4239 rid, wintype, unixname, NULL, &rc);
4240 if (retcode == IDMAP_ERR_DOMAIN_NOTFOUND) {
4241 idmap_lookup_release_batch(&qs);
4242 continue;
4244 found_ad = 1;
4245 if (retcode != IDMAP_SUCCESS)
4246 idmap_lookup_release_batch(&qs);
4247 else
4248 retcode = idmap_lookup_batch_end(&qs);
4250 if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR &&
4251 retries++ < ADUTILS_DEF_NUM_RETRIES)
4252 goto retry;
4253 else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR)
4254 degrade_svc(1,
4255 "some AD lookups timed out repeatedly");
4257 } else {
4258 /* No AD case */
4259 retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY;
4261 UNLOCK_CONFIG();
4263 if (retcode != IDMAP_SUCCESS) {
4264 idmapdlog(LOG_NOTICE,
4265 "AD lookup of winname %s@%s failed, error code %d",
4266 name == NULL ? "(null)" : name,
4267 domain == NULL ? "(null)" : domain,
4268 retcode);
4269 return (retcode);
4271 return (rc);
4275 * Given:
4276 * cache sqlite handle to cache
4277 * name Windows user name
4278 * domain Windows domain name
4279 * local_only if true, don't try AD lookups
4281 * Returns: Error code
4283 * *canonname Canonical name (if non-NULL) [1]
4284 * *canondomain Canonical domain (if non-NULL) [1]
4285 * *sidprefix SID prefix [1]
4286 * *rid RID
4287 * *req Request (direction is updated)
4289 * [1] malloc'ed, NULL on error
4291 idmap_retcode
4292 lookup_name2sid(
4293 sqlite *cache,
4294 const char *name,
4295 const char *domain,
4296 int want_wuser,
4297 char **canonname,
4298 char **canondomain,
4299 char **sidprefix,
4300 idmap_rid_t *rid,
4301 idmap_id_type *type,
4302 idmap_mapping *req,
4303 int local_only)
4305 idmap_retcode retcode;
4307 *sidprefix = NULL;
4308 if (canonname != NULL)
4309 *canonname = NULL;
4310 if (canondomain != NULL)
4311 *canondomain = NULL;
4313 /* Lookup well-known SIDs table */
4314 retcode = lookup_wksids_name2sid(name, domain, canonname, canondomain,
4315 sidprefix, rid, type);
4316 if (retcode == IDMAP_SUCCESS) {
4317 req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
4318 goto out;
4319 } else if (retcode != IDMAP_ERR_NOTFOUND) {
4320 return (retcode);
4323 /* Lookup cache */
4324 retcode = lookup_cache_name2sid(cache, name, domain, canonname,
4325 sidprefix, rid, type);
4326 if (retcode == IDMAP_SUCCESS) {
4327 req->direction |= _IDMAP_F_DONT_UPDATE_NAMECACHE;
4328 goto out;
4329 } else if (retcode != IDMAP_ERR_NOTFOUND) {
4330 return (retcode);
4334 * The caller may be using this function to determine if this
4335 * request needs to be marked for AD lookup or not
4336 * (i.e. _IDMAP_F_LOOKUP_AD) and therefore may not want this
4337 * function to AD lookup now.
4339 if (local_only)
4340 return (retcode);
4342 if (_idmapdstate.cfg->pgcfg.use_lsa &&
4343 _idmapdstate.cfg->pgcfg.domain_name != NULL &&
4344 name != NULL && *sidprefix == NULL) {
4345 retcode = lookup_lsa_by_name(name, domain,
4346 sidprefix, rid,
4347 canonname, canondomain,
4348 type);
4349 if (retcode == IDMAP_SUCCESS)
4350 goto out;
4351 else if (retcode != IDMAP_ERR_NOTFOUND)
4352 return (retcode);
4355 /* Lookup AD */
4356 retcode = ad_lookup_by_winname(NULL, name, domain, IDMAP_POSIXID,
4357 NULL, NULL, NULL, canonname, sidprefix, rid, type, NULL);
4358 if (retcode != IDMAP_SUCCESS)
4359 return (retcode);
4361 out:
4363 * Entry found (cache or Windows lookup)
4365 if (want_wuser == 1 && *type != IDMAP_USID)
4366 retcode = IDMAP_ERR_NOTUSER;
4367 else if (want_wuser == 0 && *type != IDMAP_GSID)
4368 retcode = IDMAP_ERR_NOTGROUP;
4369 else if (want_wuser == -1) {
4371 * Caller wants to know if its user or group
4372 * Verify that it's one or the other.
4374 if (*type != IDMAP_USID && *type != IDMAP_GSID)
4375 retcode = IDMAP_ERR_SID;
4378 if (retcode == IDMAP_SUCCESS) {
4380 * If we were asked for a canonical domain and none
4381 * of the searches have provided one, assume it's the
4382 * supplied domain.
4384 if (canondomain != NULL && *canondomain == NULL) {
4385 *canondomain = strdup(domain);
4386 if (*canondomain == NULL)
4387 retcode = IDMAP_ERR_MEMORY;
4390 if (retcode != IDMAP_SUCCESS) {
4391 free(*sidprefix);
4392 *sidprefix = NULL;
4393 if (canonname != NULL) {
4394 free(*canonname);
4395 *canonname = NULL;
4397 if (canondomain != NULL) {
4398 free(*canondomain);
4399 *canondomain = NULL;
4402 return (retcode);
4405 static
4406 idmap_retcode
4407 name_based_mapping_pid2sid(lookup_state_t *state, const char *unixname,
4408 int is_user, idmap_mapping *req, idmap_id_res *res)
4410 const char *winname, *windomain;
4411 char *canonname;
4412 char *canondomain;
4413 char *sql = NULL, *errmsg = NULL;
4414 idmap_retcode retcode;
4415 char *end;
4416 const char **values;
4417 sqlite_vm *vm = NULL;
4418 int ncol, r;
4419 int want_wuser;
4420 const char *me = "name_based_mapping_pid2sid";
4421 idmap_namerule *rule = &res->info.how.idmap_how_u.rule;
4422 int direction;
4424 assert(unixname != NULL); /* We have unixname */
4425 assert(req->id2name == NULL); /* We don't have winname */
4426 assert(res->id.idmap_id_u.sid.prefix == NULL); /* No SID either */
4428 sql = sqlite_mprintf(
4429 "SELECT winname_display, windomain, w2u_order, "
4430 "is_wuser, unixname, is_nt4 "
4431 "FROM namerules WHERE "
4432 "u2w_order > 0 AND is_user = %d AND "
4433 "(unixname = %Q OR unixname = '*') "
4434 "ORDER BY u2w_order ASC;", is_user, unixname);
4435 if (sql == NULL) {
4436 idmapdlog(LOG_ERR, "Out of memory");
4437 retcode = IDMAP_ERR_MEMORY;
4438 goto out;
4441 if (sqlite_compile(state->db, sql, NULL, &vm, &errmsg) != SQLITE_OK) {
4442 retcode = IDMAP_ERR_INTERNAL;
4443 idmapdlog(LOG_ERR, "%s: database error (%s)", me,
4444 CHECK_NULL(errmsg));
4445 sqlite_freemem(errmsg);
4446 goto out;
4449 for (;;) {
4450 r = sqlite_step(vm, &ncol, &values, NULL);
4451 assert(r != SQLITE_LOCKED && r != SQLITE_BUSY);
4452 if (r == SQLITE_ROW) {
4453 if (ncol < 6) {
4454 retcode = IDMAP_ERR_INTERNAL;
4455 goto out;
4458 TRACE(req, res, "Matching rule: %s -> %s@%s",
4459 values[4] == NULL ? "(null)" : values[4],
4460 values[0] == NULL ? "(null)" : values[0],
4461 values[1] == NULL ? "(null)" : values[1]);
4463 if (values[0] == NULL) {
4464 /* values [1] and [2] can be null */
4465 retcode = IDMAP_ERR_INTERNAL;
4466 goto out;
4469 if (values[2] != NULL)
4470 direction =
4471 (strtol(values[2], &end, 10) == 0)?
4472 IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
4473 else
4474 direction = IDMAP_DIRECTION_U2W;
4476 if (EMPTY_NAME(values[0])) {
4477 idmap_namerule_set(rule, values[1], values[0],
4478 values[4], is_user,
4479 strtol(values[3], &end, 10),
4480 strtol(values[5], &end, 10),
4481 direction);
4482 TRACE(req, res, "Mapping inhibited");
4483 retcode = IDMAP_ERR_NOMAPPING;
4484 goto out;
4487 if (values[0][0] == '*') {
4488 winname = unixname;
4489 } else {
4490 winname = values[0];
4493 want_wuser = res->id.idtype == IDMAP_USID ? 1
4494 : res->id.idtype == IDMAP_GSID ? 0
4495 : -1;
4496 if (values[1] != NULL)
4497 windomain = values[1];
4498 else if (state->defdom != NULL) {
4499 windomain = state->defdom;
4500 TRACE(req, res,
4501 "Added default domain %s to rule",
4502 windomain);
4503 } else {
4504 idmapdlog(LOG_ERR, "%s: no domain", me);
4505 TRACE(req, res,
4506 "No domain in rule, and no default domain");
4507 retcode = IDMAP_ERR_DOMAIN_NOTFOUND;
4508 goto out;
4511 retcode = lookup_name2sid(state->cache,
4512 winname, windomain,
4513 want_wuser, &canonname, &canondomain,
4514 &res->id.idmap_id_u.sid.prefix,
4515 &res->id.idmap_id_u.sid.rid,
4516 &res->id.idtype, req, 0);
4518 if (retcode == IDMAP_SUCCESS) {
4519 break;
4520 } else if (retcode == IDMAP_ERR_NOTFOUND) {
4521 if (values[0][0] == '*') {
4522 TRACE(req, res,
4523 "%s@%s not found, continuing",
4524 winname, windomain);
4525 continue;
4526 } else {
4527 TRACE(req, res,
4528 "%s@%s not found",
4529 winname, windomain);
4530 retcode = IDMAP_ERR_NOMAPPING;
4532 } else {
4533 TRACE(req, res,
4534 "Looking up %s@%s error=%d",
4535 winname, windomain, retcode);
4538 idmap_namerule_set(rule, values[1],
4539 values[0], values[4], is_user,
4540 strtol(values[3], &end, 10),
4541 strtol(values[5], &end, 10),
4542 direction);
4544 goto out;
4546 } else if (r == SQLITE_DONE) {
4547 TRACE(req, res, "No matching rule");
4548 retcode = IDMAP_ERR_NOTFOUND;
4549 goto out;
4550 } else {
4551 (void) sqlite_finalize(vm, &errmsg);
4552 vm = NULL;
4553 idmapdlog(LOG_ERR, "%s: database error (%s)", me,
4554 CHECK_NULL(errmsg));
4555 sqlite_freemem(errmsg);
4556 retcode = IDMAP_ERR_INTERNAL;
4557 goto out;
4561 if (values[2] != NULL)
4562 res->direction =
4563 (strtol(values[2], &end, 10) == 0)?
4564 IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
4565 else
4566 res->direction = IDMAP_DIRECTION_U2W;
4568 req->id2name = canonname;
4569 req->id2domain = canondomain;
4571 idmap_namerule_set(rule, values[1], values[0], values[4],
4572 is_user, strtol(values[3], &end, 10),
4573 strtol(values[5], &end, 10),
4574 rule->direction);
4575 TRACE(req, res, "Windows name found");
4577 out:
4578 if (sql != NULL)
4579 sqlite_freemem(sql);
4581 if (retcode != IDMAP_ERR_NOTFOUND) {
4582 res->info.how.map_type = IDMAP_MAP_TYPE_RULE_BASED;
4583 res->info.src = IDMAP_MAP_SRC_NEW;
4586 if (vm != NULL)
4587 (void) sqlite_finalize(vm, NULL);
4588 return (retcode);
4592 * Convention when processing unix2win requests:
4594 * Unix identity:
4595 * req->id1name =
4596 * unixname if given otherwise unixname found will be placed
4597 * here.
4598 * req->id1domain =
4599 * NOT USED
4600 * req->id1.idtype =
4601 * Given type (IDMAP_UID or IDMAP_GID)
4602 * req->id1..[uid or gid] =
4603 * UID/GID if given otherwise UID/GID found will be placed here.
4605 * Windows identity:
4606 * req->id2name =
4607 * winname found will be placed here.
4608 * req->id2domain =
4609 * windomain found will be placed here.
4610 * res->id.idtype =
4611 * Target type initialized from req->id2.idtype. If
4612 * it is IDMAP_SID then actual type (IDMAP_USID/GSID) found
4613 * will be placed here.
4614 * req->id..sid.[prefix, rid] =
4615 * SID found will be placed here.
4617 * Others:
4618 * res->retcode =
4619 * Return status for this request will be placed here.
4620 * res->direction =
4621 * Direction found will be placed here. Direction
4622 * meaning whether the resultant mapping is valid
4623 * only from unix2win or bi-directional.
4624 * req->direction =
4625 * INTERNAL USE. Used by idmapd to set various
4626 * flags (_IDMAP_F_xxxx) to aid in processing
4627 * of the request.
4628 * req->id2.idtype =
4629 * INTERNAL USE. Initially this is the requested target
4630 * type and is used to initialize res->id.idtype.
4631 * ad_lookup_batch() uses this field temporarily to store
4632 * sid_type obtained by the batched AD lookups and after
4633 * use resets it to IDMAP_NONE to prevent xdr from
4634 * mis-interpreting the contents of req->id2.
4635 * req->id2..[uid or gid or sid] =
4636 * NOT USED
4640 * This function does the following:
4641 * 1. Lookup well-known SIDs table.
4642 * 2. Lookup cache.
4643 * 3. Check if the client does not want new mapping to be allocated
4644 * in which case this pass is the final pass.
4645 * 4. Set AD/NLDAP lookup flags if it determines that the next stage needs
4646 * to do AD/NLDAP lookup.
4648 idmap_retcode
4649 pid2sid_first_pass(lookup_state_t *state, idmap_mapping *req,
4650 idmap_id_res *res, int is_user)
4652 idmap_retcode retcode;
4653 idmap_retcode retcode2;
4654 bool_t gen_localsid_on_err = FALSE;
4656 /* Initialize result */
4657 res->id.idtype = req->id2.idtype;
4658 res->direction = IDMAP_DIRECTION_UNDEF;
4660 if (req->id2.idmap_id_u.sid.prefix != NULL) {
4661 /* sanitize sidprefix */
4662 free(req->id2.idmap_id_u.sid.prefix);
4663 req->id2.idmap_id_u.sid.prefix = NULL;
4666 /* Find pid */
4667 if (req->id1.idmap_id_u.uid == IDMAP_SENTINEL_PID) {
4668 if (req->id1name == NULL) {
4669 retcode = IDMAP_ERR_ARG;
4670 goto out;
4673 retcode = ns_lookup_byname(req->id1name, NULL, &req->id1);
4674 if (retcode != IDMAP_SUCCESS) {
4675 TRACE(req, res, "Getting UNIX ID error=%d", retcode);
4676 retcode = IDMAP_ERR_NOMAPPING;
4677 goto out;
4679 TRACE(req, res, "Found UNIX ID");
4682 /* Lookup in well-known SIDs table */
4683 retcode = lookup_wksids_pid2sid(req, res, is_user);
4684 if (retcode == IDMAP_SUCCESS) {
4685 TRACE(req, res, "Hardwired mapping");
4686 goto out;
4687 } else if (retcode != IDMAP_ERR_NOTFOUND) {
4688 TRACE(req, res,
4689 "Well-known account lookup error=%d", retcode);
4690 goto out;
4693 /* Lookup in cache */
4694 retcode = lookup_cache_pid2sid(state->cache, req, res, is_user);
4695 if (retcode == IDMAP_SUCCESS) {
4696 TRACE(req, res, "Found in mapping cache");
4697 goto out;
4698 } else if (retcode != IDMAP_ERR_NOTFOUND) {
4699 TRACE(req, res,
4700 "Mapping cache lookup error=%d", retcode);
4701 goto out;
4703 TRACE(req, res, "Not found in mapping cache");
4705 /* Ephemeral ids cannot be allocated during pid2sid */
4706 if (IDMAP_ID_IS_EPHEMERAL(req->id1.idmap_id_u.uid)) {
4707 retcode = IDMAP_ERR_NOMAPPING;
4708 TRACE(req, res, "Shouldn't have an ephemeral ID here");
4709 goto out;
4712 if (DO_NOT_ALLOC_NEW_ID_MAPPING(req)) {
4713 retcode = IDMAP_ERR_NONE_GENERATED;
4714 goto out;
4717 if (AVOID_NAMESERVICE(req)) {
4718 gen_localsid_on_err = TRUE;
4719 retcode = IDMAP_ERR_NOMAPPING;
4720 goto out;
4723 /* Set flags for the next stage */
4724 if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU) {
4725 req->direction |= _IDMAP_F_LOOKUP_AD;
4726 state->ad_nqueries++;
4727 } else if (AD_MODE(req->id1.idtype, state)) {
4729 * If AD-based name mapping is enabled then the next stage
4730 * will need to lookup AD using unixname to get the
4731 * corresponding winname.
4733 if (req->id1name == NULL) {
4734 /* Get unixname if only pid is given. */
4735 retcode = ns_lookup_bypid(req->id1.idmap_id_u.uid,
4736 is_user, &req->id1name);
4737 if (retcode != IDMAP_SUCCESS) {
4738 TRACE(req, res,
4739 "Getting UNIX name error=%d", retcode);
4740 gen_localsid_on_err = TRUE;
4741 goto out;
4743 TRACE(req, res, "Found UNIX name");
4745 req->direction |= _IDMAP_F_LOOKUP_AD;
4746 state->ad_nqueries++;
4747 } else if (NLDAP_OR_MIXED_MODE(req->id1.idtype, state)) {
4749 * If native LDAP or mixed mode is enabled for name mapping
4750 * then the next stage will need to lookup native LDAP using
4751 * unixname/pid to get the corresponding winname.
4753 req->direction |= _IDMAP_F_LOOKUP_NLDAP;
4754 state->nldap_nqueries++;
4758 * Failed to find non-expired entry in cache. Set the flag to
4759 * indicate that we are not done yet.
4761 state->pid2sid_done = FALSE;
4762 req->direction |= _IDMAP_F_NOTDONE;
4763 retcode = IDMAP_SUCCESS;
4765 out:
4766 res->retcode = idmap_stat4prot(retcode);
4767 if (ARE_WE_DONE(req->direction) && res->retcode != IDMAP_SUCCESS) {
4768 if (gen_localsid_on_err == TRUE) {
4769 retcode2 = generate_localsid(req, res, is_user, TRUE);
4770 if (retcode2 == IDMAP_SUCCESS)
4771 TRACE(req, res, "Generate local SID");
4772 else
4773 TRACE(req, res,
4774 "Generate local SID error=%d", retcode2);
4777 return (retcode);
4780 idmap_retcode
4781 pid2sid_second_pass(lookup_state_t *state, idmap_mapping *req,
4782 idmap_id_res *res, int is_user)
4784 bool_t gen_localsid_on_err = TRUE;
4785 idmap_retcode retcode = IDMAP_SUCCESS;
4786 idmap_retcode retcode2;
4788 /* Check if second pass is needed */
4789 if (ARE_WE_DONE(req->direction))
4790 return (res->retcode);
4792 /* Get status from previous pass */
4793 retcode = res->retcode;
4794 if (retcode != IDMAP_SUCCESS)
4795 goto out;
4798 * If directory-based name mapping is enabled then the winname
4799 * may already have been retrieved from the AD object (AD-mode)
4800 * or from native LDAP object (nldap-mode or mixed-mode).
4801 * Note that if we have winname but no SID then it's an error
4802 * because this implies that the Native LDAP entry contains
4803 * winname which does not exist and it's better that we return
4804 * an error instead of doing rule-based mapping so that the user
4805 * can detect the issue and take appropriate action.
4807 if (req->id2name != NULL) {
4808 /* Return notfound if we've winname but no SID. */
4809 if (res->id.idmap_id_u.sid.prefix == NULL) {
4810 TRACE(req, res, "Windows name but no SID");
4811 retcode = IDMAP_ERR_NOTFOUND;
4812 goto out;
4814 if (state->directory_based_mapping == DIRECTORY_MAPPING_IDMU)
4815 res->direction = IDMAP_DIRECTION_BI;
4816 else if (AD_MODE(req->id1.idtype, state))
4817 res->direction = IDMAP_DIRECTION_BI;
4818 else if (NLDAP_MODE(req->id1.idtype, state))
4819 res->direction = IDMAP_DIRECTION_BI;
4820 else if (MIXED_MODE(req->id1.idtype, state))
4821 res->direction = IDMAP_DIRECTION_W2U;
4822 goto out;
4823 } else if (res->id.idmap_id_u.sid.prefix != NULL) {
4825 * We've SID but no winname. This is fine because
4826 * the caller may have only requested SID.
4828 goto out;
4831 /* Free any mapping info from Directory based mapping */
4832 if (res->info.how.map_type != IDMAP_MAP_TYPE_UNKNOWN)
4833 idmap_how_clear(&res->info.how);
4835 if (req->id1name == NULL) {
4836 /* Get unixname from name service */
4837 retcode = ns_lookup_bypid(req->id1.idmap_id_u.uid, is_user,
4838 &req->id1name);
4839 if (retcode != IDMAP_SUCCESS) {
4840 TRACE(req, res,
4841 "Getting UNIX name error=%d", retcode);
4842 goto out;
4844 TRACE(req, res, "Found UNIX name");
4845 } else if (req->id1.idmap_id_u.uid == IDMAP_SENTINEL_PID) {
4846 /* Get pid from name service */
4847 retcode = ns_lookup_byname(req->id1name, NULL, &req->id1);
4848 if (retcode != IDMAP_SUCCESS) {
4849 TRACE(req, res,
4850 "Getting UNIX ID error=%d", retcode);
4851 gen_localsid_on_err = FALSE;
4852 goto out;
4854 TRACE(req, res, "Found UNIX ID");
4857 /* Use unixname to evaluate local name-based mapping rules */
4858 retcode = name_based_mapping_pid2sid(state, req->id1name, is_user,
4859 req, res);
4860 if (retcode == IDMAP_ERR_NOTFOUND) {
4861 retcode = generate_localsid(req, res, is_user, FALSE);
4862 if (retcode == IDMAP_SUCCESS) {
4863 TRACE(req, res, "Generated local SID");
4864 } else {
4865 TRACE(req, res,
4866 "Generating local SID error=%d", retcode);
4868 gen_localsid_on_err = FALSE;
4871 out:
4872 res->retcode = idmap_stat4prot(retcode);
4873 if (res->retcode != IDMAP_SUCCESS) {
4874 req->direction = _IDMAP_F_DONE;
4875 free(req->id2name);
4876 req->id2name = NULL;
4877 free(req->id2domain);
4878 req->id2domain = NULL;
4879 if (gen_localsid_on_err == TRUE) {
4880 retcode2 = generate_localsid(req, res, is_user, TRUE);
4881 if (retcode2 == IDMAP_SUCCESS)
4882 TRACE(req, res, "Generate local SID");
4883 else
4884 TRACE(req, res,
4885 "Generate local SID error=%d", retcode2);
4886 } else {
4887 res->id.idtype = is_user ? IDMAP_USID : IDMAP_GSID;
4890 if (!ARE_WE_DONE(req->direction))
4891 state->pid2sid_done = FALSE;
4892 return (retcode);
4895 idmap_retcode
4896 idmap_cache_flush(idmap_flush_op op)
4898 idmap_retcode rc;
4899 sqlite *cache = NULL;
4900 char *sql1;
4901 char *sql2;
4903 switch (op) {
4904 case IDMAP_FLUSH_EXPIRE:
4905 sql1 =
4906 "UPDATE idmap_cache SET expiration=1 WHERE expiration>0;";
4907 sql2 =
4908 "UPDATE name_cache SET expiration=1 WHERE expiration>0;";
4909 break;
4911 case IDMAP_FLUSH_DELETE:
4912 sql1 = "DELETE FROM idmap_cache;";
4913 sql2 = "DELETE FROM name_cache;";
4914 break;
4916 default:
4917 return (IDMAP_ERR_INTERNAL);
4920 rc = get_cache_handle(&cache);
4921 if (rc != IDMAP_SUCCESS)
4922 return (rc);
4925 * Note that we flush the idmapd cache first, before the kernel
4926 * cache. If we did it the other way 'round, a request could come
4927 * in after the kernel cache flush and pull a soon-to-be-flushed
4928 * idmapd cache entry back into the kernel cache. This way the
4929 * worst that will happen is that a new entry will be added to
4930 * the kernel cache and then immediately flushed.
4933 rc = sql_exec_no_cb(cache, IDMAP_CACHENAME, sql1);
4934 if (rc != IDMAP_SUCCESS)
4935 return (rc);
4937 rc = sql_exec_no_cb(cache, IDMAP_CACHENAME, sql2);
4939 (void) __idmap_flush_kcache();
4940 return (rc);