xref: /illumos-gate/usr/src/cmd/idmap/idmapd/server.c (revision 9fb67ea305c66b6a297583b9b0db6796b0dfe497)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * Service routines
29  */
30 
31 #include "idmapd.h"
32 #include "idmap_priv.h"
33 #include "nldaputils.h"
34 #include <signal.h>
35 #include <thread.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <errno.h>
39 #include <assert.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/sid.h>
43 #include <ucred.h>
44 #include <pwd.h>
45 #include <auth_attr.h>
46 #include <secdb.h>
47 #include <sys/u8_textprep.h>
48 #include <note.h>
49 
50 #define	_VALIDATE_LIST_CB_DATA(col, val, siz)\
51 	retcode = validate_list_cb_data(cb_data, argc, argv, col,\
52 			(uchar_t **)val, siz);\
53 	if (retcode == IDMAP_NEXT) {\
54 		result->retcode = IDMAP_NEXT;\
55 		return (0);\
56 	} else if (retcode < 0) {\
57 		result->retcode = retcode;\
58 		return (1);\
59 	}
60 
61 #define	PROCESS_LIST_SVC_SQL(rcode, db, dbname, sql, limit, flag, cb, res, len)\
62 	rcode = process_list_svc_sql(db, dbname, sql, limit, flag, cb, res);\
63 	if (rcode == IDMAP_ERR_BUSY)\
64 		res->retcode = IDMAP_ERR_BUSY;\
65 	else if (rcode == IDMAP_SUCCESS && len == 0)\
66 		res->retcode = IDMAP_ERR_NOTFOUND;
67 
68 
69 #define	STRDUP_OR_FAIL(to, from) \
70 	if ((from) == NULL) \
71 		to = NULL; \
72 	else { \
73 		if ((to = strdup(from)) == NULL) \
74 			return (1); \
75 	}
76 
77 #define	STRDUP_CHECK(to, from) \
78 	if ((from) != NULL) { \
79 		to = strdup(from); \
80 		if (to == NULL) { \
81 			result->retcode = IDMAP_ERR_MEMORY; \
82 			goto out; \
83 		} \
84 	}
85 
86 /* ARGSUSED */
87 bool_t
88 idmap_null_1_svc(void *result, struct svc_req *rqstp)
89 {
90 	return (TRUE);
91 }
92 
93 /*
94  * RPC layer allocates empty strings to replace NULL char *.
95  * This utility function frees these empty strings.
96  */
97 static
98 void
99 sanitize_mapping_request(idmap_mapping *req)
100 {
101 	if (EMPTY_STRING(req->id1name)) {
102 		free(req->id1name);
103 		req->id1name = NULL;
104 	}
105 	if (EMPTY_STRING(req->id1domain)) {
106 		free(req->id1domain);
107 		req->id1domain = NULL;
108 	}
109 	if (EMPTY_STRING(req->id2name)) {
110 		free(req->id2name);
111 		req->id2name = NULL;
112 	}
113 	if (EMPTY_STRING(req->id2domain)) {
114 		free(req->id2domain);
115 		req->id2domain = NULL;
116 	}
117 	req->direction = _IDMAP_F_DONE;
118 }
119 
120 static
121 int
122 validate_mapped_id_by_name_req(idmap_mapping *req)
123 {
124 	int e;
125 
126 	if (IS_REQUEST_UID(*req) || IS_REQUEST_GID(*req))
127 		return (IDMAP_SUCCESS);
128 
129 	if (IS_REQUEST_SID(*req, 1)) {
130 		if (!EMPTY_STRING(req->id1name) &&
131 		    u8_validate(req->id1name, strlen(req->id1name),
132 		    NULL, U8_VALIDATE_ENTIRE, &e) < 0)
133 			return (IDMAP_ERR_BAD_UTF8);
134 		if (!EMPTY_STRING(req->id1domain) &&
135 		    u8_validate(req->id1domain, strlen(req->id1domain),
136 		    NULL, U8_VALIDATE_ENTIRE, &e) < 0)
137 			return (IDMAP_ERR_BAD_UTF8);
138 	}
139 
140 	return (IDMAP_SUCCESS);
141 }
142 
143 static
144 int
145 validate_rule(idmap_namerule *rule)
146 {
147 	int e;
148 
149 	if (!EMPTY_STRING(rule->winname) &&
150 	    u8_validate(rule->winname, strlen(rule->winname),
151 	    NULL, U8_VALIDATE_ENTIRE, &e) < 0)
152 		return (IDMAP_ERR_BAD_UTF8);
153 
154 	if (!EMPTY_STRING(rule->windomain) &&
155 	    u8_validate(rule->windomain, strlen(rule->windomain),
156 	    NULL, U8_VALIDATE_ENTIRE, &e) < 0)
157 		return (IDMAP_ERR_BAD_UTF8);
158 
159 	return (IDMAP_SUCCESS);
160 
161 }
162 
163 static
164 bool_t
165 validate_rules(idmap_update_batch *batch)
166 {
167 	idmap_update_op	*up;
168 	int i;
169 
170 	for (i = 0; i < batch->idmap_update_batch_len; i++) {
171 		up = &(batch->idmap_update_batch_val[i]);
172 		if (validate_rule(&(up->idmap_update_op_u.rule))
173 		    != IDMAP_SUCCESS)
174 			return (IDMAP_ERR_BAD_UTF8);
175 	}
176 
177 	return (IDMAP_SUCCESS);
178 }
179 
180 /* ARGSUSED */
181 bool_t
182 idmap_get_mapped_ids_1_svc(idmap_mapping_batch batch,
183 		idmap_ids_res *result, struct svc_req *rqstp)
184 {
185 	sqlite		*cache = NULL, *db = NULL;
186 	lookup_state_t	state;
187 	idmap_retcode	retcode;
188 	uint_t		i;
189 
190 	/* Init */
191 	(void) memset(result, 0, sizeof (*result));
192 	(void) memset(&state, 0, sizeof (state));
193 
194 	/* Return success if nothing was requested */
195 	if (batch.idmap_mapping_batch_len < 1)
196 		goto out;
197 
198 	/* Get cache handle */
199 	result->retcode = get_cache_handle(&cache);
200 	if (result->retcode != IDMAP_SUCCESS)
201 		goto out;
202 	state.cache = cache;
203 
204 	/* Get db handle */
205 	result->retcode = get_db_handle(&db);
206 	if (result->retcode != IDMAP_SUCCESS)
207 		goto out;
208 	state.db = db;
209 
210 	/* Allocate result array */
211 	result->ids.ids_val = calloc(batch.idmap_mapping_batch_len,
212 	    sizeof (idmap_id_res));
213 	if (result->ids.ids_val == NULL) {
214 		idmapdlog(LOG_ERR, "Out of memory");
215 		result->retcode = IDMAP_ERR_MEMORY;
216 		goto out;
217 	}
218 	result->ids.ids_len = batch.idmap_mapping_batch_len;
219 
220 	/* Allocate hash table to check for duplicate sids */
221 	state.sid_history = calloc(batch.idmap_mapping_batch_len,
222 	    sizeof (*state.sid_history));
223 	if (state.sid_history == NULL) {
224 		idmapdlog(LOG_ERR, "Out of memory");
225 		result->retcode = IDMAP_ERR_MEMORY;
226 		goto out;
227 	}
228 	state.sid_history_size = batch.idmap_mapping_batch_len;
229 	for (i = 0; i < state.sid_history_size; i++) {
230 		state.sid_history[i].key = state.sid_history_size;
231 		state.sid_history[i].next = state.sid_history_size;
232 	}
233 	state.batch = &batch;
234 	state.result = result;
235 
236 	/* Get directory-based name mapping info */
237 	result->retcode = load_cfg_in_state(&state);
238 	if (result->retcode != IDMAP_SUCCESS)
239 		goto out;
240 
241 	/* Init our 'done' flags */
242 	state.sid2pid_done = state.pid2sid_done = TRUE;
243 
244 	/* First stage */
245 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
246 		state.curpos = i;
247 		(void) sanitize_mapping_request(
248 		    &batch.idmap_mapping_batch_val[i]);
249 		if (IS_BATCH_SID(batch, i)) {
250 			retcode = sid2pid_first_pass(
251 			    &state,
252 			    &batch.idmap_mapping_batch_val[i],
253 			    &result->ids.ids_val[i]);
254 		} else if (IS_BATCH_UID(batch, i)) {
255 			retcode = pid2sid_first_pass(
256 			    &state,
257 			    &batch.idmap_mapping_batch_val[i],
258 			    &result->ids.ids_val[i], 1);
259 		} else if (IS_BATCH_GID(batch, i)) {
260 			retcode = pid2sid_first_pass(
261 			    &state,
262 			    &batch.idmap_mapping_batch_val[i],
263 			    &result->ids.ids_val[i], 0);
264 		} else {
265 			result->ids.ids_val[i].retcode = IDMAP_ERR_IDTYPE;
266 			continue;
267 		}
268 		if (IDMAP_FATAL_ERROR(retcode)) {
269 			result->retcode = retcode;
270 			goto out;
271 		}
272 	}
273 
274 	/* Check if we are done */
275 	if (state.sid2pid_done == TRUE && state.pid2sid_done == TRUE)
276 		goto out;
277 
278 	/*
279 	 * native LDAP lookups:
280 	 *  pid2sid:
281 	 *	- nldap or mixed mode. Lookup nldap by pid or unixname to get
282 	 *	  winname.
283 	 *  sid2pid:
284 	 *	- nldap mode. Got winname and sid (either given or found in
285 	 *	  name_cache). Lookup nldap by winname to get pid and
286 	 *	  unixname.
287 	 */
288 	if (state.nldap_nqueries) {
289 		retcode = nldap_lookup_batch(&state, &batch, result);
290 		if (IDMAP_FATAL_ERROR(retcode)) {
291 			result->retcode = retcode;
292 			goto out;
293 		}
294 	}
295 
296 	/*
297 	 * AD lookups:
298 	 *  pid2sid:
299 	 *	- nldap or mixed mode. Got winname from nldap lookup.
300 	 *	  winname2sid could not be resolved locally. Lookup AD
301 	 *	  by winname to get sid.
302 	 *	- ad mode. Got unixname. Lookup AD by unixname to get
303 	 *	  winname and sid.
304 	 *  sid2pid:
305 	 *	- ad or mixed mode. Lookup AD by sid or winname to get
306 	 *	  winname, sid and unixname.
307 	 *	- any mode. Got either sid or winname but not both. Lookup
308 	 *	  AD by sid or winname to get winname, sid.
309 	 */
310 	if (state.ad_nqueries) {
311 		retcode = ad_lookup_batch(&state, &batch, result);
312 		if (IDMAP_FATAL_ERROR(retcode)) {
313 			result->retcode = retcode;
314 			goto out;
315 		}
316 	}
317 
318 	/*
319 	 * native LDAP lookups:
320 	 *  sid2pid:
321 	 *	- nldap mode. Got winname and sid from AD lookup. Lookup nldap
322 	 *	  by winname to get pid and unixname.
323 	 */
324 	if (state.nldap_nqueries) {
325 		retcode = nldap_lookup_batch(&state, &batch, result);
326 		if (IDMAP_FATAL_ERROR(retcode)) {
327 			result->retcode = retcode;
328 			goto out;
329 		}
330 	}
331 
332 	/* Reset 'done' flags */
333 	state.sid2pid_done = state.pid2sid_done = TRUE;
334 
335 	/* Second stage */
336 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
337 		state.curpos = i;
338 		if (IS_BATCH_SID(batch, i)) {
339 			retcode = sid2pid_second_pass(
340 			    &state,
341 			    &batch.idmap_mapping_batch_val[i],
342 			    &result->ids.ids_val[i]);
343 		} else if (IS_BATCH_UID(batch, i)) {
344 			retcode = pid2sid_second_pass(
345 			    &state,
346 			    &batch.idmap_mapping_batch_val[i],
347 			    &result->ids.ids_val[i], 1);
348 		} else if (IS_BATCH_GID(batch, i)) {
349 			retcode = pid2sid_second_pass(
350 			    &state,
351 			    &batch.idmap_mapping_batch_val[i],
352 			    &result->ids.ids_val[i], 0);
353 		} else {
354 			/* First stage has already set the error */
355 			continue;
356 		}
357 		if (IDMAP_FATAL_ERROR(retcode)) {
358 			result->retcode = retcode;
359 			goto out;
360 		}
361 	}
362 
363 	/* Check if we are done */
364 	if (state.sid2pid_done == TRUE && state.pid2sid_done == TRUE)
365 		goto out;
366 
367 	/* Reset our 'done' flags */
368 	state.sid2pid_done = state.pid2sid_done = TRUE;
369 
370 	/* Update cache in a single transaction */
371 	if (sql_exec_no_cb(cache, IDMAP_CACHENAME, "BEGIN TRANSACTION;")
372 	    != IDMAP_SUCCESS)
373 		goto out;
374 
375 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
376 		state.curpos = i;
377 		if (IS_BATCH_SID(batch, i)) {
378 			(void) update_cache_sid2pid(
379 			    &state,
380 			    &batch.idmap_mapping_batch_val[i],
381 			    &result->ids.ids_val[i]);
382 		} else if ((IS_BATCH_UID(batch, i)) ||
383 		    (IS_BATCH_GID(batch, i))) {
384 			(void) update_cache_pid2sid(
385 			    &state,
386 			    &batch.idmap_mapping_batch_val[i],
387 			    &result->ids.ids_val[i]);
388 		}
389 	}
390 
391 	/* Commit if we have at least one successful update */
392 	if (state.sid2pid_done == FALSE || state.pid2sid_done == FALSE)
393 		(void) sql_exec_no_cb(cache, IDMAP_CACHENAME,
394 		    "COMMIT TRANSACTION;");
395 	else
396 		(void) sql_exec_no_cb(cache, IDMAP_CACHENAME,
397 		    "END TRANSACTION;");
398 
399 out:
400 	cleanup_lookup_state(&state);
401 	if (IDMAP_ERROR(result->retcode)) {
402 		xdr_free(xdr_idmap_ids_res, (caddr_t)result);
403 		result->ids.ids_len = 0;
404 		result->ids.ids_val = NULL;
405 	}
406 	result->retcode = idmap_stat4prot(result->retcode);
407 	return (TRUE);
408 }
409 
410 
411 /* ARGSUSED */
412 static
413 int
414 list_mappings_cb(void *parg, int argc, char **argv, char **colnames)
415 {
416 	list_cb_data_t		*cb_data;
417 	char			*str;
418 	idmap_mappings_res	*result;
419 	idmap_retcode		retcode;
420 	int			w2u, u2w;
421 	char			*end;
422 	static int		validated_column_names = 0;
423 	idmap_how		*how;
424 
425 	cb_data = (list_cb_data_t *)parg;
426 
427 	if (!validated_column_names) {
428 		assert(strcmp(colnames[0], "rowid") == 0);
429 		assert(strcmp(colnames[1], "sidprefix") == 0);
430 		assert(strcmp(colnames[2], "rid") == 0);
431 		assert(strcmp(colnames[3], "pid") == 0);
432 		assert(strcmp(colnames[4], "w2u") == 0);
433 		assert(strcmp(colnames[5], "u2w") == 0);
434 		assert(strcmp(colnames[6], "windomain") == 0);
435 		assert(strcmp(colnames[7], "canon_winname") == 0);
436 		assert(strcmp(colnames[8], "unixname") == 0);
437 		assert(strcmp(colnames[9], "is_user") == 0);
438 		assert(strcmp(colnames[10], "is_wuser") == 0);
439 		assert(strcmp(colnames[11], "map_type") == 0);
440 		assert(strcmp(colnames[12], "map_dn") == 0);
441 		assert(strcmp(colnames[13], "map_attr") == 0);
442 		assert(strcmp(colnames[14], "map_value") == 0);
443 		assert(strcmp(colnames[15], "map_windomain") == 0);
444 		assert(strcmp(colnames[16], "map_winname") == 0);
445 		assert(strcmp(colnames[17], "map_unixname") == 0);
446 		assert(strcmp(colnames[18], "map_is_nt4") == 0);
447 		validated_column_names = 1;
448 	}
449 
450 	result = (idmap_mappings_res *)cb_data->result;
451 
452 	_VALIDATE_LIST_CB_DATA(19, &result->mappings.mappings_val,
453 	    sizeof (idmap_mapping));
454 
455 	result->mappings.mappings_len++;
456 
457 	if ((str = strdup(argv[1])) == NULL)
458 		return (1);
459 	result->mappings.mappings_val[cb_data->next].id1.idmap_id_u.sid.prefix =
460 	    str;
461 	result->mappings.mappings_val[cb_data->next].id1.idmap_id_u.sid.rid =
462 	    strtoul(argv[2], &end, 10);
463 	result->mappings.mappings_val[cb_data->next].id1.idtype =
464 	    strtol(argv[10], &end, 10) ? IDMAP_USID : IDMAP_GSID;
465 
466 	result->mappings.mappings_val[cb_data->next].id2.idmap_id_u.uid =
467 	    strtoul(argv[3], &end, 10);
468 	result->mappings.mappings_val[cb_data->next].id2.idtype =
469 	    strtol(argv[9], &end, 10) ? IDMAP_UID : IDMAP_GID;
470 
471 	w2u = argv[4] ? strtol(argv[4], &end, 10) : 0;
472 	u2w = argv[5] ? strtol(argv[5], &end, 10) : 0;
473 
474 	if (w2u > 0 && u2w == 0)
475 		result->mappings.mappings_val[cb_data->next].direction =
476 		    IDMAP_DIRECTION_W2U;
477 	else if (w2u == 0 && u2w > 0)
478 		result->mappings.mappings_val[cb_data->next].direction =
479 		    IDMAP_DIRECTION_U2W;
480 	else
481 		result->mappings.mappings_val[cb_data->next].direction =
482 		    IDMAP_DIRECTION_BI;
483 
484 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1domain,
485 	    argv[6]);
486 
487 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1name,
488 	    argv[7]);
489 
490 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id2name,
491 	    argv[8]);
492 
493 	if (cb_data->flag & IDMAP_REQ_FLG_MAPPING_INFO) {
494 		how = &result->mappings.mappings_val[cb_data->next].info.how;
495 		how->map_type = strtoul(argv[11], &end, 10);
496 		switch (how->map_type) {
497 		case IDMAP_MAP_TYPE_DS_AD:
498 			how->idmap_how_u.ad.dn =
499 			    strdup(argv[12]);
500 			how->idmap_how_u.ad.attr =
501 			    strdup(argv[13]);
502 			how->idmap_how_u.ad.value =
503 			    strdup(argv[14]);
504 			break;
505 
506 		case IDMAP_MAP_TYPE_DS_NLDAP:
507 			how->idmap_how_u.nldap.dn =
508 			    strdup(argv[12]);
509 			how->idmap_how_u.nldap.attr =
510 			    strdup(argv[13]);
511 			how->idmap_how_u.nldap.value =
512 			    strdup(argv[14]);
513 			break;
514 
515 		case IDMAP_MAP_TYPE_RULE_BASED:
516 			how->idmap_how_u.rule.windomain =
517 			    strdup(argv[15]);
518 			how->idmap_how_u.rule.winname =
519 			    strdup(argv[16]);
520 			how->idmap_how_u.rule.unixname =
521 			    strdup(argv[17]);
522 			how->idmap_how_u.rule.is_nt4 =
523 			    strtoul(argv[18], &end, 10);
524 			how->idmap_how_u.rule.is_user =
525 			    strtol(argv[9], &end, 10);
526 			how->idmap_how_u.rule.is_wuser =
527 			    strtol(argv[10], &end, 10);
528 			break;
529 
530 		case IDMAP_MAP_TYPE_EPHEMERAL:
531 			break;
532 
533 		case IDMAP_MAP_TYPE_LOCAL_SID:
534 			break;
535 
536 		case IDMAP_MAP_TYPE_IDMU:
537 			how->idmap_how_u.idmu.dn =
538 			    strdup(argv[12]);
539 			how->idmap_how_u.idmu.attr =
540 			    strdup(argv[13]);
541 			how->idmap_how_u.idmu.value =
542 			    strdup(argv[14]);
543 			break;
544 
545 		default:
546 			/* Unknown mapping type */
547 			assert(FALSE);
548 		}
549 
550 	}
551 
552 	result->lastrowid = strtoll(argv[0], &end, 10);
553 	cb_data->next++;
554 	result->retcode = IDMAP_SUCCESS;
555 	return (0);
556 }
557 
558 
559 /* ARGSUSED */
560 bool_t
561 idmap_list_mappings_1_svc(int64_t lastrowid, uint64_t limit, int32_t flag,
562     idmap_mappings_res *result, struct svc_req *rqstp)
563 {
564 	sqlite		*cache = NULL;
565 	char		lbuf[30], rbuf[30];
566 	uint64_t	maxlimit;
567 	idmap_retcode	retcode;
568 	char		*sql = NULL;
569 	time_t		curtime;
570 
571 	(void) memset(result, 0, sizeof (*result));
572 
573 	/* Current time */
574 	errno = 0;
575 	if ((curtime = time(NULL)) == (time_t)-1) {
576 		idmapdlog(LOG_ERR, "Failed to get current time (%s)",
577 		    strerror(errno));
578 		retcode = IDMAP_ERR_INTERNAL;
579 		goto out;
580 	}
581 
582 	RDLOCK_CONFIG();
583 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
584 	UNLOCK_CONFIG();
585 
586 	/* Get cache handle */
587 	result->retcode = get_cache_handle(&cache);
588 	if (result->retcode != IDMAP_SUCCESS)
589 		goto out;
590 
591 	result->retcode = IDMAP_ERR_INTERNAL;
592 
593 	/* Create LIMIT expression. */
594 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
595 		limit = maxlimit;
596 	if (limit > 0)
597 		(void) snprintf(lbuf, sizeof (lbuf),
598 		    "LIMIT %" PRIu64, limit + 1ULL);
599 	else
600 		lbuf[0] = '\0';
601 
602 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
603 
604 	/*
605 	 * Combine all the above into a giant SELECT statement that
606 	 * will return the requested mappings
607 	 */
608 
609 	sql = sqlite_mprintf("SELECT rowid, sidprefix, rid, pid, w2u, "
610 	    "u2w, windomain, canon_winname, unixname, is_user, is_wuser, "
611 	    "map_type, map_dn, map_attr, map_value, map_windomain, "
612 	    "map_winname, map_unixname, map_is_nt4 "
613 	    "FROM idmap_cache WHERE %s AND "
614 	    "(pid >= 2147483648 OR (expiration = 0 OR "
615 	    "expiration ISNULL  OR expiration > %d)) "
616 	    "%s;",
617 	    rbuf, curtime, lbuf);
618 	if (sql == NULL) {
619 		result->retcode = IDMAP_ERR_MEMORY;
620 		idmapdlog(LOG_ERR, "Out of memory");
621 		goto out;
622 	}
623 
624 	/* Execute the SQL statement and update the return buffer */
625 	PROCESS_LIST_SVC_SQL(retcode, cache, IDMAP_CACHENAME, sql, limit,
626 	    flag, list_mappings_cb, result, result->mappings.mappings_len);
627 
628 out:
629 	if (sql)
630 		sqlite_freemem(sql);
631 	if (IDMAP_ERROR(result->retcode))
632 		(void) xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
633 	result->retcode = idmap_stat4prot(result->retcode);
634 	return (TRUE);
635 }
636 
637 
638 /* ARGSUSED */
639 static
640 int
641 list_namerules_cb(void *parg, int argc, char **argv, char **colnames)
642 {
643 	list_cb_data_t		*cb_data;
644 	idmap_namerules_res	*result;
645 	idmap_retcode		retcode;
646 	int			w2u_order, u2w_order;
647 	char			*end;
648 	static int		validated_column_names = 0;
649 
650 	if (!validated_column_names) {
651 		assert(strcmp(colnames[0], "rowid") == 0);
652 		assert(strcmp(colnames[1], "is_user") == 0);
653 		assert(strcmp(colnames[2], "is_wuser") == 0);
654 		assert(strcmp(colnames[3], "windomain") == 0);
655 		assert(strcmp(colnames[4], "winname_display") == 0);
656 		assert(strcmp(colnames[5], "is_nt4") == 0);
657 		assert(strcmp(colnames[6], "unixname") == 0);
658 		assert(strcmp(colnames[7], "w2u_order") == 0);
659 		assert(strcmp(colnames[8], "u2w_order") == 0);
660 		validated_column_names = 1;
661 	}
662 
663 	cb_data = (list_cb_data_t *)parg;
664 	result = (idmap_namerules_res *)cb_data->result;
665 
666 	_VALIDATE_LIST_CB_DATA(9, &result->rules.rules_val,
667 	    sizeof (idmap_namerule));
668 
669 	result->rules.rules_len++;
670 
671 	result->rules.rules_val[cb_data->next].is_user =
672 	    strtol(argv[1], &end, 10);
673 
674 	result->rules.rules_val[cb_data->next].is_wuser =
675 	    strtol(argv[2], &end, 10);
676 
677 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].windomain,
678 	    argv[3]);
679 
680 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].winname,
681 	    argv[4]);
682 
683 	result->rules.rules_val[cb_data->next].is_nt4 =
684 	    strtol(argv[5], &end, 10);
685 
686 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].unixname,
687 	    argv[6]);
688 
689 	w2u_order = argv[7] ? strtol(argv[7], &end, 10) : 0;
690 	u2w_order = argv[8] ? strtol(argv[8], &end, 10) : 0;
691 
692 	if (w2u_order > 0 && u2w_order == 0)
693 		result->rules.rules_val[cb_data->next].direction =
694 		    IDMAP_DIRECTION_W2U;
695 	else if (w2u_order == 0 && u2w_order > 0)
696 		result->rules.rules_val[cb_data->next].direction =
697 		    IDMAP_DIRECTION_U2W;
698 	else
699 		result->rules.rules_val[cb_data->next].direction =
700 		    IDMAP_DIRECTION_BI;
701 
702 	result->lastrowid = strtoll(argv[0], &end, 10);
703 	cb_data->next++;
704 	result->retcode = IDMAP_SUCCESS;
705 	return (0);
706 }
707 
708 
709 /* ARGSUSED */
710 bool_t
711 idmap_list_namerules_1_svc(idmap_namerule rule, uint64_t lastrowid,
712 		uint64_t limit, idmap_namerules_res *result,
713 		struct svc_req *rqstp)
714 {
715 
716 	sqlite		*db = NULL;
717 	char		lbuf[30], rbuf[30];
718 	char		*sql = NULL;
719 	char		*expr = NULL;
720 	uint64_t	maxlimit;
721 	idmap_retcode	retcode;
722 
723 	(void) memset(result, 0, sizeof (*result));
724 
725 	result->retcode = validate_rule(&rule);
726 	if (result->retcode != IDMAP_SUCCESS)
727 		goto out;
728 
729 	RDLOCK_CONFIG();
730 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
731 	UNLOCK_CONFIG();
732 
733 	/* Get db handle */
734 	result->retcode = get_db_handle(&db);
735 	if (result->retcode != IDMAP_SUCCESS)
736 		goto out;
737 
738 	result->retcode = gen_sql_expr_from_rule(&rule, &expr);
739 	if (result->retcode != IDMAP_SUCCESS)
740 		goto out;
741 
742 	/* Create LIMIT expression. */
743 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
744 		limit = maxlimit;
745 	if (limit > 0)
746 		(void) snprintf(lbuf, sizeof (lbuf),
747 		    "LIMIT %" PRIu64, limit + 1ULL);
748 	else
749 		lbuf[0] = '\0';
750 
751 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
752 
753 	/*
754 	 * Combine all the above into a giant SELECT statement that
755 	 * will return the requested rules
756 	 */
757 	sql = sqlite_mprintf("SELECT rowid, is_user, is_wuser, windomain, "
758 	    "winname_display, is_nt4, unixname, w2u_order, u2w_order "
759 	    "FROM namerules WHERE "
760 	    " %s %s %s;",
761 	    rbuf, expr, lbuf);
762 
763 	if (sql == NULL) {
764 		result->retcode = IDMAP_ERR_MEMORY;
765 		idmapdlog(LOG_ERR, "Out of memory");
766 		goto out;
767 	}
768 
769 	/* Execute the SQL statement and update the return buffer */
770 	PROCESS_LIST_SVC_SQL(retcode, db, IDMAP_DBNAME, sql, limit,
771 	    0, list_namerules_cb, result, result->rules.rules_len);
772 
773 out:
774 	if (expr)
775 		sqlite_freemem(expr);
776 	if (sql)
777 		sqlite_freemem(sql);
778 	if (IDMAP_ERROR(result->retcode))
779 		(void) xdr_free(xdr_idmap_namerules_res, (caddr_t)result);
780 	result->retcode = idmap_stat4prot(result->retcode);
781 	return (TRUE);
782 }
783 
784 #define	IDMAP_RULES_AUTH	"solaris.admin.idmap.rules"
785 static int
786 verify_rules_auth(struct svc_req *rqstp)
787 {
788 	ucred_t		*uc = NULL;
789 	uid_t		uid;
790 	char		buf[1024];
791 	struct passwd	pwd;
792 
793 	if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
794 		idmapdlog(LOG_ERR, "svc_getcallerucred failed during "
795 		    "authorization (%s)", strerror(errno));
796 		return (-1);
797 	}
798 
799 	uid = ucred_geteuid(uc);
800 	if (uid == (uid_t)-1) {
801 		idmapdlog(LOG_ERR, "ucred_geteuid failed during "
802 		    "authorization (%s)", strerror(errno));
803 		ucred_free(uc);
804 		return (-1);
805 	}
806 
807 	if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) {
808 		idmapdlog(LOG_ERR, "getpwuid_r(%u) failed during "
809 		    "authorization (%s)", uid, strerror(errno));
810 		ucred_free(uc);
811 		return (-1);
812 	}
813 
814 	if (chkauthattr(IDMAP_RULES_AUTH, pwd.pw_name) != 1) {
815 		idmapdlog(LOG_INFO, "%s is not authorized (%s)",
816 		    pwd.pw_name, IDMAP_RULES_AUTH);
817 		ucred_free(uc);
818 		return (-1);
819 	}
820 
821 	ucred_free(uc);
822 	return (1);
823 }
824 
825 /*
826  * Meaning of the return values is the following: For retcode ==
827  * IDMAP_SUCCESS, everything went OK and error_index is
828  * undefined. Otherwise, error_index >=0 shows the failed batch
829  * element. errro_index == -1 indicates failure at the beginning,
830  * error_index == -2 at the end.
831  */
832 
833 /* ARGSUSED */
834 bool_t
835 idmap_update_1_svc(idmap_update_batch batch, idmap_update_res *res,
836 		struct svc_req *rqstp)
837 {
838 	sqlite		*db = NULL;
839 	idmap_update_op	*up;
840 	int		i;
841 	int		trans = FALSE;
842 
843 	res->error_index = -1;
844 	(void) memset(&res->error_rule, 0, sizeof (res->error_rule));
845 	(void) memset(&res->conflict_rule, 0, sizeof (res->conflict_rule));
846 
847 	if (verify_rules_auth(rqstp) < 0) {
848 		res->retcode = IDMAP_ERR_PERMISSION_DENIED;
849 		goto out;
850 	}
851 
852 	if (batch.idmap_update_batch_len == 0 ||
853 	    batch.idmap_update_batch_val == NULL) {
854 		res->retcode = IDMAP_SUCCESS;
855 		goto out;
856 	}
857 
858 	res->retcode = validate_rules(&batch);
859 	if (res->retcode != IDMAP_SUCCESS)
860 		goto out;
861 
862 	/* Get db handle */
863 	res->retcode = get_db_handle(&db);
864 	if (res->retcode != IDMAP_SUCCESS)
865 		goto out;
866 
867 	res->retcode = sql_exec_no_cb(db, IDMAP_DBNAME, "BEGIN TRANSACTION;");
868 	if (res->retcode != IDMAP_SUCCESS)
869 		goto out;
870 	trans = TRUE;
871 
872 	for (i = 0; i < batch.idmap_update_batch_len; i++) {
873 		up = &batch.idmap_update_batch_val[i];
874 		switch (up->opnum) {
875 		case OP_NONE:
876 			res->retcode = IDMAP_SUCCESS;
877 			break;
878 		case OP_ADD_NAMERULE:
879 			res->retcode = add_namerule(db,
880 			    &up->idmap_update_op_u.rule);
881 			break;
882 		case OP_RM_NAMERULE:
883 			res->retcode = rm_namerule(db,
884 			    &up->idmap_update_op_u.rule);
885 			break;
886 		case OP_FLUSH_NAMERULES:
887 			res->retcode = flush_namerules(db);
888 			break;
889 		default:
890 			res->retcode = IDMAP_ERR_NOTSUPPORTED;
891 			break;
892 		};
893 
894 		if (res->retcode != IDMAP_SUCCESS) {
895 			res->error_index = i;
896 			if (up->opnum == OP_ADD_NAMERULE ||
897 			    up->opnum == OP_RM_NAMERULE) {
898 				idmap_stat r2 =
899 				    idmap_namerule_cpy(&res->error_rule,
900 				    &up->idmap_update_op_u.rule);
901 				if (r2 != IDMAP_SUCCESS)
902 					res->retcode = r2;
903 			}
904 			goto out;
905 		}
906 	}
907 
908 out:
909 	if (trans) {
910 		if (res->retcode == IDMAP_SUCCESS) {
911 			res->retcode =
912 			    sql_exec_no_cb(db, IDMAP_DBNAME,
913 			    "COMMIT TRANSACTION;");
914 			if (res->retcode ==  IDMAP_SUCCESS) {
915 				/*
916 				 * We've updated the rules.  Expire the cache
917 				 * so that existing mappings will be
918 				 * reconsidered.
919 				 */
920 				res->retcode =
921 				    idmap_cache_flush(IDMAP_FLUSH_EXPIRE);
922 			} else {
923 				res->error_index = -2;
924 			}
925 		}
926 		else
927 			(void) sql_exec_no_cb(db, IDMAP_DBNAME,
928 			    "ROLLBACK TRANSACTION;");
929 	}
930 
931 	res->retcode = idmap_stat4prot(res->retcode);
932 
933 	return (TRUE);
934 }
935 
936 static
937 int
938 copy_string(char **to, char *from)
939 {
940 	if (EMPTY_STRING(from)) {
941 		*to = NULL;
942 	} else {
943 		*to = strdup(from);
944 		if (*to == NULL) {
945 			idmapdlog(LOG_ERR, "Out of memory");
946 			return (IDMAP_ERR_MEMORY);
947 		}
948 	}
949 	return (IDMAP_SUCCESS);
950 }
951 
952 static
953 int
954 copy_id(idmap_id *to, idmap_id *from)
955 {
956 	(void) memset(to, 0, sizeof (*to));
957 
958 	to->idtype = from->idtype;
959 	if (IS_ID_SID(*from)) {
960 		idmap_retcode retcode;
961 
962 		to->idmap_id_u.sid.rid = from->idmap_id_u.sid.rid;
963 		retcode = copy_string(&to->idmap_id_u.sid.prefix,
964 		    from->idmap_id_u.sid.prefix);
965 
966 		return (retcode);
967 	} else {
968 		to->idmap_id_u.uid = from->idmap_id_u.uid;
969 		return (IDMAP_SUCCESS);
970 	}
971 }
972 
973 static
974 int
975 copy_mapping(idmap_mapping *mapping, idmap_mapping *request)
976 {
977 	idmap_retcode retcode;
978 
979 	(void) memset(mapping, 0, sizeof (*mapping));
980 
981 	mapping->flag = request->flag;
982 	mapping->direction = _IDMAP_F_DONE;
983 
984 	retcode = copy_id(&mapping->id1, &request->id1);
985 	if (retcode != IDMAP_SUCCESS)
986 		goto errout;
987 
988 	retcode = copy_string(&mapping->id1domain, request->id1domain);
989 	if (retcode != IDMAP_SUCCESS)
990 		goto errout;
991 
992 	retcode = copy_string(&mapping->id1name, request->id1name);
993 	if (retcode != IDMAP_SUCCESS)
994 		goto errout;
995 
996 	retcode = copy_id(&mapping->id2, &request->id2);
997 	if (retcode != IDMAP_SUCCESS)
998 		goto errout;
999 
1000 	retcode = copy_string(&mapping->id2domain, request->id2domain);
1001 	if (retcode != IDMAP_SUCCESS)
1002 		goto errout;
1003 	retcode = copy_string(&mapping->id2name, request->id2name);
1004 	if (retcode != IDMAP_SUCCESS)
1005 		goto errout;
1006 
1007 	return (IDMAP_SUCCESS);
1008 
1009 errout:
1010 	if (IS_ID_SID(mapping->id1))
1011 		free(mapping->id1.idmap_id_u.sid.prefix);
1012 	free(mapping->id1domain);
1013 	free(mapping->id1name);
1014 	if (IS_ID_SID(mapping->id2))
1015 		free(mapping->id2.idmap_id_u.sid.prefix);
1016 	free(mapping->id2domain);
1017 	free(mapping->id2name);
1018 
1019 	(void) memset(mapping, 0, sizeof (*mapping));
1020 	return (retcode);
1021 }
1022 
1023 
1024 /* ARGSUSED */
1025 bool_t
1026 idmap_get_mapped_id_by_name_1_svc(idmap_mapping request,
1027 		idmap_mappings_res *result, struct svc_req *rqstp)
1028 {
1029 	idmap_mapping_batch batch_request;
1030 	idmap_ids_res batch_result;
1031 	idmap_mapping *map;
1032 
1033 	/* Clear out things we might want to xdr_free on error */
1034 	(void) memset(&batch_result, 0, sizeof (batch_result));
1035 	(void) memset(result, 0, sizeof (*result));
1036 
1037 	result->retcode = validate_mapped_id_by_name_req(&request);
1038 	if (result->retcode != IDMAP_SUCCESS)
1039 		goto out;
1040 
1041 	/*
1042 	 * Copy the request.  We need to modify it, and
1043 	 * what we have is a shallow copy.  Freeing pointers from
1044 	 * our copy will lead to problems, since the RPC framework
1045 	 * has its own copy of those pointers.  Besides, we need
1046 	 * a copy to return.
1047 	 */
1048 	map = calloc(1, sizeof (idmap_mapping));
1049 	if (map == NULL) {
1050 		idmapdlog(LOG_ERR, "Out of memory");
1051 		result->retcode = IDMAP_ERR_MEMORY;
1052 		goto out;
1053 	}
1054 
1055 	/*
1056 	 * Set up to return the filled-in mapping structure.
1057 	 * Note that we xdr_free result on error, and that'll take
1058 	 * care of freeing the mapping structure.
1059 	 */
1060 	result->mappings.mappings_val = map;
1061 	result->mappings.mappings_len = 1;
1062 
1063 	result->retcode = copy_mapping(map, &request);
1064 	if (result->retcode != IDMAP_SUCCESS)
1065 		goto out;
1066 
1067 	/* Set up for the request to the batch API */
1068 	batch_request.idmap_mapping_batch_val = map;
1069 	batch_request.idmap_mapping_batch_len = 1;
1070 
1071 	/* Do the real work. */
1072 	(void) idmap_get_mapped_ids_1_svc(batch_request,
1073 	    &batch_result, rqstp);
1074 
1075 	/* Copy what we need out of the batch response */
1076 
1077 	if (batch_result.retcode != IDMAP_SUCCESS) {
1078 		result->retcode = batch_result.retcode;
1079 		goto out;
1080 	}
1081 
1082 	result->retcode = copy_id(&map->id2, &batch_result.ids.ids_val[0].id);
1083 	if (result->retcode != IDMAP_SUCCESS)
1084 		goto out;
1085 
1086 	map->direction = batch_result.ids.ids_val[0].direction;
1087 
1088 	result->retcode = batch_result.ids.ids_val[0].retcode;
1089 
1090 	if (map->flag & IDMAP_REQ_FLG_MAPPING_INFO ||
1091 	    result->retcode != IDMAP_SUCCESS) {
1092 		(void) idmap_info_mov(&map->info,
1093 		    &batch_result.ids.ids_val[0].info);
1094 	}
1095 
1096 out:
1097 	if (IDMAP_FATAL_ERROR(result->retcode)) {
1098 		xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
1099 		result->mappings.mappings_len = 0;
1100 		result->mappings.mappings_val = NULL;
1101 	}
1102 	result->retcode = idmap_stat4prot(result->retcode);
1103 
1104 	xdr_free(xdr_idmap_ids_res, (char *)&batch_result);
1105 
1106 	return (TRUE);
1107 }
1108 
1109 /* ARGSUSED */
1110 bool_t
1111 idmap_get_prop_1_svc(idmap_prop_type request,
1112 		idmap_prop_res *result, struct svc_req *rqstp)
1113 {
1114 	idmap_pg_config_t *pgcfg;
1115 
1116 	/* Init */
1117 	(void) memset(result, 0, sizeof (*result));
1118 	result->retcode = IDMAP_SUCCESS;
1119 	result->value.prop = request;
1120 
1121 	RDLOCK_CONFIG();
1122 
1123 	/* Just shortcuts: */
1124 	pgcfg = &_idmapdstate.cfg->pgcfg;
1125 
1126 
1127 	switch (request) {
1128 	case PROP_LIST_SIZE_LIMIT:
1129 		result->value.idmap_prop_val_u.intval = pgcfg->list_size_limit;
1130 		result->auto_discovered = FALSE;
1131 		break;
1132 	case PROP_DEFAULT_DOMAIN:
1133 		result->auto_discovered = FALSE;
1134 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1135 		    pgcfg->default_domain);
1136 		break;
1137 	case PROP_DOMAIN_NAME:
1138 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1139 		    pgcfg->domain_name);
1140 		result->auto_discovered =
1141 		    pgcfg->domain_name_auto_disc;
1142 		break;
1143 	case PROP_MACHINE_SID:
1144 		result->auto_discovered = FALSE;
1145 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1146 		    pgcfg->machine_sid);
1147 		break;
1148 	case PROP_DOMAIN_CONTROLLER:
1149 		if (pgcfg->domain_controller != NULL) {
1150 			(void) memcpy(&result->value.idmap_prop_val_u.dsval,
1151 			    pgcfg->domain_controller,
1152 			    sizeof (idmap_ad_disc_ds_t));
1153 		}
1154 		result->auto_discovered = pgcfg->domain_controller_auto_disc;
1155 		break;
1156 	case PROP_FOREST_NAME:
1157 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1158 		    pgcfg->forest_name);
1159 		result->auto_discovered = pgcfg->forest_name_auto_disc;
1160 		break;
1161 	case PROP_SITE_NAME:
1162 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1163 		    pgcfg->site_name);
1164 		result->auto_discovered = pgcfg->site_name_auto_disc;
1165 		break;
1166 	case PROP_GLOBAL_CATALOG:
1167 		if (pgcfg->global_catalog != NULL) {
1168 			(void) memcpy(&result->value.idmap_prop_val_u.dsval,
1169 			    pgcfg->global_catalog, sizeof (idmap_ad_disc_ds_t));
1170 		}
1171 		result->auto_discovered = pgcfg->global_catalog_auto_disc;
1172 		break;
1173 	case PROP_AD_UNIXUSER_ATTR:
1174 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1175 		    pgcfg->ad_unixuser_attr);
1176 		result->auto_discovered = FALSE;
1177 		break;
1178 	case PROP_AD_UNIXGROUP_ATTR:
1179 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1180 		    pgcfg->ad_unixgroup_attr);
1181 		result->auto_discovered = FALSE;
1182 		break;
1183 	case PROP_NLDAP_WINNAME_ATTR:
1184 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1185 		    pgcfg->nldap_winname_attr);
1186 		result->auto_discovered = FALSE;
1187 		break;
1188 	case PROP_DIRECTORY_BASED_MAPPING:
1189 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1190 		    enum_lookup(pgcfg->directory_based_mapping,
1191 		    directory_mapping_map));
1192 		result->auto_discovered = FALSE;
1193 		break;
1194 	default:
1195 		result->retcode = IDMAP_ERR_PROP_UNKNOWN;
1196 		break;
1197 	}
1198 
1199 out:
1200 	UNLOCK_CONFIG();
1201 	if (IDMAP_FATAL_ERROR(result->retcode)) {
1202 		xdr_free(xdr_idmap_prop_res, (caddr_t)result);
1203 		result->value.prop = PROP_UNKNOWN;
1204 	}
1205 	result->retcode = idmap_stat4prot(result->retcode);
1206 	return (TRUE);
1207 }
1208 
1209 int
1210 idmap_flush_1_svc(
1211     idmap_flush_op  op,
1212     idmap_retcode *result,
1213     struct svc_req *rqstp)
1214 {
1215 	NOTE(ARGUNUSED(rqstp))
1216 	if (verify_rules_auth(rqstp) < 0) {
1217 		*result = IDMAP_ERR_PERMISSION_DENIED;
1218 		return (TRUE);
1219 	}
1220 
1221 	*result = idmap_cache_flush(op);
1222 
1223 	return (TRUE);
1224 }
1225 
1226 /* ARGSUSED */
1227 int
1228 idmap_prog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,
1229 		caddr_t result)
1230 {
1231 	(void) xdr_free(xdr_result, result);
1232 	return (TRUE);
1233 }
1234 
1235 /*
1236  * This function is called by rpc_svc.c when it encounters an error.
1237  */
1238 NOTE(PRINTFLIKE(1))
1239 void
1240 idmap_rpc_msgout(const char *fmt, ...)
1241 {
1242 	va_list va;
1243 	char buf[1000];
1244 
1245 	va_start(va, fmt);
1246 	(void) vsnprintf(buf, sizeof (buf), fmt, va);
1247 	va_end(va);
1248 
1249 	idmapdlog(LOG_ERR, "idmap RPC:  %s", buf);
1250 }
1251