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