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