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