xref: /illumos-gate/usr/src/cmd/idmap/idmapd/server.c (revision 2850d85b7b93f31e578520dc3b3feb24db609c62)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Service routines
30  */
31 
32 #include "idmapd.h"
33 #include "idmap_priv.h"
34 #include "nldaputils.h"
35 #include <signal.h>
36 #include <thread.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <ucred.h>
44 #include <pwd.h>
45 #include <auth_attr.h>
46 #include <secdb.h>
47 
48 #define	_VALIDATE_LIST_CB_DATA(col, val, siz)\
49 	retcode = validate_list_cb_data(cb_data, argc, argv, col,\
50 			(uchar_t **)val, siz);\
51 	if (retcode == IDMAP_NEXT) {\
52 		result->retcode = IDMAP_NEXT;\
53 		return (0);\
54 	} else if (retcode < 0) {\
55 		result->retcode = retcode;\
56 		return (1);\
57 	}
58 
59 #define	PROCESS_LIST_SVC_SQL(rcode, db, sql, limit, cb, res, len)\
60 	rcode = process_list_svc_sql(db, sql, limit, cb, res);\
61 	if (rcode == IDMAP_ERR_BUSY)\
62 		res->retcode = IDMAP_ERR_BUSY;\
63 	else if (rcode == IDMAP_SUCCESS && len == 0)\
64 		res->retcode = IDMAP_ERR_NOTFOUND;
65 
66 
67 #define	STRDUP_OR_FAIL(to, from) \
68 	if ((from) == NULL) \
69 		to = NULL; \
70 	else { \
71 		if ((to = strdup(from)) == NULL) \
72 			return (1); \
73 	}
74 
75 /* ARGSUSED */
76 bool_t
77 idmap_null_1_svc(void *result, struct svc_req *rqstp)
78 {
79 	return (TRUE);
80 }
81 
82 /*
83  * RPC layer allocates empty strings to replace NULL char *.
84  * This utility function frees these empty strings.
85  */
86 static
87 void
88 sanitize_mapping_request(idmap_mapping *req)
89 {
90 	free(req->id1name);
91 	req->id1name = NULL;
92 	free(req->id1domain);
93 	req->id1domain = NULL;
94 	free(req->id2name);
95 	req->id2name = NULL;
96 	free(req->id2domain);
97 	req->id2domain = NULL;
98 	req->direction = _IDMAP_F_DONE;
99 }
100 
101 /* ARGSUSED */
102 bool_t
103 idmap_get_mapped_ids_1_svc(idmap_mapping_batch batch,
104 		idmap_ids_res *result, struct svc_req *rqstp)
105 {
106 	sqlite		*cache = NULL, *db = NULL;
107 	lookup_state_t	state;
108 	idmap_retcode	retcode;
109 	uint_t		i;
110 
111 	/* Init */
112 	(void) memset(result, 0, sizeof (*result));
113 	(void) memset(&state, 0, sizeof (state));
114 
115 	/* Return success if nothing was requested */
116 	if (batch.idmap_mapping_batch_len < 1)
117 		goto out;
118 
119 	/* Get cache handle */
120 	result->retcode = get_cache_handle(&cache);
121 	if (result->retcode != IDMAP_SUCCESS)
122 		goto out;
123 
124 	/* Get db handle */
125 	result->retcode = get_db_handle(&db);
126 	if (result->retcode != IDMAP_SUCCESS)
127 		goto out;
128 
129 	/* Allocate result array */
130 	result->ids.ids_val = calloc(batch.idmap_mapping_batch_len,
131 	    sizeof (idmap_id_res));
132 	if (result->ids.ids_val == NULL) {
133 		idmapdlog(LOG_ERR, "Out of memory");
134 		result->retcode = IDMAP_ERR_MEMORY;
135 		goto out;
136 	}
137 	result->ids.ids_len = batch.idmap_mapping_batch_len;
138 
139 	/* Allocate hash table to check for duplicate sids */
140 	state.sid_history = calloc(batch.idmap_mapping_batch_len,
141 	    sizeof (*state.sid_history));
142 	if (state.sid_history == NULL) {
143 		idmapdlog(LOG_ERR, "Out of memory");
144 		result->retcode = IDMAP_ERR_MEMORY;
145 		goto out;
146 	}
147 	state.sid_history_size = batch.idmap_mapping_batch_len;
148 	for (i = 0; i < state.sid_history_size; i++) {
149 		state.sid_history[i].key = state.sid_history_size;
150 		state.sid_history[i].next = state.sid_history_size;
151 	}
152 	state.batch = &batch;
153 	state.result = result;
154 
155 	/* Get directory-based name mapping info */
156 	result->retcode = get_ds_namemap_type(&state);
157 	if (result->retcode != IDMAP_SUCCESS)
158 		goto out;
159 
160 	/* Init our 'done' flags */
161 	state.sid2pid_done = state.pid2sid_done = TRUE;
162 
163 	/* First stage */
164 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
165 		state.curpos = i;
166 		(void) sanitize_mapping_request(
167 		    &batch.idmap_mapping_batch_val[i]);
168 		if (IS_BATCH_SID(batch, i)) {
169 			retcode = sid2pid_first_pass(
170 			    &state,
171 			    cache,
172 			    &batch.idmap_mapping_batch_val[i],
173 			    &result->ids.ids_val[i]);
174 		} else if (IS_BATCH_UID(batch, i)) {
175 			retcode = pid2sid_first_pass(
176 			    &state,
177 			    cache,
178 			    &batch.idmap_mapping_batch_val[i],
179 			    &result->ids.ids_val[i], 1, 0);
180 		} else if (IS_BATCH_GID(batch, i)) {
181 			retcode = pid2sid_first_pass(
182 			    &state,
183 			    cache,
184 			    &batch.idmap_mapping_batch_val[i],
185 			    &result->ids.ids_val[i], 0, 0);
186 		} else {
187 			result->ids.ids_val[i].retcode = IDMAP_ERR_IDTYPE;
188 			continue;
189 		}
190 		if (IDMAP_FATAL_ERROR(retcode)) {
191 			result->retcode = retcode;
192 			goto out;
193 		}
194 	}
195 
196 	/* Check if we are done */
197 	if (state.sid2pid_done == TRUE && state.pid2sid_done == TRUE)
198 		goto out;
199 
200 	/*
201 	 * native LDAP lookups:
202 	 * If nldap or mixed mode is enabled then pid2sid mapping requests
203 	 * need to lookup native LDAP directory service by uid/gid to get
204 	 * winname and unixname.
205 	 */
206 	if (state.nldap_nqueries) {
207 		retcode = nldap_lookup_batch(&state, &batch, result);
208 		if (IDMAP_FATAL_ERROR(retcode)) {
209 			result->retcode = retcode;
210 			goto out;
211 		}
212 	}
213 
214 	/*
215 	 * AD lookups:
216 	 * 1. The pid2sid requests in the preceding step which successfully
217 	 *    retrieved winname from native LDAP objects will now need to
218 	 *    lookup AD by winname to get sid.
219 	 * 2. The sid2pid requests will need to lookup AD by sid to get
220 	 *    winname and unixname (AD or mixed mode).
221 	 * 3. If AD-based name mapping is enabled then pid2sid mapping
222 	 *    requests need to lookup AD by unixname to get winname and sid.
223 	 */
224 	if (state.ad_nqueries) {
225 		retcode = ad_lookup_batch(&state, &batch, result);
226 		if (IDMAP_FATAL_ERROR(retcode)) {
227 			result->retcode = retcode;
228 			goto out;
229 		}
230 	}
231 
232 	/*
233 	 * native LDAP lookups:
234 	 * If nldap mode is enabled then sid2pid mapping requests
235 	 * which successfully retrieved winname from AD objects in the
236 	 * preceding step, will now need to lookup native LDAP directory
237 	 * service by winname to get unixname and pid.
238 	 */
239 	if (state.nldap_nqueries) {
240 		retcode = nldap_lookup_batch(&state, &batch, result);
241 		if (IDMAP_FATAL_ERROR(retcode)) {
242 			result->retcode = retcode;
243 			goto out;
244 		}
245 	}
246 
247 	/* Reset 'done' flags */
248 	state.sid2pid_done = state.pid2sid_done = TRUE;
249 
250 	/* Second stage */
251 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
252 		state.curpos = i;
253 		if (IS_BATCH_SID(batch, i)) {
254 			retcode = sid2pid_second_pass(
255 			    &state,
256 			    cache,
257 			    db,
258 			    &batch.idmap_mapping_batch_val[i],
259 			    &result->ids.ids_val[i]);
260 		} else if (IS_BATCH_UID(batch, i)) {
261 			retcode = pid2sid_second_pass(
262 			    &state,
263 			    cache,
264 			    db,
265 			    &batch.idmap_mapping_batch_val[i],
266 			    &result->ids.ids_val[i], 1);
267 		} else if (IS_BATCH_GID(batch, i)) {
268 			retcode = pid2sid_second_pass(
269 			    &state,
270 			    cache,
271 			    db,
272 			    &batch.idmap_mapping_batch_val[i],
273 			    &result->ids.ids_val[i], 0);
274 		} else {
275 			/* First stage has already set the error */
276 			continue;
277 		}
278 		if (IDMAP_FATAL_ERROR(retcode)) {
279 			result->retcode = retcode;
280 			goto out;
281 		}
282 	}
283 
284 	/* Check if we are done */
285 	if (state.sid2pid_done == TRUE && state.pid2sid_done == TRUE)
286 		goto out;
287 
288 	/* Reset our 'done' flags */
289 	state.sid2pid_done = state.pid2sid_done = TRUE;
290 
291 	/* Update cache in a single transaction */
292 	if (sql_exec_no_cb(cache, "BEGIN TRANSACTION;") != IDMAP_SUCCESS)
293 		goto out;
294 
295 	for (i = 0; i < batch.idmap_mapping_batch_len; i++) {
296 		state.curpos = i;
297 		if (IS_BATCH_SID(batch, i)) {
298 			(void) update_cache_sid2pid(
299 			    &state,
300 			    cache,
301 			    &batch.idmap_mapping_batch_val[i],
302 			    &result->ids.ids_val[i]);
303 		} else if ((IS_BATCH_UID(batch, i)) ||
304 		    (IS_BATCH_GID(batch, i))) {
305 			(void) update_cache_pid2sid(
306 			    &state,
307 			    cache,
308 			    &batch.idmap_mapping_batch_val[i],
309 			    &result->ids.ids_val[i]);
310 		}
311 	}
312 
313 	/* Commit if we have at least one successful update */
314 	if (state.sid2pid_done == FALSE || state.pid2sid_done == FALSE)
315 		(void) sql_exec_no_cb(cache, "COMMIT TRANSACTION;");
316 	else
317 		(void) sql_exec_no_cb(cache, "END TRANSACTION;");
318 
319 out:
320 	cleanup_lookup_state(&state);
321 	if (IDMAP_ERROR(result->retcode)) {
322 		xdr_free(xdr_idmap_ids_res, (caddr_t)result);
323 		result->ids.ids_len = 0;
324 		result->ids.ids_val = NULL;
325 	}
326 	result->retcode = idmap_stat4prot(result->retcode);
327 	return (TRUE);
328 }
329 
330 
331 /* ARGSUSED */
332 static
333 int
334 list_mappings_cb(void *parg, int argc, char **argv, char **colnames)
335 {
336 	list_cb_data_t		*cb_data;
337 	char			*str;
338 	idmap_mappings_res	*result;
339 	idmap_retcode		retcode;
340 	int			w2u, u2w;
341 	char			*end;
342 	static int		validated_column_names = 0;
343 
344 	if (!validated_column_names) {
345 		assert(strcmp(colnames[0], "rowid") == 0);
346 		assert(strcmp(colnames[1], "sidprefix") == 0);
347 		assert(strcmp(colnames[2], "rid") == 0);
348 		assert(strcmp(colnames[3], "pid") == 0);
349 		assert(strcmp(colnames[4], "w2u") == 0);
350 		assert(strcmp(colnames[5], "u2w") == 0);
351 		assert(strcmp(colnames[6], "windomain") == 0);
352 		assert(strcmp(colnames[7], "canon_winname") == 0);
353 		assert(strcmp(colnames[8], "unixname") == 0);
354 		assert(strcmp(colnames[9], "is_user") == 0);
355 		assert(strcmp(colnames[10], "is_wuser") == 0);
356 		validated_column_names = 1;
357 	}
358 
359 
360 	cb_data = (list_cb_data_t *)parg;
361 	result = (idmap_mappings_res *)cb_data->result;
362 
363 	_VALIDATE_LIST_CB_DATA(11, &result->mappings.mappings_val,
364 	    sizeof (idmap_mapping));
365 
366 	result->mappings.mappings_len++;
367 
368 	if ((str = strdup(argv[1])) == NULL)
369 		return (1);
370 	result->mappings.mappings_val[cb_data->next].id1.idmap_id_u.sid.prefix =
371 	    str;
372 	result->mappings.mappings_val[cb_data->next].id1.idmap_id_u.sid.rid =
373 	    strtoul(argv[2], &end, 10);
374 	result->mappings.mappings_val[cb_data->next].id1.idtype =
375 	    strtol(argv[10], &end, 10) ? IDMAP_USID : IDMAP_GSID;
376 
377 	result->mappings.mappings_val[cb_data->next].id2.idmap_id_u.uid =
378 	    strtoul(argv[3], &end, 10);
379 	result->mappings.mappings_val[cb_data->next].id2.idtype =
380 	    strtol(argv[9], &end, 10) ? IDMAP_UID : IDMAP_GID;
381 
382 	w2u = argv[4] ? strtol(argv[4], &end, 10) : 0;
383 	u2w = argv[5] ? strtol(argv[5], &end, 10) : 0;
384 
385 	if (w2u > 0 && u2w == 0)
386 		result->mappings.mappings_val[cb_data->next].direction =
387 		    IDMAP_DIRECTION_W2U;
388 	else if (w2u == 0 && u2w > 0)
389 		result->mappings.mappings_val[cb_data->next].direction =
390 		    IDMAP_DIRECTION_U2W;
391 	else
392 		result->mappings.mappings_val[cb_data->next].direction =
393 		    IDMAP_DIRECTION_BI;
394 
395 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1domain,
396 	    argv[6]);
397 
398 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1name,
399 	    argv[7]);
400 
401 	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id2name,
402 	    argv[8]);
403 
404 
405 	result->lastrowid = strtoll(argv[0], &end, 10);
406 	cb_data->next++;
407 	result->retcode = IDMAP_SUCCESS;
408 	return (0);
409 }
410 
411 
412 /* ARGSUSED */
413 bool_t
414 idmap_list_mappings_1_svc(int64_t lastrowid, uint64_t limit,
415     idmap_mappings_res *result, struct svc_req *rqstp)
416 {
417 	sqlite		*cache = NULL;
418 	char		lbuf[30], rbuf[30];
419 	uint64_t	maxlimit;
420 	idmap_retcode	retcode;
421 	char		*sql = NULL;
422 
423 	(void) memset(result, 0, sizeof (*result));
424 	lbuf[0] = rbuf[0] = 0;
425 
426 	RDLOCK_CONFIG();
427 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
428 	UNLOCK_CONFIG();
429 
430 	/* Get cache handle */
431 	result->retcode = get_cache_handle(&cache);
432 	if (result->retcode != IDMAP_SUCCESS)
433 		goto out;
434 
435 	result->retcode = IDMAP_ERR_INTERNAL;
436 
437 	/* Create LIMIT expression. */
438 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
439 		limit = maxlimit;
440 	if (limit > 0)
441 		(void) snprintf(lbuf, sizeof (lbuf),
442 		    "LIMIT %" PRIu64, limit + 1ULL);
443 
444 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
445 
446 	/*
447 	 * Combine all the above into a giant SELECT statement that
448 	 * will return the requested mappings
449 	 */
450 	sql = sqlite_mprintf("SELECT rowid, sidprefix, rid, pid, w2u, u2w, "
451 	    "windomain, canon_winname, unixname, is_user, is_wuser "
452 	    " FROM idmap_cache WHERE "
453 	    " %s %s;",
454 	    rbuf, lbuf);
455 	if (sql == NULL) {
456 		idmapdlog(LOG_ERR, "Out of memory");
457 		goto out;
458 	}
459 
460 	/* Execute the SQL statement and update the return buffer */
461 	PROCESS_LIST_SVC_SQL(retcode, cache, sql, limit, list_mappings_cb,
462 	    result, result->mappings.mappings_len);
463 
464 out:
465 	if (sql)
466 		sqlite_freemem(sql);
467 	if (IDMAP_ERROR(result->retcode))
468 		(void) xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
469 	result->retcode = idmap_stat4prot(result->retcode);
470 	return (TRUE);
471 }
472 
473 
474 /* ARGSUSED */
475 static
476 int
477 list_namerules_cb(void *parg, int argc, char **argv, char **colnames)
478 {
479 	list_cb_data_t		*cb_data;
480 	idmap_namerules_res	*result;
481 	idmap_retcode		retcode;
482 	int			w2u_order, u2w_order;
483 	char			*end;
484 	static int		validated_column_names = 0;
485 
486 	if (!validated_column_names) {
487 		assert(strcmp(colnames[0], "rowid") == 0);
488 		assert(strcmp(colnames[1], "is_user") == 0);
489 		assert(strcmp(colnames[2], "is_wuser") == 0);
490 		assert(strcmp(colnames[3], "windomain") == 0);
491 		assert(strcmp(colnames[4], "winname_display") == 0);
492 		assert(strcmp(colnames[5], "is_nt4") == 0);
493 		assert(strcmp(colnames[6], "unixname") == 0);
494 		assert(strcmp(colnames[7], "w2u_order") == 0);
495 		assert(strcmp(colnames[8], "u2w_order") == 0);
496 		validated_column_names = 1;
497 	}
498 
499 	cb_data = (list_cb_data_t *)parg;
500 	result = (idmap_namerules_res *)cb_data->result;
501 
502 	_VALIDATE_LIST_CB_DATA(9, &result->rules.rules_val,
503 	    sizeof (idmap_namerule));
504 
505 	result->rules.rules_len++;
506 
507 	result->rules.rules_val[cb_data->next].is_user =
508 	    strtol(argv[1], &end, 10);
509 
510 	result->rules.rules_val[cb_data->next].is_wuser =
511 	    strtol(argv[2], &end, 10);
512 
513 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].windomain,
514 	    argv[3]);
515 
516 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].winname,
517 	    argv[4]);
518 
519 	result->rules.rules_val[cb_data->next].is_nt4 =
520 	    strtol(argv[5], &end, 10);
521 
522 	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].unixname,
523 	    argv[6]);
524 
525 	w2u_order = argv[7] ? strtol(argv[7], &end, 10) : 0;
526 	u2w_order = argv[8] ? strtol(argv[8], &end, 10) : 0;
527 
528 	if (w2u_order > 0 && u2w_order == 0)
529 		result->rules.rules_val[cb_data->next].direction =
530 		    IDMAP_DIRECTION_W2U;
531 	else if (w2u_order == 0 && u2w_order > 0)
532 		result->rules.rules_val[cb_data->next].direction =
533 		    IDMAP_DIRECTION_U2W;
534 	else
535 		result->rules.rules_val[cb_data->next].direction =
536 		    IDMAP_DIRECTION_BI;
537 
538 	result->lastrowid = strtoll(argv[0], &end, 10);
539 	cb_data->next++;
540 	result->retcode = IDMAP_SUCCESS;
541 	return (0);
542 }
543 
544 
545 /* ARGSUSED */
546 bool_t
547 idmap_list_namerules_1_svc(idmap_namerule rule, uint64_t lastrowid,
548 		uint64_t limit, idmap_namerules_res *result,
549 		struct svc_req *rqstp)
550 {
551 
552 	sqlite		*db = NULL;
553 	char		w2ubuf[15], u2wbuf[15];
554 	char		lbuf[30], rbuf[30];
555 	char		*sql = NULL;
556 	char		*expr = NULL;
557 	uint64_t	maxlimit;
558 	idmap_retcode	retcode;
559 
560 	(void) memset(result, 0, sizeof (*result));
561 	lbuf[0] = rbuf[0] = 0;
562 
563 	RDLOCK_CONFIG();
564 	maxlimit = _idmapdstate.cfg->pgcfg.list_size_limit;
565 	UNLOCK_CONFIG();
566 
567 	/* Get db handle */
568 	result->retcode = get_db_handle(&db);
569 	if (result->retcode != IDMAP_SUCCESS)
570 		goto out;
571 
572 	result->retcode = IDMAP_ERR_INTERNAL;
573 
574 	w2ubuf[0] = u2wbuf[0] = 0;
575 	if (rule.direction == IDMAP_DIRECTION_BI) {
576 		(void) snprintf(w2ubuf, sizeof (w2ubuf), "AND w2u_order > 0");
577 		(void) snprintf(u2wbuf, sizeof (u2wbuf), "AND u2w_order > 0");
578 	} else if (rule.direction == IDMAP_DIRECTION_W2U) {
579 		(void) snprintf(w2ubuf, sizeof (w2ubuf), "AND w2u_order > 0");
580 		(void) snprintf(u2wbuf, sizeof (u2wbuf),
581 		    "AND (u2w_order = 0 OR u2w_order ISNULL)");
582 	} else if (rule.direction == IDMAP_DIRECTION_U2W) {
583 		(void) snprintf(w2ubuf, sizeof (w2ubuf),
584 		    "AND (w2u_order = 0 OR w2u_order ISNULL)");
585 		(void) snprintf(u2wbuf, sizeof (u2wbuf), "AND u2w_order > 0");
586 	}
587 
588 	retcode = gen_sql_expr_from_rule(&rule, &expr);
589 	if (retcode != IDMAP_SUCCESS)
590 		goto out;
591 
592 	/* Create LIMIT expression. */
593 	if (limit == 0 || (maxlimit > 0 && maxlimit < limit))
594 		limit = maxlimit;
595 	if (limit > 0)
596 		(void) snprintf(lbuf, sizeof (lbuf),
597 		    "LIMIT %" PRIu64, limit + 1ULL);
598 
599 	(void) snprintf(rbuf, sizeof (rbuf), "rowid > %" PRIu64, lastrowid);
600 
601 	/*
602 	 * Combine all the above into a giant SELECT statement that
603 	 * will return the requested rules
604 	 */
605 	sql = sqlite_mprintf("SELECT rowid, is_user, is_wuser, windomain, "
606 	    "winname_display, is_nt4, unixname, w2u_order, u2w_order "
607 	    "FROM namerules WHERE "
608 	    " %s %s %s %s %s;",
609 	    rbuf, expr, w2ubuf, u2wbuf, lbuf);
610 
611 	if (sql == NULL) {
612 		idmapdlog(LOG_ERR, "Out of memory");
613 		goto out;
614 	}
615 
616 	/* Execute the SQL statement and update the return buffer */
617 	PROCESS_LIST_SVC_SQL(retcode, db, sql, limit, list_namerules_cb,
618 	    result, result->rules.rules_len);
619 
620 out:
621 	if (expr)
622 		sqlite_freemem(expr);
623 	if (sql)
624 		sqlite_freemem(sql);
625 	if (IDMAP_ERROR(result->retcode))
626 		(void) xdr_free(xdr_idmap_namerules_res, (caddr_t)result);
627 	result->retcode = idmap_stat4prot(result->retcode);
628 	return (TRUE);
629 }
630 
631 #define	IDMAP_RULES_AUTH	"solaris.admin.idmap.rules"
632 static int
633 verify_rules_auth(struct svc_req *rqstp)
634 {
635 	ucred_t		*uc = NULL;
636 	uid_t		uid;
637 	char		buf[1024];
638 	struct passwd	pwd;
639 	const char	*me = "verify_rules_auth";
640 
641 	if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
642 		idmapdlog(LOG_ERR,
643 		    "%s: svc_getcallerucred failed (errno=%d)",
644 		    me, errno);
645 		return (-1);
646 	}
647 
648 	uid = ucred_geteuid(uc);
649 	if (uid == (uid_t)-1) {
650 		idmapdlog(LOG_ERR,
651 		    "%s: ucred_geteuid failed (errno=%d)",
652 		    me, errno);
653 		ucred_free(uc);
654 		return (-1);
655 	}
656 
657 	if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) {
658 		idmapdlog(LOG_ERR,
659 		    "%s: getpwuid_r(%u) failed (errno=%d)",
660 		    me, uid, errno);
661 		ucred_free(uc);
662 		return (-1);
663 	}
664 
665 	if (chkauthattr(IDMAP_RULES_AUTH, pwd.pw_name) != 1) {
666 		idmapdlog(LOG_INFO,
667 		    "%s: %s does not have authorization.",
668 		    me, pwd.pw_name);
669 		ucred_free(uc);
670 		return (-1);
671 	}
672 
673 	ucred_free(uc);
674 	return (1);
675 }
676 
677 /*
678  * Meaning of the return values is the following: For retcode ==
679  * IDMAP_SUCCESS, everything went OK and error_index is
680  * undefined. Otherwise, error_index >=0 shows the failed batch
681  * element. errro_index == -1 indicates failure at the beginning,
682  * error_index == -2 at the end.
683  */
684 
685 /* ARGSUSED */
686 bool_t
687 idmap_update_1_svc(idmap_update_batch batch, idmap_update_res *res,
688 		struct svc_req *rqstp)
689 {
690 	sqlite		*db = NULL;
691 	idmap_update_op	*up;
692 	int		i;
693 	int		trans = FALSE;
694 
695 	res->error_index = -1;
696 	(void) memset(&res->error_rule, 0, sizeof (res->error_rule));
697 	(void) memset(&res->conflict_rule, 0, sizeof (res->conflict_rule));
698 
699 	if (verify_rules_auth(rqstp) < 0) {
700 		res->retcode = IDMAP_ERR_PERMISSION_DENIED;
701 		goto out;
702 	}
703 
704 	if (batch.idmap_update_batch_len == 0 ||
705 	    batch.idmap_update_batch_val == NULL) {
706 		res->retcode = IDMAP_SUCCESS;
707 		goto out;
708 	}
709 
710 	/* Get db handle */
711 	res->retcode = get_db_handle(&db);
712 	if (res->retcode != IDMAP_SUCCESS)
713 		goto out;
714 
715 	res->retcode = sql_exec_no_cb(db, "BEGIN TRANSACTION;");
716 	if (res->retcode != IDMAP_SUCCESS)
717 		goto out;
718 	trans = TRUE;
719 
720 	for (i = 0; i < batch.idmap_update_batch_len; i++) {
721 		up = &batch.idmap_update_batch_val[i];
722 		switch (up->opnum) {
723 		case OP_NONE:
724 			res->retcode = IDMAP_SUCCESS;
725 			break;
726 		case OP_ADD_NAMERULE:
727 			res->retcode = add_namerule(db,
728 			    &up->idmap_update_op_u.rule);
729 			break;
730 		case OP_RM_NAMERULE:
731 			res->retcode = rm_namerule(db,
732 			    &up->idmap_update_op_u.rule);
733 			break;
734 		case OP_FLUSH_NAMERULES:
735 			res->retcode = flush_namerules(db);
736 			break;
737 		default:
738 			res->retcode = IDMAP_ERR_NOTSUPPORTED;
739 			break;
740 		};
741 
742 		if (res->retcode != IDMAP_SUCCESS) {
743 			res->error_index = i;
744 			if (up->opnum == OP_ADD_NAMERULE ||
745 			    up->opnum == OP_RM_NAMERULE) {
746 				idmap_stat r2 =
747 				    idmap_namerule_cpy(&res->error_rule,
748 				    &up->idmap_update_op_u.rule);
749 				if (r2 != IDMAP_SUCCESS)
750 					res->retcode = r2;
751 			}
752 			goto out;
753 		}
754 	}
755 
756 out:
757 	if (trans) {
758 		if (res->retcode == IDMAP_SUCCESS) {
759 			res->retcode =
760 			    sql_exec_no_cb(db, "COMMIT TRANSACTION;");
761 			if (res->retcode !=  IDMAP_SUCCESS)
762 				res->error_index = -2;
763 		}
764 		else
765 			(void) sql_exec_no_cb(db, "ROLLBACK TRANSACTION;");
766 	}
767 
768 	res->retcode = idmap_stat4prot(res->retcode);
769 
770 	return (TRUE);
771 }
772 
773 
774 /* ARGSUSED */
775 bool_t
776 idmap_get_mapped_id_by_name_1_svc(idmap_mapping request,
777 		idmap_mappings_res *result, struct svc_req *rqstp)
778 {
779 	sqlite		*cache = NULL, *db = NULL;
780 
781 	/* Init */
782 	(void) memset(result, 0, sizeof (*result));
783 
784 	/* Get cache handle */
785 	result->retcode = get_cache_handle(&cache);
786 	if (result->retcode != IDMAP_SUCCESS)
787 		goto out;
788 
789 	/* Get db handle */
790 	result->retcode = get_db_handle(&db);
791 	if (result->retcode != IDMAP_SUCCESS)
792 		goto out;
793 
794 	/* Allocate result */
795 	result->mappings.mappings_val = calloc(1, sizeof (idmap_mapping));
796 	if (result->mappings.mappings_val == NULL) {
797 		idmapdlog(LOG_ERR, "Out of memory");
798 		result->retcode = IDMAP_ERR_MEMORY;
799 		goto out;
800 	}
801 	result->mappings.mappings_len = 1;
802 
803 
804 	if (IS_REQUEST_SID(request, 1)) {
805 		result->retcode = get_w2u_mapping(
806 		    cache,
807 		    db,
808 		    &request,
809 		    result->mappings.mappings_val);
810 	} else if (IS_REQUEST_UID(request)) {
811 		result->retcode = get_u2w_mapping(
812 		    cache,
813 		    db,
814 		    &request,
815 		    result->mappings.mappings_val,
816 		    1);
817 	} else if (IS_REQUEST_GID(request)) {
818 		result->retcode = get_u2w_mapping(
819 		    cache,
820 		    db,
821 		    &request,
822 		    result->mappings.mappings_val,
823 		    0);
824 	} else {
825 		result->retcode = IDMAP_ERR_IDTYPE;
826 	}
827 
828 out:
829 	if (IDMAP_FATAL_ERROR(result->retcode)) {
830 		xdr_free(xdr_idmap_mappings_res, (caddr_t)result);
831 		result->mappings.mappings_len = 0;
832 		result->mappings.mappings_val = NULL;
833 	}
834 	result->retcode = idmap_stat4prot(result->retcode);
835 	return (TRUE);
836 }
837 
838 
839 /* ARGSUSED */
840 int
841 idmap_prog_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,
842 		caddr_t result)
843 {
844 	(void) xdr_free(xdr_result, result);
845 	return (TRUE);
846 }
847