xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c (revision c65ebfc7045424bd04a6c7719a27b0ad3399ad54)
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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 
26 /*
27  * Door server routines for nfsmapid daemon
28  * Translate NFSv4 users and groups between numeric and string values
29  */
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <alloca.h>
33 #include <signal.h>
34 #include <libintl.h>
35 #include <limits.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <string.h>
39 #include <memory.h>
40 #include <pwd.h>
41 #include <grp.h>
42 #include <door.h>
43 #include <syslog.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <assert.h>
47 #include <deflt.h>
48 #include <nfs/nfs4.h>
49 #include <nfs/nfssys.h>
50 #include <nfs/nfsid_map.h>
51 #include <nfs/mapid.h>
52 #include <sys/sdt.h>
53 #include <sys/idmap.h>
54 #include <idmap.h>
55 #include <sys/fs/autofs.h>
56 #include <sys/mkdev.h>
57 #include "nfs_resolve.h"
58 
59 #define		UID_MAX_STR_LEN		11	/* Digits in UID_MAX + 1 */
60 #define		DIAG_FILE		"/var/run/nfs4_domain"
61 
62 /*
63  * idmap_kcall() takes a door descriptor as it's argument when we
64  * need to (re)establish the in-kernel door handles. When we only
65  * want to flush the id kernel caches, we don't redo the door setup.
66  */
67 #define		FLUSH_KCACHES_ONLY	(int)-1
68 
69 FILE		*n4_fp;
70 int		 n4_fd;
71 
72 extern size_t	pwd_buflen;
73 extern size_t	grp_buflen;
74 extern thread_t	sig_thread;
75 
76 /*
77  * Prototypes
78  */
79 extern void	 check_domain(int);
80 extern void	 idmap_kcall(int);
81 extern int	 _nfssys(int, void *);
82 extern int	 valid_domain(const char *);
83 extern int	 validate_id_str(const char *);
84 extern int	 extract_domain(char *, char **, char **);
85 extern void	 update_diag_file(char *);
86 extern void	*cb_update_domain(void *);
87 extern int	 cur_domain_null(void);
88 
89 void
90 nfsmapid_str_uid(struct mapid_arg *argp, size_t arg_size)
91 {
92 	struct mapid_res result;
93 	struct passwd	 pwd;
94 	struct passwd	*pwd_ptr;
95 	int		 pwd_rc;
96 	char		*pwd_buf;
97 	char		*user;
98 	char		*domain;
99 	idmap_stat	 rc;
100 
101 	if (argp->u_arg.len <= 0 || arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
102 		result.status = NFSMAPID_INVALID;
103 		result.u_res.uid = UID_NOBODY;
104 		goto done;
105 	}
106 
107 	if (!extract_domain(argp->str, &user, &domain)) {
108 		unsigned long id;
109 
110 		/*
111 		 * Invalid "user@domain" string. Still, the user
112 		 * part might be an encoded uid, so do a final check.
113 		 * Remember, domain part of string was not set since
114 		 * not a valid string.
115 		 */
116 		if (!validate_id_str(user)) {
117 			result.status = NFSMAPID_UNMAPPABLE;
118 			result.u_res.uid = UID_NOBODY;
119 			goto done;
120 		}
121 
122 		errno = 0;
123 		id = strtoul(user, (char **)NULL, 10);
124 
125 		/*
126 		 * We don't accept ephemeral ids from the wire.
127 		 */
128 		if (errno || id > UID_MAX) {
129 			result.status = NFSMAPID_UNMAPPABLE;
130 			result.u_res.uid = UID_NOBODY;
131 			goto done;
132 		}
133 
134 		result.u_res.uid = (uid_t)id;
135 		result.status = NFSMAPID_NUMSTR;
136 		goto done;
137 	}
138 
139 	/*
140 	 * String properly constructed. Now we check for domain and
141 	 * group validity.
142 	 */
143 	if (!cur_domain_null() && !valid_domain(domain)) {
144 		/*
145 		 * If the domain part of the string does not
146 		 * match the NFS domain, try to map it using
147 		 * idmap service.
148 		 */
149 		rc = idmap_getuidbywinname(user, domain, 0, &result.u_res.uid);
150 		if (rc != IDMAP_SUCCESS) {
151 			result.status = NFSMAPID_BADDOMAIN;
152 			result.u_res.uid = UID_NOBODY;
153 			goto done;
154 		}
155 		result.status = NFSMAPID_OK;
156 		goto done;
157 	}
158 
159 	if ((pwd_buf = malloc(pwd_buflen)) == NULL ||
160 	    (pwd_rc = getpwnam_r(user, &pwd, pwd_buf, pwd_buflen, &pwd_ptr))
161 	    != 0 || pwd_ptr == NULL) {
162 
163 		if (pwd_buf == NULL || pwd_rc != 0)
164 			result.status = NFSMAPID_INTERNAL;
165 		else {
166 			/*
167 			 * Not a valid user
168 			 */
169 			result.status = NFSMAPID_NOTFOUND;
170 			free(pwd_buf);
171 		}
172 		result.u_res.uid = UID_NOBODY;
173 		goto done;
174 	}
175 
176 	/*
177 	 * Valid user entry
178 	 */
179 	result.u_res.uid = pwd.pw_uid;
180 	result.status = NFSMAPID_OK;
181 	free(pwd_buf);
182 done:
183 	(void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
184 }
185 
186 /* ARGSUSED1 */
187 void
188 nfsmapid_uid_str(struct mapid_arg *argp, size_t arg_size)
189 {
190 	struct mapid_res	 result;
191 	struct mapid_res	*resp;
192 	struct passwd		 pwd;
193 	struct passwd		 *pwd_ptr;
194 	char			*pwd_buf = NULL;
195 	char			*idmap_buf = NULL;
196 	uid_t			 uid = argp->u_arg.uid;
197 	size_t			 uid_str_len;
198 	char			*pw_str;
199 	size_t			 pw_str_len;
200 	char			*at_str;
201 	size_t			 at_str_len;
202 	char			 dom_str[DNAMEMAX];
203 	size_t			 dom_str_len;
204 	idmap_stat		 rc;
205 
206 	if (uid == (uid_t)-1) {
207 		/*
208 		 * Sentinel uid is not a valid id
209 		 */
210 		resp = &result;
211 		resp->status = NFSMAPID_BADID;
212 		resp->u_res.len = 0;
213 		goto done;
214 	}
215 
216 	/*
217 	 * Make local copy of domain for further manipuation
218 	 * NOTE: mapid_get_domain() returns a ptr to TSD.
219 	 */
220 	if (cur_domain_null()) {
221 		dom_str_len = 0;
222 		dom_str[0] = '\0';
223 	} else {
224 		dom_str_len = strlcpy(dom_str, mapid_get_domain(), DNAMEMAX);
225 	}
226 
227 	/*
228 	 * If uid is ephemeral then resolve it using idmap service
229 	 */
230 	if (uid > UID_MAX) {
231 		rc = idmap_getwinnamebyuid(uid, 0, &idmap_buf, NULL);
232 		if (rc != IDMAP_SUCCESS) {
233 			/*
234 			 * We don't put stringified ephemeral uids on
235 			 * the wire.
236 			 */
237 			resp = &result;
238 			resp->status = NFSMAPID_UNMAPPABLE;
239 			resp->u_res.len = 0;
240 			goto done;
241 		}
242 
243 		/*
244 		 * idmap_buf is already in the desired form i.e. name@domain
245 		 */
246 		pw_str = idmap_buf;
247 		pw_str_len = strlen(pw_str);
248 		at_str_len = dom_str_len = 0;
249 		at_str = "";
250 		dom_str[0] = '\0';
251 		goto gen_result;
252 	}
253 
254 	/*
255 	 * Handling non-ephemeral uids
256 	 *
257 	 * We want to encode the uid into a literal string... :
258 	 *
259 	 *	- upon failure to allocate space from the heap
260 	 *	- if there is no current domain configured
261 	 *	- if there is no such uid in the passwd DB's
262 	 */
263 	if ((pwd_buf = malloc(pwd_buflen)) == NULL || dom_str_len == 0 ||
264 	    getpwuid_r(uid, &pwd, pwd_buf, pwd_buflen, &pwd_ptr) != 0 ||
265 	    pwd_ptr == NULL) {
266 
267 		/*
268 		 * If we could not allocate from the heap, try
269 		 * allocating from the stack as a last resort.
270 		 */
271 		if (pwd_buf == NULL && (pwd_buf =
272 		    alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
273 			resp = &result;
274 			resp->status = NFSMAPID_INTERNAL;
275 			resp->u_res.len = 0;
276 			goto done;
277 		}
278 
279 		/*
280 		 * Constructing literal string without '@' so that
281 		 * we'll know that it's not a user, but rather a
282 		 * uid encoded string.
283 		 */
284 		pw_str = pwd_buf;
285 		(void) sprintf(pw_str, "%u", uid);
286 		pw_str_len = strlen(pw_str);
287 		at_str_len = dom_str_len = 0;
288 		at_str = "";
289 		dom_str[0] = '\0';
290 	} else {
291 		/*
292 		 * Otherwise, we construct the "user@domain" string if
293 		 * it's not already in that form.
294 		 */
295 		pw_str = pwd.pw_name;
296 		pw_str_len = strlen(pw_str);
297 		if (strchr(pw_str, '@') == NULL) {
298 			at_str = "@";
299 			at_str_len = 1;
300 		} else {
301 			at_str_len = dom_str_len = 0;
302 			at_str = "";
303 			dom_str[0] = '\0';
304 		}
305 	}
306 
307 gen_result:
308 	uid_str_len = pw_str_len + at_str_len + dom_str_len;
309 	if ((resp = alloca(MAPID_RES_LEN(uid_str_len))) == NULL) {
310 		resp = &result;
311 		resp->status = NFSMAPID_INTERNAL;
312 		resp->u_res.len = 0;
313 		goto done;
314 	}
315 	/* LINTED format argument to sprintf */
316 	(void) sprintf(resp->str, "%s%s%s", pw_str, at_str, dom_str);
317 	resp->u_res.len = uid_str_len;
318 	if (pwd_buf)
319 		free(pwd_buf);
320 	if (idmap_buf)
321 		idmap_free(idmap_buf);
322 	resp->status = NFSMAPID_OK;
323 
324 done:
325 	/*
326 	 * There is a chance that the door_return will fail because the
327 	 * resulting string is too large, try to indicate that if possible
328 	 */
329 	if (door_return((char *)resp,
330 	    MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
331 		resp->status = NFSMAPID_INTERNAL;
332 		resp->u_res.len = 0;
333 		(void) door_return((char *)&result, sizeof (struct mapid_res),
334 		    NULL, 0);
335 	}
336 }
337 
338 void
339 nfsmapid_str_gid(struct mapid_arg *argp, size_t arg_size)
340 {
341 	struct mapid_res	result;
342 	struct group		grp;
343 	struct group		*grp_ptr;
344 	int			grp_rc;
345 	char			*grp_buf;
346 	char			*group;
347 	char			*domain;
348 	idmap_stat		rc;
349 
350 	if (argp->u_arg.len <= 0 ||
351 	    arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
352 		result.status = NFSMAPID_INVALID;
353 		result.u_res.gid = GID_NOBODY;
354 		goto done;
355 	}
356 
357 	if (!extract_domain(argp->str, &group, &domain)) {
358 		unsigned long id;
359 
360 		/*
361 		 * Invalid "group@domain" string. Still, the
362 		 * group part might be an encoded gid, so do a
363 		 * final check. Remember, domain part of string
364 		 * was not set since not a valid string.
365 		 */
366 		if (!validate_id_str(group)) {
367 			result.status = NFSMAPID_UNMAPPABLE;
368 			result.u_res.gid = GID_NOBODY;
369 			goto done;
370 		}
371 
372 		errno = 0;
373 		id = strtoul(group, (char **)NULL, 10);
374 
375 		/*
376 		 * We don't accept ephemeral ids from the wire.
377 		 */
378 		if (errno || id > UID_MAX) {
379 			result.status = NFSMAPID_UNMAPPABLE;
380 			result.u_res.gid = GID_NOBODY;
381 			goto done;
382 		}
383 
384 		result.u_res.gid = (gid_t)id;
385 		result.status = NFSMAPID_NUMSTR;
386 		goto done;
387 	}
388 
389 	/*
390 	 * String properly constructed. Now we check for domain and
391 	 * group validity.
392 	 */
393 	if (!cur_domain_null() && !valid_domain(domain)) {
394 		/*
395 		 * If the domain part of the string does not
396 		 * match the NFS domain, try to map it using
397 		 * idmap service.
398 		 */
399 		rc = idmap_getgidbywinname(group, domain, 0, &result.u_res.gid);
400 		if (rc != IDMAP_SUCCESS) {
401 			result.status = NFSMAPID_BADDOMAIN;
402 			result.u_res.gid = GID_NOBODY;
403 			goto done;
404 		}
405 		result.status = NFSMAPID_OK;
406 		goto done;
407 	}
408 
409 	if ((grp_buf = malloc(grp_buflen)) == NULL ||
410 	    (grp_rc = getgrnam_r(group, &grp, grp_buf, grp_buflen, &grp_ptr))
411 	    != 0 || grp_ptr == NULL) {
412 
413 		if (grp_buf == NULL || grp_rc != 0)
414 			result.status = NFSMAPID_INTERNAL;
415 		else {
416 			/*
417 			 * Not a valid group
418 			 */
419 			result.status = NFSMAPID_NOTFOUND;
420 			free(grp_buf);
421 		}
422 		result.u_res.gid = GID_NOBODY;
423 		goto done;
424 	}
425 
426 	/*
427 	 * Valid group entry
428 	 */
429 	result.status = NFSMAPID_OK;
430 	result.u_res.gid = grp.gr_gid;
431 	free(grp_buf);
432 done:
433 	(void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
434 }
435 
436 /* ARGSUSED1 */
437 void
438 nfsmapid_gid_str(struct mapid_arg *argp, size_t arg_size)
439 {
440 	struct mapid_res	 result;
441 	struct mapid_res	*resp;
442 	struct group		 grp;
443 	struct group		*grp_ptr;
444 	char			*grp_buf = NULL;
445 	char			*idmap_buf = NULL;
446 	idmap_stat		 rc;
447 	gid_t			 gid = argp->u_arg.gid;
448 	size_t			 gid_str_len;
449 	char			*gr_str;
450 	size_t			 gr_str_len;
451 	char			*at_str;
452 	size_t			 at_str_len;
453 	char			 dom_str[DNAMEMAX];
454 	size_t			 dom_str_len;
455 
456 	if (gid == (gid_t)-1) {
457 		/*
458 		 * Sentinel gid is not a valid id
459 		 */
460 		resp = &result;
461 		resp->status = NFSMAPID_BADID;
462 		resp->u_res.len = 0;
463 		goto done;
464 	}
465 
466 	/*
467 	 * Make local copy of domain for further manipuation
468 	 * NOTE: mapid_get_domain() returns a ptr to TSD.
469 	 */
470 	if (cur_domain_null()) {
471 		dom_str_len = 0;
472 		dom_str[0] = '\0';
473 	} else {
474 		dom_str_len = strlen(mapid_get_domain());
475 		bcopy(mapid_get_domain(), dom_str, dom_str_len);
476 		dom_str[dom_str_len] = '\0';
477 	}
478 
479 	/*
480 	 * If gid is ephemeral then resolve it using idmap service
481 	 */
482 	if (gid > UID_MAX) {
483 		rc = idmap_getwinnamebygid(gid, 0, &idmap_buf, NULL);
484 		if (rc != IDMAP_SUCCESS) {
485 			/*
486 			 * We don't put stringified ephemeral gids on
487 			 * the wire.
488 			 */
489 			resp = &result;
490 			resp->status = NFSMAPID_UNMAPPABLE;
491 			resp->u_res.len = 0;
492 			goto done;
493 		}
494 
495 		/*
496 		 * idmap_buf is already in the desired form i.e. name@domain
497 		 */
498 		gr_str = idmap_buf;
499 		gr_str_len = strlen(gr_str);
500 		at_str_len = dom_str_len = 0;
501 		at_str = "";
502 		dom_str[0] = '\0';
503 		goto gen_result;
504 	}
505 
506 	/*
507 	 * Handling non-ephemeral gids
508 	 *
509 	 * We want to encode the gid into a literal string... :
510 	 *
511 	 *	- upon failure to allocate space from the heap
512 	 *	- if there is no current domain configured
513 	 *	- if there is no such gid in the group DB's
514 	 */
515 	if ((grp_buf = malloc(grp_buflen)) == NULL || dom_str_len == 0 ||
516 	    getgrgid_r(gid, &grp, grp_buf, grp_buflen, &grp_ptr) != 0 ||
517 	    grp_ptr == NULL) {
518 
519 		/*
520 		 * If we could not allocate from the heap, try
521 		 * allocating from the stack as a last resort.
522 		 */
523 		if (grp_buf == NULL && (grp_buf =
524 		    alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
525 			resp = &result;
526 			resp->status = NFSMAPID_INTERNAL;
527 			resp->u_res.len = 0;
528 			goto done;
529 		}
530 
531 		/*
532 		 * Constructing literal string without '@' so that
533 		 * we'll know that it's not a group, but rather a
534 		 * gid encoded string.
535 		 */
536 		gr_str = grp_buf;
537 		(void) sprintf(gr_str, "%u", gid);
538 		gr_str_len = strlen(gr_str);
539 		at_str_len = dom_str_len = 0;
540 		at_str = "";
541 		dom_str[0] = '\0';
542 	} else {
543 		/*
544 		 * Otherwise, we construct the "group@domain" string if
545 		 * it's not already in that form.
546 		 */
547 		gr_str = grp.gr_name;
548 		gr_str_len = strlen(gr_str);
549 		if (strchr(gr_str, '@') == NULL) {
550 			at_str = "@";
551 			at_str_len = 1;
552 		} else {
553 			at_str_len = dom_str_len = 0;
554 			at_str = "";
555 			dom_str[0] = '\0';
556 		}
557 	}
558 
559 gen_result:
560 	gid_str_len = gr_str_len + at_str_len + dom_str_len;
561 	if ((resp = alloca(MAPID_RES_LEN(gid_str_len))) == NULL) {
562 		resp = &result;
563 		resp->status = NFSMAPID_INTERNAL;
564 		resp->u_res.len = 0;
565 		goto done;
566 	}
567 	/* LINTED format argument to sprintf */
568 	(void) sprintf(resp->str, "%s%s%s", gr_str, at_str, dom_str);
569 	resp->u_res.len = gid_str_len;
570 	if (grp_buf)
571 		free(grp_buf);
572 	if (idmap_buf)
573 		idmap_free(idmap_buf);
574 	resp->status = NFSMAPID_OK;
575 
576 done:
577 	/*
578 	 * There is a chance that the door_return will fail because the
579 	 * resulting string is too large, try to indicate that if possible
580 	 */
581 	if (door_return((char *)resp,
582 	    MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
583 		resp->status = NFSMAPID_INTERNAL;
584 		resp->u_res.len = 0;
585 		(void) door_return((char *)&result, sizeof (struct mapid_res),
586 		    NULL, 0);
587 	}
588 }
589 
590 void
591 nfsmapid_server_netinfo(refd_door_args_t *referral_args, size_t arg_size)
592 {
593 	char *res;
594 	int res_size;
595 	int error;
596 	int srsz = 0;
597 	char host[MAXHOSTNAMELEN];
598 	utf8string	*nfsfsloc_args;
599 	refd_door_res_t	*door_res;
600 	refd_door_res_t	failed_res;
601 	struct nfs_fsl_info *nfs_fsloc_res;
602 
603 	if (arg_size < sizeof (refd_door_args_t)) {
604 		failed_res.res_status = EINVAL;
605 		res = (char *)&failed_res;
606 		res_size = sizeof (refd_door_res_t);
607 		syslog(LOG_ERR,
608 		    "nfsmapid_server_netinfo failed: Invalid data\n");
609 		goto send_response;
610 	}
611 
612 	if (decode_args(xdr_utf8string, (refd_door_args_t *)referral_args,
613 	    (caddr_t *)&nfsfsloc_args, sizeof (utf8string))) {
614 		syslog(LOG_ERR, "cannot allocate memory");
615 		failed_res.res_status = ENOMEM;
616 		failed_res.xdr_len = 0;
617 		res = (caddr_t)&failed_res;
618 		res_size = sizeof (refd_door_res_t);
619 		goto send_response;
620 	}
621 
622 	if (nfsfsloc_args->utf8string_len >= MAXHOSTNAMELEN) {
623 		syslog(LOG_ERR, "argument too large");
624 		failed_res.res_status = EOVERFLOW;
625 		failed_res.xdr_len = 0;
626 		res = (caddr_t)&failed_res;
627 		res_size = sizeof (refd_door_res_t);
628 		goto send_response;
629 	}
630 
631 	snprintf(host, nfsfsloc_args->utf8string_len + 1,
632 	    "%s", nfsfsloc_args->utf8string_val);
633 
634 	nfs_fsloc_res =
635 	    get_nfs4ref_info(host, NFS_PORT, NFS_V4);
636 
637 	xdr_free(xdr_utf8string, (char *)&nfsfsloc_args);
638 
639 	if (nfs_fsloc_res) {
640 		error = 0;
641 		error = encode_res(xdr_nfs_fsl_info, &door_res,
642 		    (caddr_t)nfs_fsloc_res, &res_size);
643 		free_nfs4ref_info(nfs_fsloc_res);
644 		if (error != 0) {
645 			syslog(LOG_ERR,
646 			    "error allocating fs_locations "
647 			    "results buffer");
648 			failed_res.res_status = error;
649 			failed_res.xdr_len = srsz;
650 			res = (caddr_t)&failed_res;
651 			res_size = sizeof (refd_door_res_t);
652 		} else {
653 			door_res->res_status = 0;
654 			res = (caddr_t)door_res;
655 		}
656 	} else {
657 		failed_res.res_status = EINVAL;
658 		failed_res.xdr_len = 0;
659 		res = (caddr_t)&failed_res;
660 		res_size = sizeof (refd_door_res_t);
661 	}
662 
663 send_response:
664 	srsz = res_size;
665 	errno = 0;
666 
667 	error = door_return(res, res_size, NULL, 0);
668 	if (errno == E2BIG) {
669 		failed_res.res_status = EOVERFLOW;
670 		failed_res.xdr_len = srsz;
671 		res = (caddr_t)&failed_res;
672 		res_size = sizeof (refd_door_res_t);
673 	} else {
674 		res = NULL;
675 		res_size = 0;
676 	}
677 
678 	door_return(res, res_size, NULL, 0);
679 }
680 
681 /* ARGSUSED */
682 void
683 nfsmapid_func(void *cookie, char *argp, size_t arg_size,
684 						door_desc_t *dp, uint_t n_desc)
685 {
686 	struct mapid_arg	*mapargp;
687 	struct mapid_res	mapres;
688 	refd_door_args_t	*referral_args;
689 
690 	/*
691 	 * Make sure we have a valid argument
692 	 */
693 	if (arg_size < sizeof (struct mapid_arg)) {
694 		mapres.status = NFSMAPID_INVALID;
695 		mapres.u_res.len = 0;
696 		(void) door_return((char *)&mapres, sizeof (struct mapid_res),
697 		    NULL, 0);
698 		return;
699 	}
700 
701 	/* LINTED pointer cast */
702 	mapargp = (struct mapid_arg *)argp;
703 	referral_args = (refd_door_args_t *)argp;
704 	switch (mapargp->cmd) {
705 	case NFSMAPID_STR_UID:
706 		nfsmapid_str_uid(mapargp, arg_size);
707 		return;
708 	case NFSMAPID_UID_STR:
709 		nfsmapid_uid_str(mapargp, arg_size);
710 		return;
711 	case NFSMAPID_STR_GID:
712 		nfsmapid_str_gid(mapargp, arg_size);
713 		return;
714 	case NFSMAPID_GID_STR:
715 		nfsmapid_gid_str(mapargp, arg_size);
716 		return;
717 	case NFSMAPID_SRV_NETINFO:
718 		nfsmapid_server_netinfo(referral_args, arg_size);
719 	default:
720 		break;
721 	}
722 	mapres.status = NFSMAPID_INVALID;
723 	mapres.u_res.len = 0;
724 	(void) door_return((char *)&mapres, sizeof (struct mapid_res), NULL, 0);
725 }
726 
727 /*
728  * mapid_get_domain() always returns a ptr to TSD, so the
729  * check for a NULL domain is not a simple comparison with
730  * NULL but we need to check the contents of the TSD data.
731  */
732 int
733 cur_domain_null(void)
734 {
735 	char	*p;
736 
737 	if ((p = mapid_get_domain()) == NULL)
738 		return (1);
739 
740 	return (p[0] == '\0');
741 }
742 
743 int
744 extract_domain(char *cp, char **upp, char **dpp)
745 {
746 	/*
747 	 * Caller must insure that the string is valid
748 	 */
749 	*upp = cp;
750 
751 	if ((*dpp = strchr(cp, '@')) == NULL)
752 		return (0);
753 	*(*dpp)++ = '\0';
754 	return (1);
755 }
756 
757 int
758 valid_domain(const char *dom)
759 {
760 	const char	*whoami = "valid_domain";
761 
762 	if (!mapid_stdchk_domain(dom)) {
763 		syslog(LOG_ERR, gettext("%s: Invalid inbound domain name %s."),
764 		    whoami, dom);
765 		return (0);
766 	}
767 
768 	/*
769 	 * NOTE: mapid_get_domain() returns a ptr to TSD.
770 	 */
771 	return (strcasecmp(dom, mapid_get_domain()) == 0);
772 }
773 
774 int
775 validate_id_str(const char *id)
776 {
777 	while (*id) {
778 		if (!isdigit(*id++))
779 			return (0);
780 	}
781 	return (1);
782 }
783 
784 void
785 idmap_kcall(int door_id)
786 {
787 	struct nfsidmap_args args;
788 
789 	if (door_id >= 0) {
790 		args.state = 1;
791 		args.did = door_id;
792 	} else {
793 		args.state = 0;
794 		args.did = 0;
795 	}
796 	(void) _nfssys(NFS_IDMAP, &args);
797 }
798 
799 /*
800  * Get the current NFS domain.
801  *
802  * If nfsmapid_domain is set in NFS SMF, then it is the NFS domain;
803  * otherwise, the DNS domain is used.
804  */
805 void
806 check_domain(int sighup)
807 {
808 	const char	*whoami = "check_domain";
809 	static int	 setup_done = 0;
810 	static cb_t	 cb;
811 
812 	/*
813 	 * Construct the arguments to be passed to libmapid interface
814 	 * If called in response to a SIGHUP, reset any cached DNS TXT
815 	 * RR state.
816 	 */
817 	cb.fcn = cb_update_domain;
818 	cb.signal = sighup;
819 	mapid_reeval_domain(&cb);
820 
821 	/*
822 	 * Restart the signal handler thread if we're still setting up
823 	 */
824 	if (!setup_done) {
825 		setup_done = 1;
826 		if (thr_continue(sig_thread)) {
827 			syslog(LOG_ERR, gettext("%s: Fatal error: signal "
828 			    "handler thread could not be restarted."), whoami);
829 			exit(6);
830 		}
831 	}
832 }
833 
834 /*
835  * Need to be able to open the DIAG_FILE before nfsmapid(1m)
836  * releases it's root priviledges. The DIAG_FILE then remains
837  * open for the duration of this nfsmapid instance via n4_fd.
838  */
839 void
840 open_diag_file()
841 {
842 	static int	msg_done = 0;
843 
844 	if ((n4_fp = fopen(DIAG_FILE, "w+")) != NULL) {
845 		n4_fd = fileno(n4_fp);
846 		return;
847 	}
848 
849 	if (msg_done)
850 		return;
851 
852 	syslog(LOG_ERR, "Failed to create %s. Enable syslog "
853 	    "daemon.debug for more info", DIAG_FILE);
854 	msg_done = 1;
855 }
856 
857 /*
858  * When a new domain name is configured, save to DIAG_FILE
859  * and log to syslog, with LOG_DEBUG level (if configured).
860  */
861 void
862 update_diag_file(char *new)
863 {
864 	char	buf[DNAMEMAX];
865 	ssize_t	n;
866 	size_t	len;
867 
868 	(void) lseek(n4_fd, (off_t)0, SEEK_SET);
869 	(void) ftruncate(n4_fd, 0);
870 	(void) snprintf(buf, DNAMEMAX, "%s\n", new);
871 
872 	len = strlen(buf);
873 	n = write(n4_fd, buf, len);
874 	if (n < 0 || n < len)
875 		syslog(LOG_DEBUG, "Could not write %s to diag file", new);
876 	(void) fsync(n4_fd);
877 
878 	syslog(LOG_DEBUG, "nfsmapid domain = %s", new);
879 }
880 
881 /*
882  * Callback function for libmapid. This will be called
883  * by the lib, everytime the nfsmapid(1m) domain changes.
884  */
885 void *
886 cb_update_domain(void *arg)
887 {
888 	char	*new_dname = (char *)arg;
889 
890 	DTRACE_PROBE1(nfsmapid, daemon__domain, new_dname);
891 	update_diag_file(new_dname);
892 	idmap_kcall(FLUSH_KCACHES_ONLY);
893 
894 	return (NULL);
895 }
896 
897 bool_t
898 xdr_utf8string(XDR *xdrs, utf8string *objp)
899 {
900 	if (xdrs->x_op != XDR_FREE)
901 		return (xdr_bytes(xdrs, (char **)&objp->utf8string_val,
902 		    (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING));
903 	return (TRUE);
904 }
905 
906 
907 int
908 decode_args(xdrproc_t xdrfunc, refd_door_args_t *argp, caddr_t *xdrargs,
909     int size)
910 {
911 	XDR xdrs;
912 
913 	caddr_t tmpargs = (caddr_t)&((refd_door_args_t *)argp)->xdr_arg;
914 	size_t arg_size = ((refd_door_args_t *)argp)->xdr_len;
915 
916 	xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE);
917 
918 	*xdrargs = calloc(1, size);
919 	if (*xdrargs == NULL) {
920 		syslog(LOG_ERR, "error allocating arguments buffer");
921 		return (ENOMEM);
922 	}
923 
924 	if (!(*xdrfunc)(&xdrs, *xdrargs)) {
925 		free(*xdrargs);
926 		*xdrargs = NULL;
927 		syslog(LOG_ERR, "error decoding arguments");
928 		return (EINVAL);
929 	}
930 
931 	return (0);
932 }
933 
934 int
935 encode_res(
936 	xdrproc_t xdrfunc,
937 	refd_door_res_t **results,
938 	caddr_t resp,
939 	int *size)
940 {
941 	XDR xdrs;
942 
943 	*size = xdr_sizeof((*xdrfunc), resp);
944 	*results = malloc(sizeof (refd_door_res_t) + *size);
945 	if (*results == NULL) {
946 		return (ENOMEM);
947 	}
948 	(*results)->xdr_len = *size;
949 	*size = sizeof (refd_door_res_t) + (*results)->xdr_len;
950 	xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res),
951 	    (*results)->xdr_len, XDR_ENCODE);
952 	if (!(*xdrfunc)(&xdrs, resp)) {
953 		(*results)->res_status = EINVAL;
954 		syslog(LOG_ERR, "error encoding results");
955 		return ((*results)->res_status);
956 	}
957 	(*results)->res_status = 0;
958 	return ((*results)->res_status);
959 }
960 
961 
962 bool_t
963 xdr_knetconfig(XDR *xdrs, struct knetconfig *objp)
964 {
965 	rpc_inline_t *buf;
966 	int i;
967 	u_longlong_t dev64;
968 #if !defined(_LP64)
969 	uint32_t major, minor;
970 #endif
971 
972 	if (!xdr_u_int(xdrs, &objp->knc_semantics))
973 		return (FALSE);
974 	if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE))
975 		return (FALSE);
976 	if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE))
977 		return (FALSE);
978 
979 	/*
980 	 * For interoperability between 32-bit daemon and 64-bit kernel,
981 	 * we always treat dev_t as 64-bit number and do the expanding
982 	 * or compression of dev_t as needed.
983 	 * We have to hand craft the conversion since there is no available
984 	 * function in ddi.c. Besides ddi.c is available only in the kernel
985 	 * and we want to keep both user and kernel of xdr_knetconfig() the
986 	 * same for consistency.
987 	 */
988 
989 	if (xdrs->x_op == XDR_ENCODE) {
990 #if defined(_LP64)
991 		dev64 = objp->knc_rdev;
992 #else
993 		major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32;
994 		minor = objp->knc_rdev & MAXMIN32;
995 		dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor;
996 #endif
997 		if (!xdr_u_longlong_t(xdrs, &dev64))
998 			return (FALSE);
999 	}
1000 	if (xdrs->x_op == XDR_DECODE) {
1001 #if defined(_LP64)
1002 		if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev))
1003 			return (FALSE);
1004 #else
1005 		if (!xdr_u_longlong_t(xdrs, &dev64))
1006 			return (FALSE);
1007 
1008 		major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32;
1009 		minor = dev64 & L_MAXMIN32;
1010 		objp->knc_rdev = (major << L_BITSMINOR32) | minor;
1011 #endif
1012 	}
1013 
1014 	if (xdrs->x_op == XDR_ENCODE) {
1015 		buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
1016 		if (buf == NULL) {
1017 			if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
1018 			    sizeof (uint_t), (xdrproc_t)xdr_u_int))
1019 				return (FALSE);
1020 		} else {
1021 			uint_t *genp;
1022 
1023 			for (i = 0, genp = objp->knc_unused;
1024 			    i < 8; i++) {
1025 #if defined(_LP64) || defined(_KERNEL)
1026 				IXDR_PUT_U_INT32(buf, *genp++);
1027 #else
1028 				IXDR_PUT_U_LONG(buf, *genp++);
1029 #endif
1030 			}
1031 		}
1032 		return (TRUE);
1033 	} else if (xdrs->x_op == XDR_DECODE) {
1034 		buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT);
1035 		if (buf == NULL) {
1036 			if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
1037 			    sizeof (uint_t), (xdrproc_t)xdr_u_int))
1038 				return (FALSE);
1039 		} else {
1040 			uint_t *genp;
1041 
1042 			for (i = 0, genp = objp->knc_unused;
1043 			    i < 8; i++) {
1044 #if defined(_LP64) || defined(_KERNEL)
1045 				*genp++ = IXDR_GET_U_INT32(buf);
1046 #else
1047 				*genp++ = IXDR_GET_U_LONG(buf);
1048 #endif
1049 			}
1050 		}
1051 		return (TRUE);
1052 	}
1053 
1054 	if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8,
1055 	    sizeof (uint_t), (xdrproc_t)xdr_u_int))
1056 		return (FALSE);
1057 	return (TRUE);
1058 }
1059 
1060 /*
1061  * used by NFSv4 referrals to get info needed for NFSv4 referral mount.
1062  */
1063 bool_t
1064 xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp)
1065 {
1066 
1067 	if (!xdr_u_int(xdrs, &objp->netbuf_len))
1068 		return (FALSE);
1069 	if (!xdr_u_int(xdrs, &objp->netnm_len))
1070 		return (FALSE);
1071 	if (!xdr_u_int(xdrs, &objp->knconf_len))
1072 		return (FALSE);
1073 	if (!xdr_string(xdrs, &objp->netname, ~0))
1074 		return (FALSE);
1075 	if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len,
1076 	    (xdrproc_t)xdr_netbuf))
1077 		return (FALSE);
1078 	if (!xdr_pointer(xdrs, (char **)&objp->knconf,
1079 	    objp->knconf_len, (xdrproc_t)xdr_knetconfig))
1080 		return (FALSE);
1081 	return (TRUE);
1082 }
1083