xref: /titanic_52/usr/src/cmd/idmap/idmapd/server.c (revision 91d7f85e02991954d1e1bd44673df567ad8dcc87)
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 		}
368 	}
369 
370 	/*
371 	 * native LDAP lookups:
372 	 *  sid2pid:
373 	 *	- nldap mode. Got winname and sid from AD lookup. Lookup nldap
374 	 *	  by winname to get pid and unixname.
375 	 */
376 	if (state.nldap_nqueries) {
377 		retcode = nldap_lookup_batch(&state, &batch, result);
378 		if (IDMAP_FATAL_ERROR(retcode)) {
379 			TRACE(req, res, "Native LDAP lookup error=%d", retcode);
380 			result->retcode = retcode;
381 			goto out;
382 		}
383 		if (any_tracing) {
384 			for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
385 				res = &result->ids.ids_val[i];
386 				req = &batch.idmap_mapping_batch_val[i];
387 				TRACE(req, res, "Native LDAP lookup");
388 			}
389 		}
390 	}
391 
392 	/* Reset 'done' flags */
393 	state.sid2pid_done = state.pid2sid_done = TRUE;
394 
395 	/* Second stage */
396 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
397 		req = &batch.idmap_mapping_batch_val[i];
398 		res = &result->ids.ids_val[i];
399 		state.curpos = i;
400 		if (IS_ID_SID(req->id1)) {
401 			retcode = sid2pid_second_pass(
402 			    &state,
403 			    req,
404 			    res);
405 		} else if (IS_ID_UID(req->id1)) {
406 			retcode = pid2sid_second_pass(
407 			    &state,
408 			    req,
409 			    res, 1);
410 		} else if (IS_ID_GID(req->id1)) {
411 			retcode = pid2sid_second_pass(
412 			    &state,
413 			    req,
414 			    res, 0);
415 		} else {
416 			/* First stage has already set the error */
417 			continue;
418 		}
419 		if (IDMAP_FATAL_ERROR(retcode)) {
420 			result->retcode = retcode;
421 			goto out;
422 		}
423 	}
424 
425 	/* Check if we are done */
426 	if (state.sid2pid_done == TRUE && state.pid2sid_done == TRUE)
427 		goto out;
428 
429 	/* Reset our 'done' flags */
430 	state.sid2pid_done = state.pid2sid_done = TRUE;
431 
432 	/* Update cache in a single transaction */
433 	if (sql_exec_no_cb(cache, IDMAP_CACHENAME, "BEGIN TRANSACTION;")
434 	    != IDMAP_SUCCESS)
435 		goto out;
436 
437 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
438 		req = &batch.idmap_mapping_batch_val[i];
439 		res = &result->ids.ids_val[i];
440 		state.curpos = i;
441 		if (IS_ID_SID(req->id1)) {
442 			(void) update_cache_sid2pid(
443 			    &state,
444 			    req,
445 			    res);
446 		} else if ((IS_ID_UID(req->id1)) ||
447 		    (IS_ID_GID(req->id1))) {
448 			(void) update_cache_pid2sid(
449 			    &state,
450 			    req,
451 			    res);
452 		}
453 	}
454 
455 	/* Commit if we have at least one successful update */
456 	if (state.sid2pid_done == FALSE || state.pid2sid_done == FALSE)
457 		(void) sql_exec_no_cb(cache, IDMAP_CACHENAME,
458 		    "COMMIT TRANSACTION;");
459 	else
460 		(void) sql_exec_no_cb(cache, IDMAP_CACHENAME,
461 		    "END TRANSACTION;");
462 
463 out:
464 	cleanup_lookup_state(&state);
465 	if (IDMAP_ERROR(result->retcode)) {
466 		if (any_tracing) {
467 			for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
468 				req = &batch.idmap_mapping_batch_val[i];
469 				res = &result->ids.ids_val[i];
470 				TRACE(req, res,
471 				    "Failure code %d", result->retcode);
472 			}
473 		}
474 		xdr_free(xdr_idmap_ids_res, (caddr_t)result);
475 		result->ids.ids_len = 0;
476 		result->ids.ids_val = NULL;
477 	} else {
478 		if (any_tracing) {
479 			for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
480 				req = &batch.idmap_mapping_batch_val[i];
481 				res = &result->ids.ids_val[i];
482 				TRACE(req, res, "Done");
483 			}
484 		}
485 	}
486 	result->retcode = idmap_stat4prot(result->retcode);
487 
488 	for (i = 0; i < result->ids.ids_len; i++) {
489 		req = &batch.idmap_mapping_batch_val[i];
490 		res = &result->ids.ids_val[i];
491 
492 		if (!(req->flag & IDMAP_REQ_FLG_MAPPING_INFO) &&
493 		    res->retcode == IDMAP_SUCCESS)
494 			idmap_how_clear(&res->info.how);
495 	}
496 	return (TRUE);
497 }
498 
499 
500 /* ARGSUSED */
501 static
502 int
503 list_mappings_cb(void *parg, int argc, char **argv, char **colnames)
504 {
505 	list_cb_data_t		*cb_data;
506 	char			*str;
507 	idmap_mappings_res	*result;
508 	idmap_retcode		retcode;
509 	int			w2u, u2w;
510 	char			*end;
511 	static int		validated_column_names = 0;
512 	idmap_how		*how;
513 
514 	cb_data = (list_cb_data_t *)parg;
515 
516 	if (!validated_column_names) {
517 		assert(strcmp(colnames[0], "rowid") == 0);
518 		assert(strcmp(colnames[1], "sidprefix") == 0);
519 		assert(strcmp(colnames[2], "rid") == 0);
520 		assert(strcmp(colnames[3], "pid") == 0);
521 		assert(strcmp(colnames[4], "w2u") == 0);
522 		assert(strcmp(colnames[5], "u2w") == 0);
523 		assert(strcmp(colnames[6], "windomain") == 0);
524 		assert(strcmp(colnames[7], "canon_winname") == 0);
525 		assert(strcmp(colnames[8], "unixname") == 0);
526 		assert(strcmp(colnames[9], "is_user") == 0);
527 		assert(strcmp(colnames[10], "is_wuser") == 0);
528 		assert(strcmp(colnames[11], "map_type") == 0);
529 		assert(strcmp(colnames[12], "map_dn") == 0);
530 		assert(strcmp(colnames[13], "map_attr") == 0);
531 		assert(strcmp(colnames[14], "map_value") == 0);
532 		assert(strcmp(colnames[15], "map_windomain") == 0);
533 		assert(strcmp(colnames[16], "map_winname") == 0);
534 		assert(strcmp(colnames[17], "map_unixname") == 0);
535 		assert(strcmp(colnames[18], "map_is_nt4") == 0);
536 		validated_column_names = 1;
537 	}
538 
539 	result = (idmap_mappings_res *)cb_data->result;
540 
541 	_VALIDATE_LIST_CB_DATA(19, &result->mappings.mappings_val,
542 	    sizeof (idmap_mapping));
543 
544 	result->mappings.mappings_len++;
545 
546 	if ((str = strdup(argv[1])) == NULL)
547 		return (1);
548 	result->mappings.mappings_val[cb_data->next].id1.idmap_id_u.sid.prefix =
549 	    str;
550 	result->mappings.mappings_val[cb_data->next].id1.idmap_id_u.sid.rid =
551 	    strtoul(argv[2], &end, 10);
552 	result->mappings.mappings_val[cb_data->next].id1.idtype =
553 	    strtol(argv[10], &end, 10) ? IDMAP_USID : IDMAP_GSID;
554 
555 	result->mappings.mappings_val[cb_data->next].id2.idmap_id_u.uid =
556 	    strtoul(argv[3], &end, 10);
557 	result->mappings.mappings_val[cb_data->next].id2.idtype =
558 	    strtol(argv[9], &end, 10) ? IDMAP_UID : IDMAP_GID;
559 
560 	w2u = argv[4] ? strtol(argv[4], &end, 10) : 0;
561 	u2w = argv[5] ? strtol(argv[5], &end, 10) : 0;
562 
563 	if (w2u > 0 && u2w == 0)
564 		result->mappings.mappings_val[cb_data->next].direction =
565 		    IDMAP_DIRECTION_W2U;
566 	else if (w2u == 0 && u2w > 0)
567 		result->mappings.mappings_val[cb_data->next].direction =
568 		    IDMAP_DIRECTION_U2W;
569 	else
570 		result->mappings.mappings_val[cb_data->next].direction =
571 		    IDMAP_DIRECTION_BI;
572 
573 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1domain,
574 	    argv[6]);
575 
576 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1name,
577 	    argv[7]);
578 
579 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id2name,
580 	    argv[8]);
581 
582 	if (cb_data->flag & IDMAP_REQ_FLG_MAPPING_INFO) {
583 		how = &result->mappings.mappings_val[cb_data->next].info.how;
584 		how->map_type = strtoul(argv[11], &end, 10);
585 		switch (how->map_type) {
586 		case IDMAP_MAP_TYPE_DS_AD:
587 			how->idmap_how_u.ad.dn =
588 			    strdup(argv[12]);
589 			how->idmap_how_u.ad.attr =
590 			    strdup(argv[13]);
591 			how->idmap_how_u.ad.value =
592 			    strdup(argv[14]);
593 			break;
594 
595 		case IDMAP_MAP_TYPE_DS_NLDAP:
596 			how->idmap_how_u.nldap.dn =
597 			    strdup(argv[12]);
598 			how->idmap_how_u.nldap.attr =
599 			    strdup(argv[13]);
600 			how->idmap_how_u.nldap.value =
601 			    strdup(argv[14]);
602 			break;
603 
604 		case IDMAP_MAP_TYPE_RULE_BASED:
605 			how->idmap_how_u.rule.windomain =
606 			    strdup(argv[15]);
607 			how->idmap_how_u.rule.winname =
608 			    strdup(argv[16]);
609 			how->idmap_how_u.rule.unixname =
610 			    strdup(argv[17]);
611 			how->idmap_how_u.rule.is_nt4 =
612 			    strtoul(argv[18], &end, 10);
613 			how->idmap_how_u.rule.is_user =
614 			    strtol(argv[9], &end, 10);
615 			how->idmap_how_u.rule.is_wuser =
616 			    strtol(argv[10], &end, 10);
617 			break;
618 
619 		case IDMAP_MAP_TYPE_EPHEMERAL:
620 			break;
621 
622 		case IDMAP_MAP_TYPE_LOCAL_SID:
623 			break;
624 
625 		case IDMAP_MAP_TYPE_IDMU:
626 			how->idmap_how_u.idmu.dn =
627 			    strdup(argv[12]);
628 			how->idmap_how_u.idmu.attr =
629 			    strdup(argv[13]);
630 			how->idmap_how_u.idmu.value =
631 			    strdup(argv[14]);
632 			break;
633 
634 		default:
635 			/* Unknown mapping type */
636 			assert(FALSE);
637 		}
638 
639 	}
640 
641 	result->lastrowid = strtoll(argv[0], &end, 10);
642 	cb_data->next++;
643 	result->retcode = IDMAP_SUCCESS;
644 	return (0);
645 }
646 
647 
648 /* ARGSUSED */
649 bool_t
650 idmap_list_mappings_1_svc(int64_t lastrowid, uint64_t limit, int32_t flag,
651     idmap_mappings_res *result, struct svc_req *rqstp)
652 {
653 	sqlite		*cache = NULL;
654 	char		lbuf[30], rbuf[30];
655 	uint64_t	maxlimit;
656 	idmap_retcode	retcode;
657 	char		*sql = NULL;
658 	time_t		curtime;
659 
660 	(void) memset(result, 0, sizeof (*result));
661 
662 	/* Current time */
663 	errno = 0;
664 	if ((curtime = time(NULL)) == (time_t)-1) {
665 		idmapdlog(LOG_ERR, "Failed to get current time (%s)",
666 		    strerror(errno));
667 		retcode = IDMAP_ERR_INTERNAL;
668 		goto out;
669 	}
670 
671 	RDLOCK_CONFIG();
672 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
673 	UNLOCK_CONFIG();
674 
675 	/* Get cache handle */
676 	result->retcode = get_cache_handle(&cache);
677 	if (result->retcode != IDMAP_SUCCESS)
678 		goto out;
679 
680 	result->retcode = IDMAP_ERR_INTERNAL;
681 
682 	/* Create LIMIT expression. */
683 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
684 		limit = maxlimit;
685 	if (limit > 0)
686 		(void) snprintf(lbuf, sizeof (lbuf),
687 		    "LIMIT %" PRIu64, limit + 1ULL);
688 	else
689 		lbuf[0] = '\0';
690 
691 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
692 
693 	/*
694 	 * Combine all the above into a giant SELECT statement that
695 	 * will return the requested mappings
696 	 */
697 
698 	sql = sqlite_mprintf("SELECT rowid, sidprefix, rid, pid, w2u, "
699 	    "u2w, windomain, canon_winname, unixname, is_user, is_wuser, "
700 	    "map_type, map_dn, map_attr, map_value, map_windomain, "
701 	    "map_winname, map_unixname, map_is_nt4 "
702 	    "FROM idmap_cache WHERE %s AND "
703 	    "(pid >= 2147483648 OR (expiration = 0 OR "
704 	    "expiration ISNULL  OR expiration > %d)) "
705 	    "%s;",
706 	    rbuf, curtime, lbuf);
707 	if (sql == NULL) {
708 		result->retcode = IDMAP_ERR_MEMORY;
709 		idmapdlog(LOG_ERR, "Out of memory");
710 		goto out;
711 	}
712 
713 	/* Execute the SQL statement and update the return buffer */
714 	PROCESS_LIST_SVC_SQL(retcode, cache, IDMAP_CACHENAME, sql, limit,
715 	    flag, list_mappings_cb, result, result->mappings.mappings_len);
716 
717 out:
718 	if (sql)
719 		sqlite_freemem(sql);
720 	if (IDMAP_ERROR(result->retcode))
721 		(void) xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
722 	result->retcode = idmap_stat4prot(result->retcode);
723 	return (TRUE);
724 }
725 
726 
727 /* ARGSUSED */
728 static
729 int
730 list_namerules_cb(void *parg, int argc, char **argv, char **colnames)
731 {
732 	list_cb_data_t		*cb_data;
733 	idmap_namerules_res	*result;
734 	idmap_retcode		retcode;
735 	int			w2u_order, u2w_order;
736 	char			*end;
737 	static int		validated_column_names = 0;
738 
739 	if (!validated_column_names) {
740 		assert(strcmp(colnames[0], "rowid") == 0);
741 		assert(strcmp(colnames[1], "is_user") == 0);
742 		assert(strcmp(colnames[2], "is_wuser") == 0);
743 		assert(strcmp(colnames[3], "windomain") == 0);
744 		assert(strcmp(colnames[4], "winname_display") == 0);
745 		assert(strcmp(colnames[5], "is_nt4") == 0);
746 		assert(strcmp(colnames[6], "unixname") == 0);
747 		assert(strcmp(colnames[7], "w2u_order") == 0);
748 		assert(strcmp(colnames[8], "u2w_order") == 0);
749 		validated_column_names = 1;
750 	}
751 
752 	cb_data = (list_cb_data_t *)parg;
753 	result = (idmap_namerules_res *)cb_data->result;
754 
755 	_VALIDATE_LIST_CB_DATA(9, &result->rules.rules_val,
756 	    sizeof (idmap_namerule));
757 
758 	result->rules.rules_len++;
759 
760 	result->rules.rules_val[cb_data->next].is_user =
761 	    strtol(argv[1], &end, 10);
762 
763 	result->rules.rules_val[cb_data->next].is_wuser =
764 	    strtol(argv[2], &end, 10);
765 
766 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].windomain,
767 	    argv[3]);
768 
769 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].winname,
770 	    argv[4]);
771 
772 	result->rules.rules_val[cb_data->next].is_nt4 =
773 	    strtol(argv[5], &end, 10);
774 
775 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].unixname,
776 	    argv[6]);
777 
778 	w2u_order = argv[7] ? strtol(argv[7], &end, 10) : 0;
779 	u2w_order = argv[8] ? strtol(argv[8], &end, 10) : 0;
780 
781 	if (w2u_order > 0 && u2w_order == 0)
782 		result->rules.rules_val[cb_data->next].direction =
783 		    IDMAP_DIRECTION_W2U;
784 	else if (w2u_order == 0 && u2w_order > 0)
785 		result->rules.rules_val[cb_data->next].direction =
786 		    IDMAP_DIRECTION_U2W;
787 	else
788 		result->rules.rules_val[cb_data->next].direction =
789 		    IDMAP_DIRECTION_BI;
790 
791 	result->lastrowid = strtoll(argv[0], &end, 10);
792 	cb_data->next++;
793 	result->retcode = IDMAP_SUCCESS;
794 	return (0);
795 }
796 
797 
798 /* ARGSUSED */
799 bool_t
800 idmap_list_namerules_1_svc(idmap_namerule rule, uint64_t lastrowid,
801 		uint64_t limit, idmap_namerules_res *result,
802 		struct svc_req *rqstp)
803 {
804 
805 	sqlite		*db = NULL;
806 	char		lbuf[30], rbuf[30];
807 	char		*sql = NULL;
808 	char		*expr = NULL;
809 	uint64_t	maxlimit;
810 	idmap_retcode	retcode;
811 
812 	(void) memset(result, 0, sizeof (*result));
813 
814 	result->retcode = validate_rule(&rule);
815 	if (result->retcode != IDMAP_SUCCESS)
816 		goto out;
817 
818 	RDLOCK_CONFIG();
819 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
820 	UNLOCK_CONFIG();
821 
822 	/* Get db handle */
823 	result->retcode = get_db_handle(&db);
824 	if (result->retcode != IDMAP_SUCCESS)
825 		goto out;
826 
827 	result->retcode = gen_sql_expr_from_rule(&rule, &expr);
828 	if (result->retcode != IDMAP_SUCCESS)
829 		goto out;
830 
831 	/* Create LIMIT expression. */
832 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
833 		limit = maxlimit;
834 	if (limit > 0)
835 		(void) snprintf(lbuf, sizeof (lbuf),
836 		    "LIMIT %" PRIu64, limit + 1ULL);
837 	else
838 		lbuf[0] = '\0';
839 
840 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
841 
842 	/*
843 	 * Combine all the above into a giant SELECT statement that
844 	 * will return the requested rules
845 	 */
846 	sql = sqlite_mprintf("SELECT rowid, is_user, is_wuser, windomain, "
847 	    "winname_display, is_nt4, unixname, w2u_order, u2w_order "
848 	    "FROM namerules WHERE "
849 	    " %s %s %s;",
850 	    rbuf, expr, lbuf);
851 
852 	if (sql == NULL) {
853 		result->retcode = IDMAP_ERR_MEMORY;
854 		idmapdlog(LOG_ERR, "Out of memory");
855 		goto out;
856 	}
857 
858 	/* Execute the SQL statement and update the return buffer */
859 	PROCESS_LIST_SVC_SQL(retcode, db, IDMAP_DBNAME, sql, limit,
860 	    0, list_namerules_cb, result, result->rules.rules_len);
861 
862 out:
863 	if (expr)
864 		sqlite_freemem(expr);
865 	if (sql)
866 		sqlite_freemem(sql);
867 	if (IDMAP_ERROR(result->retcode))
868 		(void) xdr_free(xdr_idmap_namerules_res, (caddr_t)result);
869 	result->retcode = idmap_stat4prot(result->retcode);
870 	return (TRUE);
871 }
872 
873 #define	IDMAP_RULES_AUTH	"solaris.admin.idmap.rules"
874 static int
875 verify_rules_auth(struct svc_req *rqstp)
876 {
877 	ucred_t		*uc = NULL;
878 	uid_t		uid;
879 	char		buf[1024];
880 	struct passwd	pwd;
881 
882 	if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
883 		idmapdlog(LOG_ERR, "svc_getcallerucred failed during "
884 		    "authorization (%s)", strerror(errno));
885 		return (-1);
886 	}
887 
888 	uid = ucred_geteuid(uc);
889 	if (uid == (uid_t)-1) {
890 		idmapdlog(LOG_ERR, "ucred_geteuid failed during "
891 		    "authorization (%s)", strerror(errno));
892 		ucred_free(uc);
893 		return (-1);
894 	}
895 
896 	if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) {
897 		idmapdlog(LOG_ERR, "getpwuid_r(%u) failed during "
898 		    "authorization (%s)", uid, strerror(errno));
899 		ucred_free(uc);
900 		return (-1);
901 	}
902 
903 	if (chkauthattr(IDMAP_RULES_AUTH, pwd.pw_name) != 1) {
904 		idmapdlog(LOG_INFO, "%s is not authorized (%s)",
905 		    pwd.pw_name, IDMAP_RULES_AUTH);
906 		ucred_free(uc);
907 		return (-1);
908 	}
909 
910 	ucred_free(uc);
911 	return (1);
912 }
913 
914 /*
915  * Meaning of the return values is the following: For retcode ==
916  * IDMAP_SUCCESS, everything went OK and error_index is
917  * undefined. Otherwise, error_index >=0 shows the failed batch
918  * element. errro_index == -1 indicates failure at the beginning,
919  * error_index == -2 at the end.
920  */
921 
922 /* ARGSUSED */
923 bool_t
924 idmap_update_1_svc(idmap_update_batch batch, idmap_update_res *res,
925 		struct svc_req *rqstp)
926 {
927 	sqlite		*db = NULL;
928 	idmap_update_op	*up;
929 	int		i;
930 	int		trans = FALSE;
931 
932 	res->error_index = -1;
933 	(void) memset(&res->error_rule, 0, sizeof (res->error_rule));
934 	(void) memset(&res->conflict_rule, 0, sizeof (res->conflict_rule));
935 
936 	if (verify_rules_auth(rqstp) < 0) {
937 		res->retcode = IDMAP_ERR_PERMISSION_DENIED;
938 		goto out;
939 	}
940 
941 	if (batch.idmap_update_batch_len == 0 ||
942 	    batch.idmap_update_batch_val == NULL) {
943 		res->retcode = IDMAP_SUCCESS;
944 		goto out;
945 	}
946 
947 	res->retcode = validate_rules(&batch);
948 	if (res->retcode != IDMAP_SUCCESS)
949 		goto out;
950 
951 	/* Get db handle */
952 	res->retcode = get_db_handle(&db);
953 	if (res->retcode != IDMAP_SUCCESS)
954 		goto out;
955 
956 	res->retcode = sql_exec_no_cb(db, IDMAP_DBNAME, "BEGIN TRANSACTION;");
957 	if (res->retcode != IDMAP_SUCCESS)
958 		goto out;
959 	trans = TRUE;
960 
961 	for (i = 0; i < batch.idmap_update_batch_len; i++) {
962 		up = &batch.idmap_update_batch_val[i];
963 		switch (up->opnum) {
964 		case OP_NONE:
965 			res->retcode = IDMAP_SUCCESS;
966 			break;
967 		case OP_ADD_NAMERULE:
968 			res->retcode = add_namerule(db,
969 			    &up->idmap_update_op_u.rule);
970 			break;
971 		case OP_RM_NAMERULE:
972 			res->retcode = rm_namerule(db,
973 			    &up->idmap_update_op_u.rule);
974 			break;
975 		case OP_FLUSH_NAMERULES:
976 			res->retcode = flush_namerules(db);
977 			break;
978 		default:
979 			res->retcode = IDMAP_ERR_NOTSUPPORTED;
980 			break;
981 		};
982 
983 		if (res->retcode != IDMAP_SUCCESS) {
984 			res->error_index = i;
985 			if (up->opnum == OP_ADD_NAMERULE ||
986 			    up->opnum == OP_RM_NAMERULE) {
987 				idmap_stat r2 =
988 				    idmap_namerule_cpy(&res->error_rule,
989 				    &up->idmap_update_op_u.rule);
990 				if (r2 != IDMAP_SUCCESS)
991 					res->retcode = r2;
992 			}
993 			goto out;
994 		}
995 	}
996 
997 out:
998 	if (trans) {
999 		if (res->retcode == IDMAP_SUCCESS) {
1000 			res->retcode =
1001 			    sql_exec_no_cb(db, IDMAP_DBNAME,
1002 			    "COMMIT TRANSACTION;");
1003 			if (res->retcode ==  IDMAP_SUCCESS) {
1004 				/*
1005 				 * We've updated the rules.  Expire the cache
1006 				 * so that existing mappings will be
1007 				 * reconsidered.
1008 				 */
1009 				res->retcode =
1010 				    idmap_cache_flush(IDMAP_FLUSH_EXPIRE);
1011 			} else {
1012 				res->error_index = -2;
1013 			}
1014 		}
1015 		else
1016 			(void) sql_exec_no_cb(db, IDMAP_DBNAME,
1017 			    "ROLLBACK TRANSACTION;");
1018 	}
1019 
1020 	res->retcode = idmap_stat4prot(res->retcode);
1021 
1022 	return (TRUE);
1023 }
1024 
1025 static
1026 int
1027 copy_string(char **to, char *from)
1028 {
1029 	if (EMPTY_STRING(from)) {
1030 		*to = NULL;
1031 	} else {
1032 		*to = strdup(from);
1033 		if (*to == NULL) {
1034 			idmapdlog(LOG_ERR, "Out of memory");
1035 			return (IDMAP_ERR_MEMORY);
1036 		}
1037 	}
1038 	return (IDMAP_SUCCESS);
1039 }
1040 
1041 static
1042 int
1043 copy_id(idmap_id *to, idmap_id *from)
1044 {
1045 	(void) memset(to, 0, sizeof (*to));
1046 
1047 	to->idtype = from->idtype;
1048 	if (IS_ID_SID(*from)) {
1049 		idmap_retcode retcode;
1050 
1051 		to->idmap_id_u.sid.rid = from->idmap_id_u.sid.rid;
1052 		retcode = copy_string(&to->idmap_id_u.sid.prefix,
1053 		    from->idmap_id_u.sid.prefix);
1054 
1055 		return (retcode);
1056 	} else {
1057 		to->idmap_id_u.uid = from->idmap_id_u.uid;
1058 		return (IDMAP_SUCCESS);
1059 	}
1060 }
1061 
1062 static
1063 int
1064 copy_mapping(idmap_mapping *mapping, idmap_mapping *request)
1065 {
1066 	idmap_retcode retcode;
1067 
1068 	(void) memset(mapping, 0, sizeof (*mapping));
1069 
1070 	mapping->flag = request->flag;
1071 	mapping->direction = _IDMAP_F_DONE;
1072 
1073 	retcode = copy_id(&mapping->id1, &request->id1);
1074 	if (retcode != IDMAP_SUCCESS)
1075 		goto errout;
1076 
1077 	retcode = copy_string(&mapping->id1domain, request->id1domain);
1078 	if (retcode != IDMAP_SUCCESS)
1079 		goto errout;
1080 
1081 	retcode = copy_string(&mapping->id1name, request->id1name);
1082 	if (retcode != IDMAP_SUCCESS)
1083 		goto errout;
1084 
1085 	retcode = copy_id(&mapping->id2, &request->id2);
1086 	if (retcode != IDMAP_SUCCESS)
1087 		goto errout;
1088 
1089 	retcode = copy_string(&mapping->id2domain, request->id2domain);
1090 	if (retcode != IDMAP_SUCCESS)
1091 		goto errout;
1092 	retcode = copy_string(&mapping->id2name, request->id2name);
1093 	if (retcode != IDMAP_SUCCESS)
1094 		goto errout;
1095 
1096 	return (IDMAP_SUCCESS);
1097 
1098 errout:
1099 	if (IS_ID_SID(mapping->id1))
1100 		free(mapping->id1.idmap_id_u.sid.prefix);
1101 	free(mapping->id1domain);
1102 	free(mapping->id1name);
1103 	if (IS_ID_SID(mapping->id2))
1104 		free(mapping->id2.idmap_id_u.sid.prefix);
1105 	free(mapping->id2domain);
1106 	free(mapping->id2name);
1107 
1108 	(void) memset(mapping, 0, sizeof (*mapping));
1109 	return (retcode);
1110 }
1111 
1112 
1113 /* ARGSUSED */
1114 bool_t
1115 idmap_get_mapped_id_by_name_1_svc(idmap_mapping request,
1116 		idmap_mappings_res *result, struct svc_req *rqstp)
1117 {
1118 	idmap_mapping_batch batch_request;
1119 	idmap_ids_res batch_result;
1120 	idmap_mapping *map;
1121 
1122 	/* Clear out things we might want to xdr_free on error */
1123 	(void) memset(&batch_result, 0, sizeof (batch_result));
1124 	(void) memset(result, 0, sizeof (*result));
1125 
1126 	result->retcode = validate_mapped_id_by_name_req(&request);
1127 	if (result->retcode != IDMAP_SUCCESS)
1128 		goto out;
1129 
1130 	/*
1131 	 * Copy the request.  We need to modify it, and
1132 	 * what we have is a shallow copy.  Freeing pointers from
1133 	 * our copy will lead to problems, since the RPC framework
1134 	 * has its own copy of those pointers.  Besides, we need
1135 	 * a copy to return.
1136 	 */
1137 	map = calloc(1, sizeof (idmap_mapping));
1138 	if (map == NULL) {
1139 		idmapdlog(LOG_ERR, "Out of memory");
1140 		result->retcode = IDMAP_ERR_MEMORY;
1141 		goto out;
1142 	}
1143 
1144 	/*
1145 	 * Set up to return the filled-in mapping structure.
1146 	 * Note that we xdr_free result on error, and that'll take
1147 	 * care of freeing the mapping structure.
1148 	 */
1149 	result->mappings.mappings_val = map;
1150 	result->mappings.mappings_len = 1;
1151 
1152 	result->retcode = copy_mapping(map, &request);
1153 	if (result->retcode != IDMAP_SUCCESS)
1154 		goto out;
1155 
1156 	/* Set up for the request to the batch API */
1157 	batch_request.idmap_mapping_batch_val = map;
1158 	batch_request.idmap_mapping_batch_len = 1;
1159 
1160 	/* Do the real work. */
1161 	(void) idmap_get_mapped_ids_1_svc(batch_request,
1162 	    &batch_result, rqstp);
1163 
1164 	/* Copy what we need out of the batch response */
1165 
1166 	if (batch_result.retcode != IDMAP_SUCCESS) {
1167 		result->retcode = batch_result.retcode;
1168 		goto out;
1169 	}
1170 
1171 	result->retcode = copy_id(&map->id2, &batch_result.ids.ids_val[0].id);
1172 	if (result->retcode != IDMAP_SUCCESS)
1173 		goto out;
1174 
1175 	map->direction = batch_result.ids.ids_val[0].direction;
1176 
1177 	result->retcode = batch_result.ids.ids_val[0].retcode;
1178 
1179 	idmap_info_mov(&map->info, &batch_result.ids.ids_val[0].info);
1180 
1181 out:
1182 	if (IDMAP_FATAL_ERROR(result->retcode)) {
1183 		xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
1184 		result->mappings.mappings_len = 0;
1185 		result->mappings.mappings_val = NULL;
1186 	}
1187 	result->retcode = idmap_stat4prot(result->retcode);
1188 
1189 	xdr_free(xdr_idmap_ids_res, (char *)&batch_result);
1190 
1191 	return (TRUE);
1192 }
1193 
1194 /* ARGSUSED */
1195 bool_t
1196 idmap_get_prop_1_svc(idmap_prop_type request,
1197 		idmap_prop_res *result, struct svc_req *rqstp)
1198 {
1199 	idmap_pg_config_t *pgcfg;
1200 
1201 	/* Init */
1202 	(void) memset(result, 0, sizeof (*result));
1203 	result->retcode = IDMAP_SUCCESS;
1204 	result->value.prop = request;
1205 
1206 	RDLOCK_CONFIG();
1207 
1208 	/* Just shortcuts: */
1209 	pgcfg = &_idmapdstate.cfg->pgcfg;
1210 
1211 
1212 	switch (request) {
1213 	case PROP_LIST_SIZE_LIMIT:
1214 		result->value.idmap_prop_val_u.intval = pgcfg->list_size_limit;
1215 		result->auto_discovered = FALSE;
1216 		break;
1217 	case PROP_DEFAULT_DOMAIN:
1218 		result->auto_discovered = FALSE;
1219 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1220 		    pgcfg->default_domain);
1221 		break;
1222 	case PROP_DOMAIN_NAME:
1223 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1224 		    pgcfg->domain_name);
1225 		result->auto_discovered =
1226 		    pgcfg->domain_name_auto_disc;
1227 		break;
1228 	case PROP_MACHINE_SID:
1229 		result->auto_discovered = FALSE;
1230 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1231 		    pgcfg->machine_sid);
1232 		break;
1233 	case PROP_DOMAIN_CONTROLLER:
1234 		if (pgcfg->domain_controller != NULL) {
1235 			(void) memcpy(&result->value.idmap_prop_val_u.dsval,
1236 			    pgcfg->domain_controller,
1237 			    sizeof (idmap_ad_disc_ds_t));
1238 		}
1239 		result->auto_discovered = pgcfg->domain_controller_auto_disc;
1240 		break;
1241 	case PROP_FOREST_NAME:
1242 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1243 		    pgcfg->forest_name);
1244 		result->auto_discovered = pgcfg->forest_name_auto_disc;
1245 		break;
1246 	case PROP_SITE_NAME:
1247 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1248 		    pgcfg->site_name);
1249 		result->auto_discovered = pgcfg->site_name_auto_disc;
1250 		break;
1251 	case PROP_GLOBAL_CATALOG:
1252 		if (pgcfg->global_catalog != NULL) {
1253 			(void) memcpy(&result->value.idmap_prop_val_u.dsval,
1254 			    pgcfg->global_catalog, sizeof (idmap_ad_disc_ds_t));
1255 		}
1256 		result->auto_discovered = pgcfg->global_catalog_auto_disc;
1257 		break;
1258 	case PROP_AD_UNIXUSER_ATTR:
1259 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1260 		    pgcfg->ad_unixuser_attr);
1261 		result->auto_discovered = FALSE;
1262 		break;
1263 	case PROP_AD_UNIXGROUP_ATTR:
1264 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1265 		    pgcfg->ad_unixgroup_attr);
1266 		result->auto_discovered = FALSE;
1267 		break;
1268 	case PROP_NLDAP_WINNAME_ATTR:
1269 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1270 		    pgcfg->nldap_winname_attr);
1271 		result->auto_discovered = FALSE;
1272 		break;
1273 	case PROP_DIRECTORY_BASED_MAPPING:
1274 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1275 		    enum_lookup(pgcfg->directory_based_mapping,
1276 		    directory_mapping_map));
1277 		result->auto_discovered = FALSE;
1278 		break;
1279 	default:
1280 		result->retcode = IDMAP_ERR_PROP_UNKNOWN;
1281 		break;
1282 	}
1283 
1284 out:
1285 	UNLOCK_CONFIG();
1286 	if (IDMAP_FATAL_ERROR(result->retcode)) {
1287 		xdr_free(xdr_idmap_prop_res, (caddr_t)result);
1288 		result->value.prop = PROP_UNKNOWN;
1289 	}
1290 	result->retcode = idmap_stat4prot(result->retcode);
1291 	return (TRUE);
1292 }
1293 
1294 int
1295 idmap_flush_1_svc(
1296     idmap_flush_op  op,
1297     idmap_retcode *result,
1298     struct svc_req *rqstp)
1299 {
1300 	NOTE(ARGUNUSED(rqstp))
1301 	if (verify_rules_auth(rqstp) < 0) {
1302 		*result = IDMAP_ERR_PERMISSION_DENIED;
1303 		return (TRUE);
1304 	}
1305 
1306 	*result = idmap_cache_flush(op);
1307 
1308 	return (TRUE);
1309 }
1310 
1311 /* ARGSUSED */
1312 int
1313 idmap_prog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,
1314 		caddr_t result)
1315 {
1316 	(void) xdr_free(xdr_result, result);
1317 	return (TRUE);
1318 }
1319 
1320 /*
1321  * This function is called by rpc_svc.c when it encounters an error.
1322  */
1323 NOTE(PRINTFLIKE(1))
1324 void
1325 idmap_rpc_msgout(const char *fmt, ...)
1326 {
1327 	va_list va;
1328 	char buf[1000];
1329 
1330 	va_start(va, fmt);
1331 	(void) vsnprintf(buf, sizeof (buf), fmt, va);
1332 	va_end(va);
1333 
1334 	idmapdlog(LOG_ERR, "idmap RPC:  %s", buf);
1335 }
1336