xref: /illumos-gate/usr/src/cmd/idmap/idmapd/server.c (revision a28480febf31f0e61debac062a55216a98a05a92)
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 		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, struct svc_req *rqstp)
802 {
803 
804 	sqlite		*db = NULL;
805 	char		lbuf[30], rbuf[30];
806 	char		*sql = NULL;
807 	char		*expr = NULL;
808 	uint64_t	maxlimit;
809 	idmap_retcode	retcode;
810 
811 	(void) memset(result, 0, sizeof (*result));
812 
813 	result->retcode = validate_rule(&rule);
814 	if (result->retcode != IDMAP_SUCCESS)
815 		goto out;
816 
817 	RDLOCK_CONFIG();
818 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
819 	UNLOCK_CONFIG();
820 
821 	/* Get db handle */
822 	result->retcode = get_db_handle(&db);
823 	if (result->retcode != IDMAP_SUCCESS)
824 		goto out;
825 
826 	result->retcode = gen_sql_expr_from_rule(&rule, &expr);
827 	if (result->retcode != IDMAP_SUCCESS)
828 		goto out;
829 
830 	/* Create LIMIT expression. */
831 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
832 		limit = maxlimit;
833 	if (limit > 0)
834 		(void) snprintf(lbuf, sizeof (lbuf),
835 		    "LIMIT %" PRIu64, limit + 1ULL);
836 	else
837 		lbuf[0] = '\0';
838 
839 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
840 
841 	/*
842 	 * Combine all the above into a giant SELECT statement that
843 	 * will return the requested rules
844 	 */
845 	sql = sqlite_mprintf("SELECT rowid, is_user, is_wuser, windomain, "
846 	    "winname_display, is_nt4, unixname, w2u_order, u2w_order "
847 	    "FROM namerules WHERE "
848 	    " %s %s %s;",
849 	    rbuf, expr, lbuf);
850 
851 	if (sql == NULL) {
852 		result->retcode = IDMAP_ERR_MEMORY;
853 		idmapdlog(LOG_ERR, "Out of memory");
854 		goto out;
855 	}
856 
857 	/* Execute the SQL statement and update the return buffer */
858 	PROCESS_LIST_SVC_SQL(retcode, db, IDMAP_DBNAME, sql, limit,
859 	    0, list_namerules_cb, result, result->rules.rules_len);
860 
861 out:
862 	if (expr)
863 		sqlite_freemem(expr);
864 	if (sql)
865 		sqlite_freemem(sql);
866 	if (IDMAP_ERROR(result->retcode))
867 		xdr_free(xdr_idmap_namerules_res, (caddr_t)result);
868 	result->retcode = idmap_stat4prot(result->retcode);
869 	return (TRUE);
870 }
871 
872 #define	IDMAP_RULES_AUTH	"solaris.admin.idmap.rules"
873 static int
874 verify_rules_auth(struct svc_req *rqstp)
875 {
876 	ucred_t		*uc = NULL;
877 	uid_t		uid;
878 	char		buf[1024];
879 	struct passwd	pwd;
880 
881 	if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
882 		idmapdlog(LOG_ERR, "svc_getcallerucred failed during "
883 		    "authorization (%s)", strerror(errno));
884 		return (-1);
885 	}
886 
887 	uid = ucred_geteuid(uc);
888 	if (uid == (uid_t)-1) {
889 		idmapdlog(LOG_ERR, "ucred_geteuid failed during "
890 		    "authorization (%s)", strerror(errno));
891 		ucred_free(uc);
892 		return (-1);
893 	}
894 
895 	if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) {
896 		idmapdlog(LOG_ERR, "getpwuid_r(%u) failed during "
897 		    "authorization (%s)", uid, strerror(errno));
898 		ucred_free(uc);
899 		return (-1);
900 	}
901 
902 	if (chkauthattr(IDMAP_RULES_AUTH, pwd.pw_name) != 1) {
903 		idmapdlog(LOG_INFO, "%s is not authorized (%s)",
904 		    pwd.pw_name, IDMAP_RULES_AUTH);
905 		ucred_free(uc);
906 		return (-1);
907 	}
908 
909 	ucred_free(uc);
910 	return (1);
911 }
912 
913 /*
914  * Meaning of the return values is the following: For retcode ==
915  * IDMAP_SUCCESS, everything went OK and error_index is
916  * undefined. Otherwise, error_index >=0 shows the failed batch
917  * element. errro_index == -1 indicates failure at the beginning,
918  * error_index == -2 at the end.
919  */
920 
921 /* ARGSUSED */
922 bool_t
923 idmap_update_1_svc(idmap_update_batch batch, idmap_update_res *res,
924     struct svc_req *rqstp)
925 {
926 	sqlite		*db = NULL;
927 	idmap_update_op	*up;
928 	int		i;
929 	int		trans = FALSE;
930 
931 	res->error_index = -1;
932 	(void) memset(&res->error_rule, 0, sizeof (res->error_rule));
933 	(void) memset(&res->conflict_rule, 0, sizeof (res->conflict_rule));
934 
935 	if (verify_rules_auth(rqstp) < 0) {
936 		res->retcode = IDMAP_ERR_PERMISSION_DENIED;
937 		goto out;
938 	}
939 
940 	if (batch.idmap_update_batch_len == 0 ||
941 	    batch.idmap_update_batch_val == NULL) {
942 		res->retcode = IDMAP_SUCCESS;
943 		goto out;
944 	}
945 
946 	res->retcode = validate_rules(&batch);
947 	if (res->retcode != IDMAP_SUCCESS)
948 		goto out;
949 
950 	/* Get db handle */
951 	res->retcode = get_db_handle(&db);
952 	if (res->retcode != IDMAP_SUCCESS)
953 		goto out;
954 
955 	res->retcode = sql_exec_no_cb(db, IDMAP_DBNAME, "BEGIN TRANSACTION;");
956 	if (res->retcode != IDMAP_SUCCESS)
957 		goto out;
958 	trans = TRUE;
959 
960 	for (i = 0; i < batch.idmap_update_batch_len; i++) {
961 		up = &batch.idmap_update_batch_val[i];
962 		switch (up->opnum) {
963 		case OP_NONE:
964 			res->retcode = IDMAP_SUCCESS;
965 			break;
966 		case OP_ADD_NAMERULE:
967 			res->retcode = add_namerule(db,
968 			    &up->idmap_update_op_u.rule);
969 			break;
970 		case OP_RM_NAMERULE:
971 			res->retcode = rm_namerule(db,
972 			    &up->idmap_update_op_u.rule);
973 			break;
974 		case OP_FLUSH_NAMERULES:
975 			res->retcode = flush_namerules(db);
976 			break;
977 		default:
978 			res->retcode = IDMAP_ERR_NOTSUPPORTED;
979 			break;
980 		};
981 
982 		if (res->retcode != IDMAP_SUCCESS) {
983 			res->error_index = i;
984 			if (up->opnum == OP_ADD_NAMERULE ||
985 			    up->opnum == OP_RM_NAMERULE) {
986 				idmap_stat r2 =
987 				    idmap_namerule_cpy(&res->error_rule,
988 				    &up->idmap_update_op_u.rule);
989 				if (r2 != IDMAP_SUCCESS)
990 					res->retcode = r2;
991 			}
992 			goto out;
993 		}
994 	}
995 
996 out:
997 	if (trans) {
998 		if (res->retcode == IDMAP_SUCCESS) {
999 			res->retcode =
1000 			    sql_exec_no_cb(db, IDMAP_DBNAME,
1001 			    "COMMIT TRANSACTION;");
1002 			if (res->retcode ==  IDMAP_SUCCESS) {
1003 				/*
1004 				 * We've updated the rules.  Expire the cache
1005 				 * so that existing mappings will be
1006 				 * reconsidered.
1007 				 */
1008 				res->retcode =
1009 				    idmap_cache_flush(IDMAP_FLUSH_EXPIRE);
1010 			} else {
1011 				res->error_index = -2;
1012 			}
1013 		}
1014 		else
1015 			(void) sql_exec_no_cb(db, IDMAP_DBNAME,
1016 			    "ROLLBACK TRANSACTION;");
1017 	}
1018 
1019 	res->retcode = idmap_stat4prot(res->retcode);
1020 
1021 	return (TRUE);
1022 }
1023 
1024 static
1025 int
1026 copy_string(char **to, char *from)
1027 {
1028 	if (EMPTY_STRING(from)) {
1029 		*to = NULL;
1030 	} else {
1031 		*to = strdup(from);
1032 		if (*to == NULL) {
1033 			idmapdlog(LOG_ERR, "Out of memory");
1034 			return (IDMAP_ERR_MEMORY);
1035 		}
1036 	}
1037 	return (IDMAP_SUCCESS);
1038 }
1039 
1040 static
1041 int
1042 copy_id(idmap_id *to, idmap_id *from)
1043 {
1044 	(void) memset(to, 0, sizeof (*to));
1045 
1046 	to->idtype = from->idtype;
1047 	if (IS_ID_SID(*from)) {
1048 		idmap_retcode retcode;
1049 
1050 		to->idmap_id_u.sid.rid = from->idmap_id_u.sid.rid;
1051 		retcode = copy_string(&to->idmap_id_u.sid.prefix,
1052 		    from->idmap_id_u.sid.prefix);
1053 
1054 		return (retcode);
1055 	} else {
1056 		to->idmap_id_u.uid = from->idmap_id_u.uid;
1057 		return (IDMAP_SUCCESS);
1058 	}
1059 }
1060 
1061 static
1062 int
1063 copy_mapping(idmap_mapping *mapping, idmap_mapping *request)
1064 {
1065 	idmap_retcode retcode;
1066 
1067 	(void) memset(mapping, 0, sizeof (*mapping));
1068 
1069 	mapping->flag = request->flag;
1070 	mapping->direction = _IDMAP_F_DONE;
1071 
1072 	retcode = copy_id(&mapping->id1, &request->id1);
1073 	if (retcode != IDMAP_SUCCESS)
1074 		goto errout;
1075 
1076 	retcode = copy_string(&mapping->id1domain, request->id1domain);
1077 	if (retcode != IDMAP_SUCCESS)
1078 		goto errout;
1079 
1080 	retcode = copy_string(&mapping->id1name, request->id1name);
1081 	if (retcode != IDMAP_SUCCESS)
1082 		goto errout;
1083 
1084 	retcode = copy_id(&mapping->id2, &request->id2);
1085 	if (retcode != IDMAP_SUCCESS)
1086 		goto errout;
1087 
1088 	retcode = copy_string(&mapping->id2domain, request->id2domain);
1089 	if (retcode != IDMAP_SUCCESS)
1090 		goto errout;
1091 	retcode = copy_string(&mapping->id2name, request->id2name);
1092 	if (retcode != IDMAP_SUCCESS)
1093 		goto errout;
1094 
1095 	return (IDMAP_SUCCESS);
1096 
1097 errout:
1098 	if (IS_ID_SID(mapping->id1))
1099 		free(mapping->id1.idmap_id_u.sid.prefix);
1100 	free(mapping->id1domain);
1101 	free(mapping->id1name);
1102 	if (IS_ID_SID(mapping->id2))
1103 		free(mapping->id2.idmap_id_u.sid.prefix);
1104 	free(mapping->id2domain);
1105 	free(mapping->id2name);
1106 
1107 	(void) memset(mapping, 0, sizeof (*mapping));
1108 	return (retcode);
1109 }
1110 
1111 
1112 /* ARGSUSED */
1113 bool_t
1114 idmap_get_mapped_id_by_name_1_svc(idmap_mapping request,
1115     idmap_mappings_res *result, struct svc_req *rqstp)
1116 {
1117 	idmap_mapping_batch batch_request;
1118 	idmap_ids_res batch_result;
1119 	idmap_mapping *map;
1120 
1121 	/* Clear out things we might want to xdr_free on error */
1122 	(void) memset(&batch_result, 0, sizeof (batch_result));
1123 	(void) memset(result, 0, sizeof (*result));
1124 
1125 	result->retcode = validate_mapped_id_by_name_req(&request);
1126 	if (result->retcode != IDMAP_SUCCESS)
1127 		goto out;
1128 
1129 	/*
1130 	 * Copy the request.  We need to modify it, and
1131 	 * what we have is a shallow copy.  Freeing pointers from
1132 	 * our copy will lead to problems, since the RPC framework
1133 	 * has its own copy of those pointers.  Besides, we need
1134 	 * a copy to return.
1135 	 */
1136 	map = calloc(1, sizeof (idmap_mapping));
1137 	if (map == NULL) {
1138 		idmapdlog(LOG_ERR, "Out of memory");
1139 		result->retcode = IDMAP_ERR_MEMORY;
1140 		goto out;
1141 	}
1142 
1143 	/*
1144 	 * Set up to return the filled-in mapping structure.
1145 	 * Note that we xdr_free result on error, and that'll take
1146 	 * care of freeing the mapping structure.
1147 	 */
1148 	result->mappings.mappings_val = map;
1149 	result->mappings.mappings_len = 1;
1150 
1151 	result->retcode = copy_mapping(map, &request);
1152 	if (result->retcode != IDMAP_SUCCESS)
1153 		goto out;
1154 
1155 	/* Set up for the request to the batch API */
1156 	batch_request.idmap_mapping_batch_val = map;
1157 	batch_request.idmap_mapping_batch_len = 1;
1158 
1159 	/* Do the real work. */
1160 	(void) idmap_get_mapped_ids_1_svc(batch_request,
1161 	    &batch_result, rqstp);
1162 
1163 	/* Copy what we need out of the batch response */
1164 
1165 	if (batch_result.retcode != IDMAP_SUCCESS) {
1166 		result->retcode = batch_result.retcode;
1167 		goto out;
1168 	}
1169 
1170 	result->retcode = copy_id(&map->id2, &batch_result.ids.ids_val[0].id);
1171 	if (result->retcode != IDMAP_SUCCESS)
1172 		goto out;
1173 
1174 	map->direction = batch_result.ids.ids_val[0].direction;
1175 
1176 	result->retcode = batch_result.ids.ids_val[0].retcode;
1177 
1178 	idmap_info_mov(&map->info, &batch_result.ids.ids_val[0].info);
1179 
1180 out:
1181 	if (IDMAP_FATAL_ERROR(result->retcode)) {
1182 		xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
1183 		result->mappings.mappings_len = 0;
1184 		result->mappings.mappings_val = NULL;
1185 	}
1186 	result->retcode = idmap_stat4prot(result->retcode);
1187 
1188 	xdr_free(xdr_idmap_ids_res, (char *)&batch_result);
1189 
1190 	return (TRUE);
1191 }
1192 
1193 /* ARGSUSED */
1194 bool_t
1195 idmap_get_prop_1_svc(idmap_prop_type request,
1196     idmap_prop_res *result, struct svc_req *rqstp)
1197 {
1198 	idmap_pg_config_t *pgcfg;
1199 
1200 	/* Init */
1201 	(void) memset(result, 0, sizeof (*result));
1202 	result->retcode = IDMAP_SUCCESS;
1203 	result->value.prop = request;
1204 
1205 	RDLOCK_CONFIG();
1206 
1207 	/* Just shortcuts: */
1208 	pgcfg = &_idmapdstate.cfg->pgcfg;
1209 
1210 
1211 	switch (request) {
1212 	case PROP_LIST_SIZE_LIMIT:
1213 		result->value.idmap_prop_val_u.intval = pgcfg->list_size_limit;
1214 		result->auto_discovered = FALSE;
1215 		break;
1216 	case PROP_DEFAULT_DOMAIN:
1217 		result->auto_discovered = FALSE;
1218 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1219 		    pgcfg->default_domain);
1220 		break;
1221 	case PROP_DOMAIN_NAME:
1222 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1223 		    pgcfg->domain_name);
1224 		result->auto_discovered =
1225 		    pgcfg->domain_name_auto_disc;
1226 		break;
1227 	case PROP_MACHINE_SID:
1228 		result->auto_discovered = FALSE;
1229 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1230 		    pgcfg->machine_sid);
1231 		break;
1232 	case PROP_DOMAIN_CONTROLLER:
1233 		if (pgcfg->domain_controller != NULL) {
1234 			(void) memcpy(&result->value.idmap_prop_val_u.dsval,
1235 			    pgcfg->domain_controller,
1236 			    sizeof (idmap_ad_disc_ds_t));
1237 		}
1238 		result->auto_discovered = pgcfg->domain_controller_auto_disc;
1239 		break;
1240 	case PROP_FOREST_NAME:
1241 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1242 		    pgcfg->forest_name);
1243 		result->auto_discovered = pgcfg->forest_name_auto_disc;
1244 		break;
1245 	case PROP_SITE_NAME:
1246 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1247 		    pgcfg->site_name);
1248 		result->auto_discovered = pgcfg->site_name_auto_disc;
1249 		break;
1250 	case PROP_GLOBAL_CATALOG:
1251 		if (pgcfg->global_catalog != NULL) {
1252 			(void) memcpy(&result->value.idmap_prop_val_u.dsval,
1253 			    pgcfg->global_catalog, sizeof (idmap_ad_disc_ds_t));
1254 		}
1255 		result->auto_discovered = pgcfg->global_catalog_auto_disc;
1256 		break;
1257 	case PROP_AD_UNIXUSER_ATTR:
1258 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1259 		    pgcfg->ad_unixuser_attr);
1260 		result->auto_discovered = FALSE;
1261 		break;
1262 	case PROP_AD_UNIXGROUP_ATTR:
1263 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1264 		    pgcfg->ad_unixgroup_attr);
1265 		result->auto_discovered = FALSE;
1266 		break;
1267 	case PROP_NLDAP_WINNAME_ATTR:
1268 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1269 		    pgcfg->nldap_winname_attr);
1270 		result->auto_discovered = FALSE;
1271 		break;
1272 	case PROP_DIRECTORY_BASED_MAPPING:
1273 		STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val,
1274 		    enum_lookup(pgcfg->directory_based_mapping,
1275 		    directory_mapping_map));
1276 		result->auto_discovered = FALSE;
1277 		break;
1278 	default:
1279 		result->retcode = IDMAP_ERR_PROP_UNKNOWN;
1280 		break;
1281 	}
1282 
1283 out:
1284 	UNLOCK_CONFIG();
1285 	if (IDMAP_FATAL_ERROR(result->retcode)) {
1286 		xdr_free(xdr_idmap_prop_res, (caddr_t)result);
1287 		result->value.prop = PROP_UNKNOWN;
1288 	}
1289 	result->retcode = idmap_stat4prot(result->retcode);
1290 	return (TRUE);
1291 }
1292 
1293 int
1294 idmap_flush_1_svc(
1295     idmap_flush_op  op,
1296     idmap_retcode *result,
1297     struct svc_req *rqstp)
1298 {
1299 	NOTE(ARGUNUSED(rqstp))
1300 	if (verify_rules_auth(rqstp) < 0) {
1301 		*result = IDMAP_ERR_PERMISSION_DENIED;
1302 		return (TRUE);
1303 	}
1304 
1305 	*result = idmap_cache_flush(op);
1306 
1307 	return (TRUE);
1308 }
1309 
1310 /* ARGSUSED */
1311 int
1312 idmap_prog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,
1313     caddr_t result)
1314 {
1315 	xdr_free(xdr_result, result);
1316 	return (TRUE);
1317 }
1318 
1319 /*
1320  * This function is called by rpc_svc.c when it encounters an error.
1321  */
1322 NOTE(PRINTFLIKE(1))
1323 void
1324 idmap_rpc_msgout(const char *fmt, ...)
1325 {
1326 	va_list va;
1327 	char buf[1000];
1328 
1329 	va_start(va, fmt);
1330 	(void) vsnprintf(buf, sizeof (buf), fmt, va);
1331 	va_end(va);
1332 
1333 	idmapdlog(LOG_ERR, "idmap RPC:  %s", buf);
1334 }
1335