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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Door server routines for nfsmapid daemon
31  * Translate NFSv4 users and groups between numeric and string values
32  */
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <alloca.h>
36 #include <signal.h>
37 #include <libintl.h>
38 #include <limits.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <memory.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <door.h>
46 #include <syslog.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <assert.h>
50 #include <deflt.h>
51 #include <nfs/nfs4.h>
52 #include <nfs/nfssys.h>
53 #include <nfs/nfsid_map.h>
54 #include "nfsmapid_resolv.h"
55 
56 /*
57  * We cannot use the backend nscd as it may make syscalls that may
58  * cause further nfsmapid upcalls introducing deadlock.
59  * Use the internal uncached versions of get*_r.
60  */
61 extern struct group *_uncached_getgrgid_r(gid_t, struct group *, char *, int);
62 extern struct group *_uncached_getgrnam_r(const char *, struct group *,
63     char *, int);
64 extern struct passwd *_uncached_getpwuid_r(uid_t, struct passwd *, char *, int);
65 extern struct passwd *_uncached_getpwnam_r(const char *, struct passwd *,
66     char *, int);
67 
68 /*
69  * is timestamp a == b?
70  */
71 #define	TIMESTRUC_EQ(a, b) \
72 	(((a).tv_sec == (b).tv_sec) && ((a).tv_nsec == (b).tv_nsec))
73 
74 #define	UID_MAX_STR_LEN	11	/* Digits in UID_MAX + 1 */
75 
76 /*
77  * domain*: describe nfsmapid domain currently in use
78  * nfs_*  : describe nfsmapid domain specified by /etc/default/nfs
79  * dns_*  : describe nfsmapid domain speficied by /etc/resolv.conf
80  *
81  * domain_cfg_lock: rwlock used to serialize access/changes to the
82  * vars listed above (between nfsmapid service threads).
83  *
84  * Each nfsmapid thread holds the rdlock and stats the config files.
85  * If the mtime is different, then they get the writelock and update
86  * the cached info.
87  *
88  * If the domain is set via /etc/default/nfs, then we don't have
89  * to look at resolv.conf.
90  */
91 timestruc_t	nfs_mtime = {0};
92 uint32_t	nfs_domain_len = 0;
93 char		nfs_domain[NS_MAXCDNAME + 1] = {0};
94 
95 timestruc_t	dns_mtime = {0};
96 uint32_t	dns_domain_len = 0;
97 char		dns_domain[NS_MAXCDNAME + 1] = {0};
98 
99 uint32_t	cur_domain_len = 0;
100 char		cur_domain[NS_MAXCDNAME + 1] = {0};
101 #define		CUR_DOMAIN_NULL()		cur_domain[0] == '\0'
102 
103 timestruc_t	zapped_mtime = {0};
104 
105 #define		ZAP_DOMAIN(which) {		\
106 		which##_domain[0] = '\0';	\
107 		which##_domain_len = 0;		\
108 		which##_mtime = zapped_mtime;	\
109 }
110 
111 rwlock_t	domain_cfg_lock = DEFAULTRWLOCK;
112 
113 /*
114  * Diags
115  */
116 #define	DIAG_FILE	"/var/run/nfs4_domain"
117 FILE		*n4_fp;
118 
119 extern size_t	pwd_buflen;
120 extern size_t	grp_buflen;
121 extern thread_t	sig_thread;
122 
123 /*
124  * Prototypes
125  */
126 extern void	check_domain(int);
127 extern void	idmap_kcall(int);
128 extern int	standard_domain_str(const char *);
129 extern int	_nfssys(int, void *);
130 static int	valid_domain(const char *);
131 static int	validate_id_str(const char *);
132 static int	get_mtime(char *, timestruc_t *);
133 static void	get_nfs_domain(void);
134 static void	get_dns_domain(void);
135 static int	extract_domain(char *, char **, char **);
136 extern void	update_diag_file(char *);
137 
138 static void
139 nfsmapid_str_uid(struct mapid_arg *argp, size_t arg_size)
140 {
141 	struct mapid_res result;
142 	struct passwd	 pwd;
143 	char		*pwd_buf;
144 	char		*user;
145 	char		*domain;
146 
147 	if (argp->u_arg.len <= 0 || arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
148 		result.status = NFSMAPID_INVALID;
149 		result.u_res.uid = UID_NOBODY;
150 		goto done;
151 	}
152 
153 	if (!extract_domain(argp->str, &user, &domain)) {
154 		long id;
155 
156 		/*
157 		 * Invalid "user@dns_domain" string. Still, the user
158 		 * part might be an encoded uid, so do a final check.
159 		 * Remember, domain part of string was not set since
160 		 * not a valid string.
161 		 */
162 		if (!validate_id_str(user)) {
163 			result.status = NFSMAPID_UNMAPPABLE;
164 			result.u_res.uid = UID_NOBODY;
165 			goto done;
166 		}
167 
168 		/*
169 		 * Since atoi() does not return proper errors for
170 		 * invalid translation, use strtol() instead.
171 		 */
172 		errno = 0;
173 		id = strtol(user, (char **)NULL, 10);
174 
175 		if (errno || id < 0 || id > UID_MAX) {
176 			result.status = NFSMAPID_UNMAPPABLE;
177 			result.u_res.uid = UID_NOBODY;
178 			goto done;
179 		}
180 
181 		result.u_res.uid = (uid_t)id;
182 		result.status = NFSMAPID_NUMSTR;
183 		goto done;
184 	}
185 
186 	/*
187 	 * String properly constructed. Now we check for domain and
188 	 * group validity. Note that we only look at the domain iff
189 	 * the local domain is configured.
190 	 */
191 	if (!CUR_DOMAIN_NULL() && !valid_domain(domain)) {
192 		result.status = NFSMAPID_BADDOMAIN;
193 		result.u_res.uid = UID_NOBODY;
194 		goto done;
195 	}
196 
197 	if ((pwd_buf = malloc(pwd_buflen)) == NULL ||
198 	    _uncached_getpwnam_r(user, &pwd, pwd_buf, pwd_buflen) == NULL) {
199 
200 		if (pwd_buf == NULL)
201 			result.status = NFSMAPID_INTERNAL;
202 		else {
203 			/*
204 			 * Not a valid user
205 			 */
206 			result.status = NFSMAPID_NOTFOUND;
207 			free(pwd_buf);
208 		}
209 		result.u_res.uid = UID_NOBODY;
210 		goto done;
211 	}
212 
213 	/*
214 	 * Valid user entry
215 	 */
216 	result.u_res.uid = pwd.pw_uid;
217 	result.status = NFSMAPID_OK;
218 	free(pwd_buf);
219 done:
220 	(void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
221 }
222 
223 /* ARGSUSED1 */
224 static void
225 nfsmapid_uid_str(struct mapid_arg *argp, size_t arg_size)
226 {
227 	struct mapid_res	 result;
228 	struct mapid_res	*resp;
229 	struct passwd		 pwd;
230 	int			 pwd_len;
231 	char			*pwd_buf;
232 	uid_t			 uid = argp->u_arg.uid;
233 	size_t			 uid_str_len;
234 	char			*pw_str;
235 	size_t			 pw_str_len;
236 	char			*at_str;
237 	size_t			 at_str_len;
238 	char			 dom_str[NS_MAXCDNAME + 1];
239 	size_t			 dom_str_len;
240 
241 	if (uid < 0 || uid > UID_MAX) {
242 		/*
243 		 * Negative uid or greater than UID_MAX
244 		 */
245 		resp = &result;
246 		resp->status = NFSMAPID_BADID;
247 		resp->u_res.len = 0;
248 		goto done;
249 	}
250 
251 	/*
252 	 * Make local copy of domain for further manipuation
253 	 */
254 	(void) rw_rdlock(&domain_cfg_lock);
255 	if (CUR_DOMAIN_NULL()) {
256 		dom_str_len = 0;
257 		dom_str[0] = '\0';
258 	} else {
259 		dom_str_len = cur_domain_len;
260 		bcopy(cur_domain, dom_str, cur_domain_len);
261 		dom_str[dom_str_len] = '\0';
262 	}
263 	(void) rw_unlock(&domain_cfg_lock);
264 
265 	/*
266 	 * We want to encode the uid into a literal string... :
267 	 *
268 	 *	- upon failure to allocate space from the heap
269 	 *	- if there is no current domain configured
270 	 *	- if there is no such uid in the passwd DB's
271 	 */
272 	if ((pwd_buf = malloc(pwd_buflen)) == NULL || dom_str_len == 0 ||
273 	    _uncached_getpwuid_r(uid, &pwd, pwd_buf, pwd_buflen) == NULL) {
274 
275 		/*
276 		 * If we could not allocate from the heap, try
277 		 * allocating from the stack as a last resort.
278 		 */
279 		if (pwd_buf == NULL && (pwd_buf =
280 		    alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
281 			resp = &result;
282 			resp->status = NFSMAPID_INTERNAL;
283 			resp->u_res.len = 0;
284 			goto done;
285 		}
286 
287 		/*
288 		 * Constructing literal string without '@' so that
289 		 * we'll know that it's not a user, but rather a
290 		 * uid encoded string. Can't overflow because we
291 		 * already checked UID_MAX.
292 		 */
293 		pw_str = pwd_buf;
294 		(void) sprintf(pw_str, "%d", (int)uid);
295 		pw_str_len = strlen(pw_str);
296 		at_str_len = dom_str_len = 0;
297 		at_str = "";
298 		dom_str[0] = '\0';
299 	} else {
300 		/*
301 		 * Otherwise, we construct the "user@domain" string
302 		 */
303 		pw_str = pwd.pw_name;
304 		pw_str_len = strlen(pw_str);
305 		at_str = "@";
306 		at_str_len = 1;
307 	}
308 
309 	uid_str_len = pw_str_len + at_str_len + dom_str_len;
310 	if ((resp = alloca(MAPID_RES_LEN(UID_MAX_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 	free(pwd_buf);
320 	resp->status = NFSMAPID_OK;
321 
322 done:
323 	/*
324 	 * There is a chance that the door_return will fail because the
325 	 * resulting string is too large, try to indicate that if possible
326 	 */
327 	if (door_return((char *)resp,
328 	    MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
329 		resp->status = NFSMAPID_INTERNAL;
330 		resp->u_res.len = 0;
331 		(void) door_return((char *)&result, sizeof (struct mapid_res),
332 							NULL, 0);
333 	}
334 }
335 
336 static void
337 nfsmapid_str_gid(struct mapid_arg *argp, size_t arg_size)
338 {
339 	struct mapid_res	result;
340 	struct group		grp;
341 	char			*grp_buf;
342 	char			*group;
343 	char			*domain;
344 
345 	if (argp->u_arg.len <= 0 ||
346 				arg_size < MAPID_ARG_LEN(argp->u_arg.len)) {
347 		result.status = NFSMAPID_INVALID;
348 		result.u_res.gid = GID_NOBODY;
349 		goto done;
350 	}
351 
352 	if (!extract_domain(argp->str, &group, &domain)) {
353 		long id;
354 
355 		/*
356 		 * Invalid "group@dns_domain" string. Still, the
357 		 * group part might be an encoded gid, so do a
358 		 * final check. Remember, domain part of string
359 		 * was not set since not a valid string.
360 		 */
361 		if (!validate_id_str(group)) {
362 			result.status = NFSMAPID_UNMAPPABLE;
363 			result.u_res.gid = GID_NOBODY;
364 			goto done;
365 		}
366 
367 		/*
368 		 * Since atoi() does not return proper errors for
369 		 * invalid translation, use strtol() instead.
370 		 */
371 		errno = 0;
372 		id = strtol(group, (char **)NULL, 10);
373 
374 		if (errno || id < 0 || id > UID_MAX) {
375 			result.status = NFSMAPID_UNMAPPABLE;
376 			result.u_res.gid = GID_NOBODY;
377 			goto done;
378 		}
379 
380 		result.u_res.gid = (gid_t)id;
381 		result.status = NFSMAPID_NUMSTR;
382 		goto done;
383 	}
384 
385 	/*
386 	 * String properly constructed. Now we check for domain and
387 	 * group validity. Note that we only look at the domain iff
388 	 * the local domain is configured.
389 	 */
390 	if (!CUR_DOMAIN_NULL() && !valid_domain(domain)) {
391 		result.status = NFSMAPID_BADDOMAIN;
392 		result.u_res.gid = GID_NOBODY;
393 		goto done;
394 	}
395 
396 	if ((grp_buf = malloc(grp_buflen)) == NULL ||
397 	    _uncached_getgrnam_r(group, &grp, grp_buf, grp_buflen) == NULL) {
398 
399 		if (grp_buf == NULL)
400 			result.status = NFSMAPID_INTERNAL;
401 		else {
402 			/*
403 			 * Not a valid group
404 			 */
405 			result.status = NFSMAPID_NOTFOUND;
406 			free(grp_buf);
407 		}
408 		result.u_res.gid = GID_NOBODY;
409 		goto done;
410 	}
411 
412 	/*
413 	 * Valid group entry
414 	 */
415 	result.status = NFSMAPID_OK;
416 	result.u_res.gid = grp.gr_gid;
417 	free(grp_buf);
418 done:
419 	(void) door_return((char *)&result, sizeof (struct mapid_res), NULL, 0);
420 }
421 
422 /* ARGSUSED1 */
423 static void
424 nfsmapid_gid_str(struct mapid_arg *argp, size_t arg_size)
425 {
426 	struct mapid_res	 result;
427 	struct mapid_res	*resp;
428 	struct group		 grp;
429 	char			*grp_buf;
430 	gid_t			 gid = argp->u_arg.gid;
431 	size_t			 gid_str_len;
432 	char			*gr_str;
433 	size_t			 gr_str_len;
434 	char			*at_str;
435 	size_t			 at_str_len;
436 	char			 dom_str[NS_MAXCDNAME + 1];
437 	size_t			 dom_str_len;
438 
439 	if (gid < 0 || gid > UID_MAX) {
440 		/*
441 		 * Negative gid or greater than UID_MAX
442 		 */
443 		resp = &result;
444 		resp->status = NFSMAPID_BADID;
445 		resp->u_res.len = 0;
446 		goto done;
447 	}
448 
449 	/*
450 	 * Make local copy of domain for further manipuation
451 	 */
452 	(void) rw_rdlock(&domain_cfg_lock);
453 	if (CUR_DOMAIN_NULL()) {
454 		dom_str_len = 0;
455 		dom_str[0] = '\0';
456 	} else {
457 		dom_str_len = cur_domain_len;
458 		bcopy(cur_domain, dom_str, cur_domain_len);
459 		dom_str[dom_str_len] = '\0';
460 	}
461 	(void) rw_unlock(&domain_cfg_lock);
462 
463 	/*
464 	 * We want to encode the gid into a literal string... :
465 	 *
466 	 *	- upon failure to allocate space from the heap
467 	 *	- if there is no current domain configured
468 	 *	- if there is no such gid in the group DB's
469 	 */
470 	if ((grp_buf = malloc(grp_buflen)) == NULL || dom_str_len == 0 ||
471 	    _uncached_getgrgid_r(gid, &grp, grp_buf, grp_buflen) == NULL) {
472 
473 		/*
474 		 * If we could not allocate from the heap, try
475 		 * allocating from the stack as a last resort.
476 		 */
477 		if (grp_buf == NULL && (grp_buf =
478 		    alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
479 			resp = &result;
480 			resp->status = NFSMAPID_INTERNAL;
481 			resp->u_res.len = 0;
482 			goto done;
483 		}
484 
485 		/*
486 		 * Constructing literal string without '@' so that
487 		 * we'll know that it's not a group, but rather a
488 		 * gid encoded string. Can't overflow because we
489 		 * already checked UID_MAX.
490 		 */
491 		gr_str = grp_buf;
492 		(void) sprintf(gr_str, "%d", (int)gid);
493 		gr_str_len = strlen(gr_str);
494 		at_str_len = dom_str_len = 0;
495 		at_str = "";
496 		dom_str[0] = '\0';
497 	} else {
498 		/*
499 		 * Otherwise, we construct the "group@domain" string
500 		 */
501 		gr_str = grp.gr_name;
502 		gr_str_len = strlen(gr_str);
503 		at_str = "@";
504 		at_str_len = 1;
505 	}
506 
507 	gid_str_len = gr_str_len + at_str_len + dom_str_len;
508 	if ((resp = alloca(MAPID_RES_LEN(UID_MAX_STR_LEN))) == NULL) {
509 		resp = &result;
510 		resp->status = NFSMAPID_INTERNAL;
511 		resp->u_res.len = 0;
512 		goto done;
513 	}
514 	/* LINTED format argument to sprintf */
515 	(void) sprintf(resp->str, "%s%s%s", gr_str, at_str, dom_str);
516 	resp->u_res.len = gid_str_len;
517 	free(grp_buf);
518 	resp->status = NFSMAPID_OK;
519 
520 done:
521 	/*
522 	 * There is a chance that the door_return will fail because the
523 	 * resulting string is too large, try to indicate that if possible
524 	 */
525 	if (door_return((char *)resp,
526 	    MAPID_RES_LEN(resp->u_res.len), NULL, 0) == -1) {
527 		resp->status = NFSMAPID_INTERNAL;
528 		resp->u_res.len = 0;
529 		(void) door_return((char *)&result, sizeof (struct mapid_res),
530 							NULL, 0);
531 	}
532 }
533 
534 /* ARGSUSED */
535 void
536 nfsmapid_func(void *cookie, char *argp, size_t arg_size,
537 						door_desc_t *dp, uint_t n_desc)
538 {
539 	struct mapid_arg	*mapargp;
540 	struct mapid_res	mapres;
541 
542 	/*
543 	 * Make sure we have a valid argument
544 	 */
545 	if (arg_size < sizeof (struct mapid_arg)) {
546 		mapres.status = NFSMAPID_INVALID;
547 		mapres.u_res.len = 0;
548 		(void) door_return((char *)&mapres, sizeof (struct mapid_res),
549 								NULL, 0);
550 		return;
551 	}
552 
553 	/* LINTED pointer cast */
554 	mapargp = (struct mapid_arg *)argp;
555 	switch (mapargp->cmd) {
556 	case NFSMAPID_STR_UID:
557 		nfsmapid_str_uid(mapargp, arg_size);
558 		return;
559 	case NFSMAPID_UID_STR:
560 		nfsmapid_uid_str(mapargp, arg_size);
561 		return;
562 	case NFSMAPID_STR_GID:
563 		nfsmapid_str_gid(mapargp, arg_size);
564 		return;
565 	case NFSMAPID_GID_STR:
566 		nfsmapid_gid_str(mapargp, arg_size);
567 		return;
568 	default:
569 		break;
570 	}
571 	mapres.status = NFSMAPID_INVALID;
572 	mapres.u_res.len = 0;
573 	(void) door_return((char *)&mapres, sizeof (struct mapid_res), NULL, 0);
574 }
575 
576 static int
577 extract_domain(char *cp, char **upp, char **dpp)
578 {
579 	/*
580 	 * Caller must insure that the string is valid
581 	 */
582 	*upp = cp;
583 
584 	if ((*dpp = strchr(cp, '@')) == NULL)
585 		return (0);
586 	*(*dpp)++ = '\0';
587 	return (1);
588 }
589 
590 static int
591 valid_domain(const char *dom)
592 {
593 	const char	*whoami = "valid_domain";
594 
595 	if (!standard_domain_str(dom)) {
596 		syslog(LOG_ERR, gettext("%s: Invalid domain name %s. Check "
597 			"configuration file and restart daemon."), whoami, dom);
598 		return (0);
599 	}
600 
601 	(void) rw_rdlock(&domain_cfg_lock);
602 	if (strcasecmp(dom, cur_domain) == 0) {
603 		(void) rw_unlock(&domain_cfg_lock);
604 		return (1);
605 	}
606 	(void) rw_unlock(&domain_cfg_lock);
607 	return (0);
608 }
609 
610 static int
611 validate_id_str(const char *id)
612 {
613 	while (*id) {
614 		if (!isdigit(*id++))
615 			return (0);
616 	}
617 	return (1);
618 }
619 
620 static int
621 get_mtime(char *fname, timestruc_t *mtim)
622 {
623 	struct stat st;
624 	int err;
625 
626 	if ((err = stat(fname, &st)) != 0)
627 		return (err);
628 
629 	*mtim = st.st_mtim;
630 	return (0);
631 }
632 
633 static void
634 get_nfs_domain(void)
635 {
636 	const char	*whoami = "get_nfs_domain";
637 	char		*ndomain;
638 	timestruc_t	 ntime;
639 
640 	/*
641 	 * If we can't get stats for the config file, then
642 	 * zap the NFS domain info.  If mtime hasn't changed,
643 	 * then there's no work to do, so just return.
644 	 */
645 	if (get_mtime(NFSADMIN, &ntime) != 0) {
646 		ZAP_DOMAIN(nfs);
647 		return;
648 	}
649 
650 	if (TIMESTRUC_EQ(ntime, nfs_mtime))
651 		return;
652 
653 	/*
654 	 * Get NFSMAPID_DOMAIN value from /etc/default/nfs for now.
655 	 * Note: defread() returns a ptr to TSD.
656 	 */
657 	if (defopen(NFSADMIN) == 0) {
658 		ndomain = (char *)defread("NFSMAPID_DOMAIN=");
659 
660 		/* close default file */
661 		(void) defopen(NULL);
662 
663 		/*
664 		 * NFSMAPID_DOMAIN was set so its time for validation.
665 		 * If its okay, then update NFS domain and return.  If not,
666 		 * complain about invalid domain.
667 		 */
668 		if (ndomain) {
669 			if (standard_domain_str(ndomain)) {
670 				nfs_domain_len = strlen(ndomain);
671 				(void) strncpy(nfs_domain, ndomain,
672 								NS_MAXCDNAME);
673 				nfs_mtime = ntime;
674 				return;
675 			}
676 
677 			syslog(LOG_ERR, gettext("%s: Invalid domain name %s. "
678 				"Check configuration file and restart daemon."),
679 				whoami, ndomain);
680 		}
681 	}
682 
683 	/*
684 	 * So the NFS config file changed but it couldn't be opened or
685 	 * it didn't specify NFSMAPID_DOMAIN or it specified an invalid
686 	 * NFSMAPID_DOMAIN.  Time to zap current NFS domain info.
687 	 */
688 	ZAP_DOMAIN(nfs);
689 }
690 
691 static void
692 get_dns_domain(void)
693 {
694 #ifdef DEBUG
695 	const char	*whoami = "get_dns_domain";
696 #endif
697 	timestruc_t	 ntime = {0};
698 
699 	/*
700 	 * If we can't get stats for the config file, then
701 	 * zap the DNS domain info.  If mtime hasn't changed,
702 	 * then there's no work to do, so just return.
703 	 */
704 	errno = 0;
705 	if (get_mtime(_PATH_RESCONF, &ntime) != 0) {
706 		switch (errno) {
707 			case ENOENT:
708 				/*
709 				 * The resolver defaults to obtaining the
710 				 * domain off of the NIS domainname(1M) if
711 				 * /etc/resolv.conf does not exist, so we
712 				 * move forward.
713 				 */
714 				IDMAP_DBG("%s: no %s file", whoami,
715 				    _PATH_RESCONF);
716 				break;
717 
718 			default:
719 				ZAP_DOMAIN(dns);
720 				return;
721 		}
722 	} else if (TIMESTRUC_EQ(ntime, dns_mtime)) {
723 		IDMAP_DBG("%s: no mtime changes in %s", whoami, _PATH_RESCONF);
724 		return;
725 	}
726 
727 	/*
728 	 * Re-initialize resolver to zap DNS domain from previous
729 	 * resolv_init() calls.
730 	 */
731 	(void) resolv_init();
732 
733 	/*
734 	 * Update cached DNS domain.  No need for validation since
735 	 * domain comes from resolver.  If resolver doesn't return the
736 	 * domain, then zap the DNS domain.  This shouldn't ever happen,
737 	 * and if it does, the machine has bigger problems (so no need
738 	 * to generating a message that says DNS appears to be broken).
739 	 */
740 	(void) rw_rdlock(&dns_data_lock);
741 	if (sysdns_domain[0] != '\0') {
742 		(void) strncpy(dns_domain, sysdns_domain, NS_MAXCDNAME);
743 		dns_mtime = ntime;
744 		dns_domain_len = strlen(sysdns_domain);
745 		(void) rw_unlock(&dns_data_lock);
746 		return;
747 	}
748 	(void) rw_unlock(&dns_data_lock);
749 
750 	ZAP_DOMAIN(dns);
751 }
752 
753 void
754 idmap_kcall(int did)
755 {
756 	struct nfsidmap_args args;
757 
758 	if (did >= 0) {
759 		args.state = 1;
760 		args.did = did;
761 	} else {
762 		args.state = 0;
763 		args.did = 0;
764 	}
765 
766 	(void) _nfssys(NFS_IDMAP, &args);
767 }
768 
769 /*
770  * Get the current NFS domain.
771  *
772  * If NFSMAPID_DOMAIN is set in /etc/default/nfs, then it is the NFS domain;
773  * otherwise, the DNS domain is used.
774  */
775 void
776 check_domain(int flush)
777 {
778 	const char	*whoami = "check_domain";
779 	char		*new_domain;
780 	int		 new_dlen = 0;
781 	static int	 setup_done = 0;
782 
783 	get_nfs_domain();
784 	if (nfs_domain_len != 0) {
785 		new_domain = nfs_domain;
786 		new_dlen = nfs_domain_len;
787 		IDMAP_DBG("%s: NFS File Domain: %s", whoami, nfs_domain);
788 		goto dname_chkd;
789 	}
790 
791 	/*
792 	 * If called in response to a SIGHUP,
793 	 * reset any cached DNS TXT RR state.
794 	 */
795 	get_dns_txt_domain(flush);
796 	if (dns_txt_domain_len != 0) {
797 		new_domain = dns_txt_domain;
798 		new_dlen = dns_txt_domain_len;
799 		IDMAP_DBG("%s: DNS TXT Record: %s", whoami, dns_txt_domain);
800 	} else {
801 		/*
802 		 * We're either here because:
803 		 *
804 		 *  . NFSMAPID_DOMAIN was not set in /etc/default/nfs
805 		 *  . No suitable DNS TXT resource record exists
806 		 *  . DNS server is not responding to requests
807 		 *
808 		 * in either case, we want to default to using the
809 		 * system configured DNS domain. If this fails, then
810 		 * dns_domain will be empty and dns_domain_len will
811 		 * be 0.
812 		 */
813 		get_dns_domain();
814 		new_domain = dns_domain;
815 		new_dlen = dns_domain_len;
816 		IDMAP_DBG("%s: Default DNS Domain: %s", whoami, dns_domain);
817 	}
818 
819 dname_chkd:
820 	/*
821 	 * Update cur_domain if new_domain is different.  Set flush
822 	 * to guarantee that kernel idmapping caches are flushed.
823 	 */
824 	if (strncasecmp(new_domain, cur_domain, NS_MAXCDNAME)) {
825 		(void) rw_wrlock(&domain_cfg_lock);
826 		(void) strncpy(cur_domain, new_domain, NS_MAXCDNAME);
827 		cur_domain_len = new_dlen;
828 		update_diag_file(new_domain);
829 		DTRACE_PROBE1(nfsmapid, daemon__domain, cur_domain);
830 		(void) rw_unlock(&domain_cfg_lock);
831 		flush = 1;
832 	}
833 
834 	/*
835 	 * Restart the signal handler thread if we're still setting up
836 	 */
837 	if (!setup_done) {
838 		setup_done = 1;
839 		IDMAP_DBG("%s: Initial setup done !", whoami, NULL);
840 		if (thr_continue(sig_thread)) {
841 			syslog(LOG_ERR, gettext("%s: Fatal error: signal "
842 			    "handler thread could not be restarted."), whoami);
843 			exit(6);
844 		}
845 
846 		/*
847 		 * We force bail here so we don't end up flushing kernel
848 		 * caches until we _know_ we're up.
849 		 */
850 		return;
851 	}
852 
853 	/*
854 	 * If caller requested flush or if domain has changed, then
855 	 * flush kernel idmapping caches.
856 	 */
857 	if (flush)
858 		idmap_kcall(-1);
859 }
860 
861 
862 /*
863  * Based on the recommendations from
864  *	RFC1033  DOMAIN ADMINISTRATORS OPERATIONS GUIDE
865  *	RFC1035  DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
866  * check if a given domain name string is valid.
867  */
868 int
869 standard_domain_str(const char *ds)
870 {
871 	int	i;
872 
873 	for (i = 0; *ds && i < NS_MAXCDNAME; i++, ds++) {
874 		if (!isalpha(*ds) && !isdigit(*ds) && (*ds != '.') &&
875 				(*ds != '-') && (*ds != '_'))
876 			return (0);
877 	}
878 	if (i == NS_MAXCDNAME)
879 		return (0);
880 	return (1);
881 }
882 
883 /*
884  * Need to be able to open the DIAG_FILE before nfsmapid(1m)
885  * releases it's root priviledges. The DIAG_FILE then remains
886  * open for the duration of this nfsmapid instance via n4_fp.
887  */
888 void
889 open_diag_file()
890 {
891 	static int	msg_done = 0;
892 
893 	if ((n4_fp = fopen(DIAG_FILE, "w+")) != NULL)
894 		return;
895 
896 	if (msg_done)
897 		return;
898 
899 	syslog(LOG_ERR, "Failed to create %s. Enable syslog "
900 			"daemon.debug for more info", DIAG_FILE);
901 	msg_done = 1;
902 }
903 
904 /*
905  * When a new domain name is configured, save to DIAG_FILE
906  * and log to syslog, with LOG_DEBUG level (if configured).
907  */
908 void
909 update_diag_file(char *new)
910 {
911 	rewind(n4_fp);
912 	ftruncate(fileno(n4_fp), 0);
913 	fprintf(n4_fp, "%.*s\n", NS_MAXCDNAME, new);
914 	fflush(n4_fp);
915 
916 	syslog(LOG_DEBUG, "nfsmapid domain = %s", new);
917 }
918