xref: /illumos-gate/usr/src/cmd/fs.d/nfs/mountd/mountd.c (revision cf7e209d)
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  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
22  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
23  */
24 
25 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
26 /*	  All Rights Reserved  	*/
27 
28 /*
29  * Portions of this source code were derived from Berkeley 4.3 BSD
30  * under license from the Regents of the University of California.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <sys/param.h>
40 #include <rpc/rpc.h>
41 #include <sys/stat.h>
42 #include <netconfig.h>
43 #include <netdir.h>
44 #include <sys/file.h>
45 #include <sys/time.h>
46 #include <sys/errno.h>
47 #include <rpcsvc/mount.h>
48 #include <sys/pathconf.h>
49 #include <sys/systeminfo.h>
50 #include <sys/utsname.h>
51 #include <sys/wait.h>
52 #include <signal.h>
53 #include <locale.h>
54 #include <unistd.h>
55 #include <errno.h>
56 #include <sys/socket.h>
57 #include <netinet/in.h>
58 #include <arpa/inet.h>
59 #include <netdb.h>
60 #include <thread.h>
61 #include <assert.h>
62 #include <priv_utils.h>
63 #include <nfs/auth.h>
64 #include <nfs/nfssys.h>
65 #include <nfs/nfs.h>
66 #include <nfs/nfs_sec.h>
67 #include <rpcsvc/daemon_utils.h>
68 #include <deflt.h>
69 #include "../../fslib.h"
70 #include <sharefs/share.h>
71 #include <sharefs/sharetab.h>
72 #include "../lib/sharetab.h"
73 #include "mountd.h"
74 #include <tsol/label.h>
75 #include <sys/tsol/label_macro.h>
76 #include <libtsnet.h>
77 #include <sys/sdt.h>
78 #include <libscf.h>
79 #include <limits.h>
80 #include <sys/nvpair.h>
81 #include <attr.h>
82 #include "smfcfg.h"
83 
84 extern int daemonize_init(void);
85 extern void daemonize_fini(int fd);
86 
87 struct sh_list *share_list;
88 
89 rwlock_t sharetab_lock;		/* lock to protect the cached sharetab */
90 static mutex_t mnttab_lock;	/* prevent concurrent mnttab readers */
91 
92 static mutex_t logging_queue_lock;
93 static cond_t logging_queue_cv;
94 
95 static share_t *find_lofsentry(char *, int *);
96 static int getclientsnames_lazy(char *, struct netbuf **,
97 	struct nd_hostservlist **);
98 static int getclientsnames(SVCXPRT *, struct netbuf **,
99 	struct nd_hostservlist **);
100 static int getclientsflavors_old(share_t *, SVCXPRT *, struct netbuf **,
101 	struct nd_hostservlist **, int *);
102 static int getclientsflavors_new(share_t *, SVCXPRT *, struct netbuf **,
103 	struct nd_hostservlist **, int *);
104 static int check_client_old(share_t *, SVCXPRT *, struct netbuf **,
105 	struct nd_hostservlist **, int);
106 static int check_client_new(share_t *, SVCXPRT *, struct netbuf **,
107 	struct nd_hostservlist **, int);
108 static void mnt(struct svc_req *, SVCXPRT *);
109 static void mnt_pathconf(struct svc_req *);
110 static int mount(struct svc_req *r);
111 static void sh_free(struct sh_list *);
112 static void umount(struct svc_req *);
113 static void umountall(struct svc_req *);
114 static void sigexit(int);
115 static int newopts(char *);
116 static tsol_tpent_t *get_client_template(struct sockaddr *);
117 
118 static int verbose;
119 static int rejecting;
120 static int mount_vers_min = MOUNTVERS;
121 static int mount_vers_max = MOUNTVERS3;
122 
123 /* Needs to be accessed by nfscmd.c */
124 int  in_access_list(SVCXPRT *, struct netbuf **,
125 	struct nd_hostservlist **, char *);
126 
127 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
128 
129 thread_t	nfsauth_thread;
130 thread_t	cmd_thread;
131 thread_t	logging_thread;
132 
133 typedef struct logging_data {
134 	char			*ld_host;
135 	char			*ld_path;
136 	char			*ld_rpath;
137 	int			ld_status;
138 	char			*ld_netid;
139 	struct netbuf		*ld_nb;
140 	struct logging_data	*ld_next;
141 } logging_data;
142 
143 static logging_data *logging_head = NULL;
144 static logging_data *logging_tail = NULL;
145 
146 /* ARGSUSED */
147 static void *
148 nfsauth_svc(void *arg)
149 {
150 	int	doorfd = -1;
151 	uint_t	darg;
152 #ifdef DEBUG
153 	int	dfd;
154 #endif
155 
156 	if ((doorfd = door_create(nfsauth_func, NULL,
157 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
158 		syslog(LOG_ERR, "Unable to create door: %m\n");
159 		exit(10);
160 	}
161 
162 #ifdef DEBUG
163 	/*
164 	 * Create a file system path for the door
165 	 */
166 	if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
167 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
168 		syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
169 		(void) close(doorfd);
170 		exit(11);
171 	}
172 
173 	/*
174 	 * Clean up any stale namespace associations
175 	 */
176 	(void) fdetach(MOUNTD_DOOR);
177 
178 	/*
179 	 * Register in namespace to pass to the kernel to door_ki_open
180 	 */
181 	if (fattach(doorfd, MOUNTD_DOOR) == -1) {
182 		syslog(LOG_ERR, "Unable to fattach door: %m\n");
183 		(void) close(dfd);
184 		(void) close(doorfd);
185 		exit(12);
186 	}
187 	(void) close(dfd);
188 #endif
189 
190 	/*
191 	 * Must pass the doorfd down to the kernel.
192 	 */
193 	darg = doorfd;
194 	(void) _nfssys(MOUNTD_ARGS, &darg);
195 
196 	/*
197 	 * Wait for incoming calls
198 	 */
199 	/*CONSTCOND*/
200 	for (;;)
201 		(void) pause();
202 
203 	/*NOTREACHED*/
204 	syslog(LOG_ERR, gettext("Door server exited"));
205 	return (NULL);
206 }
207 
208 /*
209  * NFS command service thread code for setup and handling of the
210  * nfs_cmd requests for character set conversion and other future
211  * events.
212  */
213 
214 static void *
215 cmd_svc(void *arg)
216 {
217 	int	doorfd = -1;
218 	uint_t	darg;
219 
220 	if ((doorfd = door_create(nfscmd_func, NULL,
221 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
222 		syslog(LOG_ERR, "Unable to create cmd door: %m\n");
223 		exit(10);
224 	}
225 
226 	/*
227 	 * Must pass the doorfd down to the kernel.
228 	 */
229 	darg = doorfd;
230 	(void) _nfssys(NFSCMD_ARGS, &darg);
231 
232 	/*
233 	 * Wait for incoming calls
234 	 */
235 	/*CONSTCOND*/
236 	for (;;)
237 		(void) pause();
238 
239 	/*NOTREACHED*/
240 	syslog(LOG_ERR, gettext("Cmd door server exited"));
241 	return (NULL);
242 }
243 
244 static void
245 free_logging_data(logging_data *lq)
246 {
247 	if (lq != NULL) {
248 		free(lq->ld_host);
249 		free(lq->ld_netid);
250 
251 		if (lq->ld_nb != NULL) {
252 			free(lq->ld_nb->buf);
253 			free(lq->ld_nb);
254 		}
255 
256 		free(lq->ld_path);
257 		free(lq->ld_rpath);
258 
259 		free(lq);
260 	}
261 }
262 
263 static logging_data *
264 remove_head_of_queue(void)
265 {
266 	logging_data    *lq;
267 
268 	/*
269 	 * Pull it off the queue.
270 	 */
271 	lq = logging_head;
272 	if (lq) {
273 		logging_head = lq->ld_next;
274 
275 		/*
276 		 * Drained it.
277 		 */
278 		if (logging_head == NULL) {
279 			logging_tail = NULL;
280 		}
281 	}
282 
283 	return (lq);
284 }
285 
286 static void
287 do_logging_queue(logging_data *lq)
288 {
289 	logging_data	*lq_clean = NULL;
290 	int		cleared = 0;
291 	char		*host;
292 
293 	struct nd_hostservlist	*clnames;
294 
295 	while (lq) {
296 		if (lq->ld_host == NULL) {
297 			DTRACE_PROBE(mountd, name_by_lazy);
298 			if (getclientsnames_lazy(lq->ld_netid,
299 			    &lq->ld_nb, &clnames) != 0)
300 				host = NULL;
301 			else
302 				host = clnames->h_hostservs[0].h_host;
303 		} else
304 			host = lq->ld_host;
305 
306 		audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
307 
308 		/* add entry to mount list */
309 		if (lq->ld_rpath)
310 			mntlist_new(host, lq->ld_rpath);
311 
312 		lq->ld_next = lq_clean;
313 		lq_clean = lq;
314 
315 		(void) mutex_lock(&logging_queue_lock);
316 		lq = remove_head_of_queue();
317 		(void) mutex_unlock(&logging_queue_lock);
318 	}
319 
320 	while (lq_clean) {
321 		lq = lq_clean;
322 		lq_clean = lq->ld_next;
323 
324 		free_logging_data(lq);
325 		cleared++;
326 	}
327 
328 	DTRACE_PROBE1(mountd, logging_cleared, cleared);
329 }
330 
331 static void *
332 logging_svc(void *arg)
333 {
334 	logging_data	*lq;
335 
336 	for (;;) {
337 		(void) mutex_lock(&logging_queue_lock);
338 		while (logging_head == NULL) {
339 			(void) cond_wait(&logging_queue_cv,
340 			    &logging_queue_lock);
341 		}
342 
343 		lq = remove_head_of_queue();
344 		(void) mutex_unlock(&logging_queue_lock);
345 
346 		do_logging_queue(lq);
347 	}
348 
349 	/*NOTREACHED*/
350 	syslog(LOG_ERR, gettext("Logging server exited"));
351 	return (NULL);
352 }
353 
354 int
355 main(int argc, char *argv[])
356 {
357 	int	pid;
358 	int	c;
359 	int	rpc_svc_mode = RPC_SVC_MT_AUTO;
360 	int	maxthreads;
361 	int	maxrecsz = RPC_MAXDATASIZE;
362 	bool_t	exclbind = TRUE;
363 	bool_t	can_do_mlp;
364 	long	thr_flags = (THR_NEW_LWP|THR_DAEMON);
365 	char defval[4];
366 	int defvers, ret, bufsz;
367 
368 	int	pipe_fd = -1;
369 
370 	/*
371 	 * Mountd requires uid 0 for:
372 	 *	/etc/rmtab updates (we could chown it to daemon)
373 	 *	/etc/dfs/dfstab reading (it wants to lock out share which
374 	 *		doesn't do any locking before first truncate;
375 	 *		NFS share does; should use fcntl locking instead)
376 	 *	Needed privileges:
377 	 *		auditing
378 	 *		nfs syscall
379 	 *		file dac search (so it can stat all files)
380 	 *	Optional privileges:
381 	 *		MLP
382 	 */
383 	can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
384 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
385 	    PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
386 	    can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
387 		(void) fprintf(stderr,
388 		    "%s: must be run with sufficient privileges\n",
389 		    argv[0]);
390 		exit(1);
391 	}
392 
393 	maxthreads = 0;
394 
395 	while ((c = getopt(argc, argv, "vrm:")) != EOF) {
396 		switch (c) {
397 		case 'v':
398 			verbose++;
399 			break;
400 		case 'r':
401 			rejecting = 1;
402 			break;
403 		case 'm':
404 			maxthreads = atoi(optarg);
405 			if (maxthreads < 1) {
406 				(void) fprintf(stderr,
407 	"%s: must specify positive maximum threads count, using default\n",
408 				    argv[0]);
409 				maxthreads = 0;
410 			}
411 			break;
412 		}
413 	}
414 
415 	/*
416 	 * Read in the NFS version values from config file.
417 	 */
418 	bufsz = 4;
419 	ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
420 	    SCF_TYPE_INTEGER, NFSD, &bufsz);
421 	if (ret == SA_OK) {
422 		errno = 0;
423 		defvers = strtol(defval, (char **)NULL, 10);
424 		if (errno == 0) {
425 			mount_vers_min = defvers;
426 			/*
427 			 * special because NFSv2 is
428 			 * supported by mount v1 & v2
429 			 */
430 			if (defvers == NFS_VERSION)
431 				mount_vers_min = MOUNTVERS;
432 		}
433 	}
434 
435 	bufsz = 4;
436 	ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
437 	    SCF_TYPE_INTEGER, NFSD, &bufsz);
438 	if (ret == SA_OK) {
439 		errno = 0;
440 		defvers = strtol(defval, (char **)NULL, 10);
441 		if (errno == 0) {
442 			mount_vers_max = defvers;
443 		}
444 	}
445 
446 	/*
447 	 * Sanity check versions,
448 	 * even though we may get versions > MOUNTVERS3, we still need
449 	 * to start nfsauth service, so continue on regardless of values.
450 	 */
451 	if (mount_vers_min > mount_vers_max) {
452 		fprintf(stderr, "server_versmin > server_versmax");
453 		mount_vers_max = mount_vers_min;
454 	}
455 	(void) setlocale(LC_ALL, "");
456 	(void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
457 	(void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
458 	(void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
459 	(void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
460 
461 	netgroup_init();
462 
463 #if !defined(TEXT_DOMAIN)
464 #define	TEXT_DOMAIN "SYS_TEST"
465 #endif
466 	(void) textdomain(TEXT_DOMAIN);
467 
468 	/* Don't drop core if the NFS module isn't loaded. */
469 	(void) signal(SIGSYS, SIG_IGN);
470 
471 	pipe_fd = daemonize_init();
472 
473 	/*
474 	 * If we coredump it'll be in /core
475 	 */
476 	if (chdir("/") < 0)
477 		fprintf(stderr, "chdir /: %s", strerror(errno));
478 
479 	openlog("mountd", LOG_PID, LOG_DAEMON);
480 
481 	/*
482 	 * establish our lock on the lock file and write our pid to it.
483 	 * exit if some other process holds the lock, or if there's any
484 	 * error in writing/locking the file.
485 	 */
486 	pid = _enter_daemon_lock(MOUNTD);
487 	switch (pid) {
488 	case 0:
489 		break;
490 	case -1:
491 		fprintf(stderr, "error locking for %s: %s", MOUNTD,
492 		    strerror(errno));
493 		exit(2);
494 	default:
495 		/* daemon was already running */
496 		exit(0);
497 	}
498 
499 	audit_mountd_setup();	/* BSM */
500 
501 	/*
502 	 * Tell RPC that we want automatic thread mode.
503 	 * A new thread will be spawned for each request.
504 	 */
505 	if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
506 		fprintf(stderr, "unable to set automatic MT mode");
507 		exit(1);
508 	}
509 
510 	/*
511 	 * Enable non-blocking mode and maximum record size checks for
512 	 * connection oriented transports.
513 	 */
514 	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
515 		fprintf(stderr, "unable to set RPC max record size");
516 	}
517 
518 	/*
519 	 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
520 	 * from being hijacked by a bind to a more specific addr.
521 	 */
522 	if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
523 		fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND");
524 	}
525 
526 	/*
527 	 * If the -m argument was specified, then set the
528 	 * maximum number of threads to the value specified.
529 	 */
530 	if (maxthreads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &maxthreads)) {
531 		fprintf(stderr, "unable to set maxthreads");
532 		exit(1);
533 	}
534 
535 	/*
536 	 * Make sure to unregister any previous versions in case the
537 	 * user is reconfiguring the server in interesting ways.
538 	 */
539 	svc_unreg(MOUNTPROG, MOUNTVERS);
540 	svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
541 	svc_unreg(MOUNTPROG, MOUNTVERS3);
542 
543 	/*
544 	 * Create the nfsauth thread with same signal disposition
545 	 * as the main thread. We need to create a separate thread
546 	 * since mountd() will be both an RPC server (for remote
547 	 * traffic) _and_ a doors server (for kernel upcalls).
548 	 */
549 	if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
550 		fprintf(stderr, gettext("Failed to create NFSAUTH svc thread"));
551 		exit(2);
552 	}
553 
554 	/*
555 	 * Create the cmd service thread with same signal disposition
556 	 * as the main thread. We need to create a separate thread
557 	 * since mountd() will be both an RPC server (for remote
558 	 * traffic) _and_ a doors server (for kernel upcalls).
559 	 */
560 	if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
561 		syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
562 		exit(2);
563 	}
564 
565 	/*
566 	 * Create an additional thread to service the rmtab and
567 	 * audit_mountd_mount logging for mount requests. Use the same
568 	 * signal disposition as the main thread. We create
569 	 * a separate thread to allow the mount request threads to
570 	 * clear as soon as possible.
571 	 */
572 	if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
573 		syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
574 		exit(2);
575 	}
576 
577 	/*
578 	 * Create datagram and connection oriented services
579 	 */
580 	if (mount_vers_max >= MOUNTVERS) {
581 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
582 			fprintf(stderr,
583 			    "couldn't register datagram_v MOUNTVERS");
584 			exit(1);
585 		}
586 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
587 			fprintf(stderr,
588 			    "couldn't register circuit_v MOUNTVERS");
589 			exit(1);
590 		}
591 	}
592 
593 	if (mount_vers_max >= MOUNTVERS_POSIX) {
594 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
595 		    "datagram_v") == 0) {
596 			fprintf(stderr,
597 			    "couldn't register datagram_v MOUNTVERS_POSIX");
598 			exit(1);
599 		}
600 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
601 		    "circuit_v") == 0) {
602 			fprintf(stderr,
603 			    "couldn't register circuit_v MOUNTVERS_POSIX");
604 			exit(1);
605 		}
606 	}
607 
608 	if (mount_vers_max >= MOUNTVERS3) {
609 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
610 			fprintf(stderr,
611 			    "couldn't register datagram_v MOUNTVERS3");
612 			exit(1);
613 		}
614 		if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
615 			fprintf(stderr,
616 			    "couldn't register circuit_v MOUNTVERS3");
617 			exit(1);
618 		}
619 	}
620 
621 	/*
622 	 * Start serving
623 	 */
624 	rmtab_load();
625 
626 	daemonize_fini(pipe_fd);
627 
628 	/* Get rid of the most dangerous basic privileges. */
629 	__fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
630 	    (char *)NULL);
631 
632 	svc_run();
633 	syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
634 	abort();
635 
636 	/* NOTREACHED */
637 	return (0);
638 }
639 
640 /*
641  * Server procedure switch routine
642  */
643 void
644 mnt(struct svc_req *rqstp, SVCXPRT *transp)
645 {
646 	switch (rqstp->rq_proc) {
647 	case NULLPROC:
648 		errno = 0;
649 		if (!svc_sendreply(transp, xdr_void, (char *)0))
650 			log_cant_reply(transp);
651 		return;
652 
653 	case MOUNTPROC_MNT:
654 		(void) mount(rqstp);
655 		return;
656 
657 	case MOUNTPROC_DUMP:
658 		mntlist_send(transp);
659 		return;
660 
661 	case MOUNTPROC_UMNT:
662 		umount(rqstp);
663 		return;
664 
665 	case MOUNTPROC_UMNTALL:
666 		umountall(rqstp);
667 		return;
668 
669 	case MOUNTPROC_EXPORT:
670 	case MOUNTPROC_EXPORTALL:
671 		export(rqstp);
672 		return;
673 
674 	case MOUNTPROC_PATHCONF:
675 		if (rqstp->rq_vers == MOUNTVERS_POSIX)
676 			mnt_pathconf(rqstp);
677 		else
678 			svcerr_noproc(transp);
679 		return;
680 
681 	default:
682 		svcerr_noproc(transp);
683 		return;
684 	}
685 }
686 
687 /* Set up anonymous client */
688 
689 struct nd_hostservlist *
690 anon_client(char *host)
691 {
692 	struct nd_hostservlist *anon_hsl;
693 	struct nd_hostserv *anon_hs;
694 
695 	anon_hsl = malloc(sizeof (*anon_hsl));
696 	if (anon_hsl == NULL)
697 		return (NULL);
698 
699 	anon_hs = malloc(sizeof (*anon_hs));
700 	if (anon_hs == NULL) {
701 		free(anon_hsl);
702 		return (NULL);
703 	}
704 
705 	if (host == NULL)
706 		anon_hs->h_host = strdup("(anon)");
707 	else
708 		anon_hs->h_host = strdup(host);
709 
710 	if (anon_hs->h_host == NULL) {
711 		free(anon_hs);
712 		free(anon_hsl);
713 		return (NULL);
714 	}
715 	anon_hs->h_serv = '\0';
716 
717 	anon_hsl->h_cnt = 1;
718 	anon_hsl->h_hostservs = anon_hs;
719 
720 	return (anon_hsl);
721 }
722 
723 static int
724 getclientsnames_common(struct netconfig *nconf, struct netbuf **nbuf,
725     struct nd_hostservlist **serv)
726 {
727 	char host[MAXIPADDRLEN];
728 
729 	assert(*nbuf != NULL);
730 
731 	/*
732 	 * Use the this API instead of the netdir_getbyaddr()
733 	 * to avoid service lookup.
734 	 */
735 	if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf) != 0) {
736 		if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
737 			struct sockaddr_in *sa;
738 
739 			/* LINTED pointer alignment */
740 			sa = (struct sockaddr_in *)((*nbuf)->buf);
741 			(void) inet_ntoa_r(sa->sin_addr, host);
742 		} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
743 			struct sockaddr_in6 *sa;
744 
745 			/* LINTED pointer alignment */
746 			sa = (struct sockaddr_in6 *)((*nbuf)->buf);
747 			(void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr,
748 			    host, INET6_ADDRSTRLEN);
749 		} else {
750 			syslog(LOG_ERR, gettext(
751 			    "Client's address is neither IPv4 nor IPv6"));
752 			return (EINVAL);
753 		}
754 
755 		*serv = anon_client(host);
756 		if (*serv == NULL)
757 			return (ENOMEM);
758 	}
759 
760 	assert(*serv != NULL);
761 	return (0);
762 }
763 
764 /*
765  * Get the client's hostname from the copy of the
766  * relevant transport handle parts.
767  * If the name is not available then return "(anon)".
768  */
769 static int
770 getclientsnames_lazy(char *netid, struct netbuf **nbuf,
771     struct nd_hostservlist **serv)
772 {
773 	struct netconfig *nconf;
774 	int	rc;
775 
776 	nconf = getnetconfigent(netid);
777 	if (nconf == NULL) {
778 		syslog(LOG_ERR, "%s: getnetconfigent failed", netid);
779 		*serv = anon_client(NULL);
780 		if (*serv == NULL)
781 			return (ENOMEM);
782 		return (0);
783 	}
784 
785 	rc = getclientsnames_common(nconf, nbuf, serv);
786 	freenetconfigent(nconf);
787 	return (rc);
788 }
789 
790 /*
791  * Get the client's hostname from the transport handle.
792  * If the name is not available then return "(anon)".
793  */
794 int
795 getclientsnames(SVCXPRT *transp, struct netbuf **nbuf,
796     struct nd_hostservlist **serv)
797 {
798 	struct netconfig *nconf;
799 	int	rc;
800 
801 	nconf = getnetconfigent(transp->xp_netid);
802 	if (nconf == NULL) {
803 		syslog(LOG_ERR, "%s: getnetconfigent failed",
804 		    transp->xp_netid);
805 		*serv = anon_client(NULL);
806 		if (*serv == NULL)
807 			return (ENOMEM);
808 		return (0);
809 	}
810 
811 	*nbuf = svc_getrpccaller(transp);
812 	if (*nbuf == NULL) {
813 		freenetconfigent(nconf);
814 		*serv = anon_client(NULL);
815 		if (*serv == NULL)
816 			return (ENOMEM);
817 		return (0);
818 	}
819 
820 	rc = getclientsnames_common(nconf, nbuf, serv);
821 	freenetconfigent(nconf);
822 	return (rc);
823 }
824 
825 void
826 log_cant_reply(SVCXPRT *transp)
827 {
828 	int saverrno;
829 	struct nd_hostservlist *clnames = NULL;
830 	register char *host;
831 	struct netbuf *nb;
832 
833 	saverrno = errno;	/* save error code */
834 	if (getclientsnames(transp, &nb, &clnames) != 0)
835 		return;
836 	host = clnames->h_hostservs->h_host;
837 
838 	errno = saverrno;
839 	if (errno == 0)
840 		syslog(LOG_ERR, "couldn't send reply to %s", host);
841 	else
842 		syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
843 
844 	netdir_free(clnames, ND_HOSTSERVLIST);
845 }
846 
847 /*
848  * Answer pathconf questions for the mount point fs
849  */
850 static void
851 mnt_pathconf(struct svc_req *rqstp)
852 {
853 	SVCXPRT *transp;
854 	struct pathcnf p;
855 	char *path, rpath[MAXPATHLEN];
856 	struct stat st;
857 
858 	transp = rqstp->rq_xprt;
859 	path = NULL;
860 	(void) memset((caddr_t)&p, 0, sizeof (p));
861 
862 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
863 		svcerr_decode(transp);
864 		return;
865 	}
866 	if (lstat(path, &st) < 0) {
867 		_PC_SET(_PC_ERROR, p.pc_mask);
868 		goto done;
869 	}
870 	/*
871 	 * Get a path without symbolic links.
872 	 */
873 	if (realpath(path, rpath) == NULL) {
874 		syslog(LOG_DEBUG,
875 		    "mount request: realpath failed on %s: %m",
876 		    path);
877 		_PC_SET(_PC_ERROR, p.pc_mask);
878 		goto done;
879 	}
880 	(void) memset((caddr_t)&p, 0, sizeof (p));
881 	/*
882 	 * can't ask about devices over NFS
883 	 */
884 	_PC_SET(_PC_MAX_CANON, p.pc_mask);
885 	_PC_SET(_PC_MAX_INPUT, p.pc_mask);
886 	_PC_SET(_PC_PIPE_BUF, p.pc_mask);
887 	_PC_SET(_PC_VDISABLE, p.pc_mask);
888 
889 	errno = 0;
890 	p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
891 	if (errno)
892 		_PC_SET(_PC_LINK_MAX, p.pc_mask);
893 	p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
894 	if (errno)
895 		_PC_SET(_PC_NAME_MAX, p.pc_mask);
896 	p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
897 	if (errno)
898 		_PC_SET(_PC_PATH_MAX, p.pc_mask);
899 	if (pathconf(rpath, _PC_NO_TRUNC) == 1)
900 		_PC_SET(_PC_NO_TRUNC, p.pc_mask);
901 	if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
902 		_PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
903 
904 done:
905 	errno = 0;
906 	if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
907 		log_cant_reply(transp);
908 	if (path != NULL)
909 		svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
910 }
911 
912 /*
913  * If the rootmount (export) option is specified, the all mount requests for
914  * subdirectories return EACCES.
915  */
916 static int
917 checkrootmount(share_t *sh, char *rpath)
918 {
919 	char *val;
920 
921 	if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
922 		free(val);
923 		if (strcmp(sh->sh_path, rpath) != 0)
924 			return (0);
925 		else
926 			return (1);
927 	} else
928 		return (1);
929 }
930 
931 #define	MAX_FLAVORS	128
932 
933 /*
934  * Return only EACCES if client does not have access
935  *  to this directory.
936  * "If the server exports only /a/b, an attempt to
937  *  mount a/b/c will fail with ENOENT if the directory
938  *  does not exist"... However, if the client
939  *  does not have access to /a/b, an attacker can
940  *  determine whether the directory exists.
941  * This routine checks either existence of the file or
942  * existence of the file name entry in the mount table.
943  * If the file exists and there is no file name entry,
944  * the error returned should be EACCES.
945  * If the file does not exist, it must be determined
946  * whether the client has access to a parent
947  * directory.  If the client has access to a parent
948  * directory, the error returned should be ENOENT,
949  * otherwise EACCES.
950  */
951 static int
952 mount_enoent_error(SVCXPRT *transp, char *path, char *rpath,
953     struct nd_hostservlist **clnames, struct netbuf **nb, int *flavor_list)
954 {
955 	char *checkpath, *dp;
956 	share_t *sh = NULL;
957 	int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
958 	int flavor_count;
959 
960 	checkpath = strdup(path);
961 	if (checkpath == NULL) {
962 		syslog(LOG_ERR, "mount_enoent: no memory");
963 		return (EACCES);
964 	}
965 
966 	/* CONSTCOND */
967 	while (1) {
968 		if (sh) {
969 			sharefree(sh);
970 			sh = NULL;
971 		}
972 
973 		if ((sh = findentry(rpath)) == NULL &&
974 		    (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
975 			/*
976 			 * There is no file name entry.
977 			 * If the file (with symbolic links resolved) exists,
978 			 * the error returned should be EACCES.
979 			 */
980 			if (realpath_error == 0)
981 				break;
982 		} else if (checkrootmount(sh, rpath) == 0) {
983 			/*
984 			 * This is a "nosub" only export, in which case,
985 			 * mounting subdirectories isn't allowed.
986 			 * If the file (with symbolic links resolved) exists,
987 			 * the error returned should be EACCES.
988 			 */
989 			if (realpath_error == 0)
990 				break;
991 		} else {
992 			/*
993 			 * Check permissions in mount table.
994 			 */
995 			if (newopts(sh->sh_opts))
996 				flavor_count = getclientsflavors_new(sh,
997 				    transp, nb, clnames, flavor_list);
998 			else
999 				flavor_count = getclientsflavors_old(sh,
1000 				    transp, nb, clnames, flavor_list);
1001 			if (flavor_count != 0) {
1002 				/*
1003 				 * Found entry in table and
1004 				 * client has correct permissions.
1005 				 */
1006 				reply_error = ENOENT;
1007 				break;
1008 			}
1009 		}
1010 
1011 		/*
1012 		 * Check all parent directories.
1013 		 */
1014 		dp = strrchr(checkpath, '/');
1015 		if (dp == NULL)
1016 			break;
1017 		*dp = '\0';
1018 		if (strlen(checkpath) == 0)
1019 			break;
1020 		/*
1021 		 * Get the real path (no symbolic links in it)
1022 		 */
1023 		if (realpath(checkpath, rpath) == NULL) {
1024 			if (errno != ENOENT)
1025 				break;
1026 		} else {
1027 			realpath_error = 0;
1028 		}
1029 	}
1030 
1031 	if (sh)
1032 		sharefree(sh);
1033 	free(checkpath);
1034 	return (reply_error);
1035 }
1036 
1037 /*
1038  * We need to inform the caller whether or not we were
1039  * able to add a node to the queue. If we are not, then
1040  * it is up to the caller to go ahead and log the data.
1041  */
1042 static int
1043 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
1044     char *rpath, int status, int error)
1045 {
1046 	logging_data	*lq;
1047 	struct netbuf	*nb;
1048 
1049 	lq = (logging_data *)calloc(1, sizeof (logging_data));
1050 	if (lq == NULL)
1051 		goto cleanup;
1052 
1053 	/*
1054 	 * We might not yet have the host...
1055 	 */
1056 	if (host) {
1057 		DTRACE_PROBE1(mountd, log_host, host);
1058 		lq->ld_host = strdup(host);
1059 		if (lq->ld_host == NULL)
1060 			goto cleanup;
1061 	} else {
1062 		DTRACE_PROBE(mountd, log_no_host);
1063 
1064 		lq->ld_netid = strdup(transp->xp_netid);
1065 		if (lq->ld_netid == NULL)
1066 			goto cleanup;
1067 
1068 		lq->ld_nb = calloc(1, sizeof (struct netbuf));
1069 		if (lq->ld_nb == NULL)
1070 			goto cleanup;
1071 
1072 		nb = svc_getrpccaller(transp);
1073 		if (nb == NULL) {
1074 			DTRACE_PROBE(mountd, e__nb__enqueue);
1075 			goto cleanup;
1076 		}
1077 
1078 		DTRACE_PROBE(mountd, nb_set_enqueue);
1079 
1080 		lq->ld_nb->maxlen = nb->maxlen;
1081 		lq->ld_nb->len = nb->len;
1082 
1083 		lq->ld_nb->buf = malloc(lq->ld_nb->len);
1084 		if (lq->ld_nb->buf == NULL)
1085 			goto cleanup;
1086 
1087 		bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1088 	}
1089 
1090 	lq->ld_path = strdup(path);
1091 	if (lq->ld_path == NULL)
1092 		goto cleanup;
1093 
1094 	if (!error) {
1095 		lq->ld_rpath = strdup(rpath);
1096 		if (lq->ld_rpath == NULL)
1097 			goto cleanup;
1098 	}
1099 
1100 	lq->ld_status = status;
1101 
1102 	/*
1103 	 * Add to the tail of the logging queue.
1104 	 */
1105 	(void) mutex_lock(&logging_queue_lock);
1106 	if (logging_tail == NULL) {
1107 		logging_tail = logging_head = lq;
1108 	} else {
1109 		logging_tail->ld_next = lq;
1110 		logging_tail = lq;
1111 	}
1112 	(void) cond_signal(&logging_queue_cv);
1113 	(void) mutex_unlock(&logging_queue_lock);
1114 
1115 	return (TRUE);
1116 
1117 cleanup:
1118 
1119 	free_logging_data(lq);
1120 
1121 	return (FALSE);
1122 }
1123 
1124 /*
1125  * Check mount requests, add to mounted list if ok
1126  */
1127 static int
1128 mount(struct svc_req *rqstp)
1129 {
1130 	SVCXPRT *transp;
1131 	int version, vers;
1132 	struct fhstatus fhs;
1133 	struct mountres3 mountres3;
1134 	char fh[FHSIZE3];
1135 	int len = FHSIZE3;
1136 	char *path, rpath[MAXPATHLEN];
1137 	share_t *sh = NULL;
1138 	struct nd_hostservlist *clnames = NULL;
1139 	char *host = NULL;
1140 	int error = 0, lofs_tried = 0, enqueued;
1141 	int flavor_list[MAX_FLAVORS];
1142 	int flavor_count;
1143 	struct netbuf *nb = NULL;
1144 	ucred_t	*uc = NULL;
1145 
1146 	int audit_status;
1147 
1148 	transp = rqstp->rq_xprt;
1149 	version = rqstp->rq_vers;
1150 	path = NULL;
1151 
1152 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1153 		svcerr_decode(transp);
1154 		return (EACCES);
1155 	}
1156 
1157 	/*
1158 	 * Put off getting the name for the client until we
1159 	 * need it. This is a performance gain. If we are logging,
1160 	 * then we don't care about performance and might as well
1161 	 * get the host name now in case we need to spit out an
1162 	 * error message.
1163 	 */
1164 	if (verbose) {
1165 		DTRACE_PROBE(mountd, name_by_verbose);
1166 		if (getclientsnames(transp, &nb, &clnames) != 0) {
1167 			/*
1168 			 * We failed to get a name for the client, even
1169 			 * 'anon', probably because we ran out of memory.
1170 			 * In this situation it doesn't make sense to
1171 			 * allow the mount to succeed.
1172 			 */
1173 			error = EACCES;
1174 			goto reply;
1175 		}
1176 		host = clnames->h_hostservs[0].h_host;
1177 	}
1178 
1179 	/*
1180 	 * If the version being used is less than the minimum version,
1181 	 * the filehandle translation should not be provided to the
1182 	 * client.
1183 	 */
1184 	if (rejecting || version < mount_vers_min) {
1185 		if (verbose)
1186 			syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1187 			    host, path);
1188 		error = EACCES;
1189 		goto reply;
1190 	}
1191 
1192 	/*
1193 	 * Trusted Extension doesn't support nfsv2. nfsv2 client
1194 	 * uses MOUNT protocol v1 and v2. To prevent circumventing
1195 	 * TX label policy via using nfsv2 client, reject a mount
1196 	 * request with version less than 3 and log an error.
1197 	 */
1198 	if (is_system_labeled()) {
1199 		if (version < 3) {
1200 			if (verbose)
1201 				syslog(LOG_ERR,
1202 				    "Rejected mount: TX doesn't support NFSv2");
1203 			error = EACCES;
1204 			goto reply;
1205 		}
1206 	}
1207 
1208 	/*
1209 	 * Get the real path (no symbolic links in it)
1210 	 */
1211 	if (realpath(path, rpath) == NULL) {
1212 		error = errno;
1213 		if (verbose)
1214 			syslog(LOG_ERR,
1215 			    "mount request: realpath: %s: %m", path);
1216 		if (error == ENOENT)
1217 			error = mount_enoent_error(transp, path, rpath,
1218 			    &clnames, &nb, flavor_list);
1219 		goto reply;
1220 	}
1221 
1222 	if ((sh = findentry(rpath)) == NULL &&
1223 	    (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1224 		error = EACCES;
1225 		goto reply;
1226 	}
1227 
1228 	/*
1229 	 * Check if this is a "nosub" only export, in which case, mounting
1230 	 * subdirectories isn't allowed. Bug 1184573.
1231 	 */
1232 	if (checkrootmount(sh, rpath) == 0) {
1233 		error = EACCES;
1234 		goto reply;
1235 	}
1236 
1237 	if (newopts(sh->sh_opts))
1238 		flavor_count = getclientsflavors_new(sh, transp, &nb, &clnames,
1239 		    flavor_list);
1240 	else
1241 		flavor_count = getclientsflavors_old(sh, transp, &nb, &clnames,
1242 		    flavor_list);
1243 
1244 	if (clnames)
1245 		host = clnames->h_hostservs[0].h_host;
1246 
1247 	if (flavor_count == 0) {
1248 		error = EACCES;
1249 		goto reply;
1250 	}
1251 
1252 	/*
1253 	 * Check MAC policy here. The server side policy should be
1254 	 * consistent with client side mount policy, i.e.
1255 	 * - we disallow an admin_low unlabeled client to mount
1256 	 * - we disallow mount from a lower labeled client.
1257 	 */
1258 	if (is_system_labeled()) {
1259 		m_label_t *clabel = NULL;
1260 		m_label_t *slabel = NULL;
1261 		m_label_t admin_low;
1262 
1263 		if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1264 			syslog(LOG_ERR,
1265 			    "mount request: Failed to get caller's ucred : %m");
1266 			error = EACCES;
1267 			goto reply;
1268 		}
1269 		if ((clabel = ucred_getlabel(uc)) == NULL) {
1270 			syslog(LOG_ERR,
1271 			    "mount request: can't get client label from ucred");
1272 			error = EACCES;
1273 			goto reply;
1274 		}
1275 
1276 		bsllow(&admin_low);
1277 		if (blequal(&admin_low, clabel)) {
1278 			struct sockaddr *ca;
1279 			tsol_tpent_t	*tp;
1280 
1281 			ca = (struct sockaddr *)(void *)svc_getrpccaller(
1282 			    rqstp->rq_xprt)->buf;
1283 			if (ca == NULL) {
1284 				error = EACCES;
1285 				goto reply;
1286 			}
1287 			/*
1288 			 * get trusted network template associated
1289 			 * with the client.
1290 			 */
1291 			tp = get_client_template(ca);
1292 			if (tp == NULL || tp->host_type != SUN_CIPSO) {
1293 				if (tp != NULL)
1294 					tsol_freetpent(tp);
1295 				error = EACCES;
1296 				goto reply;
1297 			}
1298 			tsol_freetpent(tp);
1299 		} else {
1300 			if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1301 				error = EACCES;
1302 				goto reply;
1303 			}
1304 
1305 			if (getlabel(rpath, slabel) != 0) {
1306 				m_label_free(slabel);
1307 				error = EACCES;
1308 				goto reply;
1309 			}
1310 
1311 			if (!bldominates(clabel, slabel)) {
1312 				m_label_free(slabel);
1313 				error = EACCES;
1314 				goto reply;
1315 			}
1316 			m_label_free(slabel);
1317 		}
1318 	}
1319 
1320 	/*
1321 	 * Now get the filehandle.
1322 	 *
1323 	 * NFS V2 clients get a 32 byte filehandle.
1324 	 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1325 	 * the embedded FIDs.
1326 	 */
1327 	vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1328 
1329 	/* LINTED pointer alignment */
1330 	while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1331 		if (errno == EINVAL &&
1332 		    (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1333 			errno = 0;
1334 			continue;
1335 		}
1336 		error = errno == EINVAL ? EACCES : errno;
1337 		syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1338 		    path);
1339 		break;
1340 	}
1341 
1342 	if (version == MOUNTVERS3) {
1343 		mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1344 		mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1345 	} else {
1346 		bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1347 	}
1348 
1349 reply:
1350 	if (uc != NULL)
1351 		ucred_free(uc);
1352 
1353 	switch (version) {
1354 	case MOUNTVERS:
1355 	case MOUNTVERS_POSIX:
1356 		if (error == EINVAL)
1357 			fhs.fhs_status = NFSERR_ACCES;
1358 		else if (error == EREMOTE)
1359 			fhs.fhs_status = NFSERR_REMOTE;
1360 		else
1361 			fhs.fhs_status = error;
1362 
1363 		if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1364 			log_cant_reply(transp);
1365 
1366 		audit_status = fhs.fhs_status;
1367 		break;
1368 
1369 	case MOUNTVERS3:
1370 		if (!error) {
1371 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1372 		    flavor_list;
1373 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1374 		    flavor_count;
1375 
1376 		} else if (error == ENAMETOOLONG)
1377 			error = MNT3ERR_NAMETOOLONG;
1378 
1379 		mountres3.fhs_status = error;
1380 		if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1381 			log_cant_reply(transp);
1382 
1383 		audit_status = mountres3.fhs_status;
1384 		break;
1385 	}
1386 
1387 	if (verbose)
1388 		syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1389 		    (host == NULL) ? "unknown host" : host,
1390 		    error ? "denied" : "mounted", path);
1391 
1392 	/*
1393 	 * If we can not create a queue entry, go ahead and do it
1394 	 * in the context of this thread.
1395 	 */
1396 	enqueued = enqueue_logging_data(host, transp, path, rpath,
1397 	    audit_status, error);
1398 	if (enqueued == FALSE) {
1399 		if (host == NULL) {
1400 			DTRACE_PROBE(mountd, name_by_in_thread);
1401 			if (getclientsnames(transp, &nb, &clnames) == 0)
1402 				host = clnames->h_hostservs[0].h_host;
1403 		}
1404 
1405 		DTRACE_PROBE(mountd, logged_in_thread);
1406 		audit_mountd_mount(host, path, audit_status); /* BSM */
1407 		if (!error)
1408 			mntlist_new(host, rpath); /* add entry to mount list */
1409 	}
1410 
1411 	if (path != NULL)
1412 		svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1413 
1414 done:
1415 	if (sh)
1416 		sharefree(sh);
1417 	netdir_free(clnames, ND_HOSTSERVLIST);
1418 
1419 	return (error);
1420 }
1421 
1422 /*
1423  * Determine whether two paths are within the same file system.
1424  * Returns nonzero (true) if paths are the same, zero (false) if
1425  * they are different.  If an error occurs, return false.
1426  *
1427  * Use the actual FSID if it's available (via getattrat()); otherwise,
1428  * fall back on st_dev.
1429  *
1430  * With ZFS snapshots, st_dev differs from the regular file system
1431  * versus the snapshot.  But the fsid is the same throughout.  Thus
1432  * the fsid is a better test.
1433  */
1434 static int
1435 same_file_system(const char *path1, const char *path2)
1436 {
1437 	uint64_t fsid1, fsid2;
1438 	struct stat64 st1, st2;
1439 	nvlist_t *nvl1 = NULL;
1440 	nvlist_t *nvl2 = NULL;
1441 
1442 	if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1443 	    (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1444 	    (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1445 	    (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1446 		nvlist_free(nvl1);
1447 		nvlist_free(nvl2);
1448 		/*
1449 		 * We have found fsid's for both paths.
1450 		 */
1451 
1452 		if (fsid1 == fsid2)
1453 			return (B_TRUE);
1454 
1455 		return (B_FALSE);
1456 	}
1457 
1458 	if (nvl1 != NULL)
1459 		nvlist_free(nvl1);
1460 	if (nvl2 != NULL)
1461 		nvlist_free(nvl2);
1462 
1463 	/*
1464 	 * We were unable to find fsid's for at least one of the paths.
1465 	 * fall back on st_dev.
1466 	 */
1467 
1468 	if (stat64(path1, &st1) < 0) {
1469 		syslog(LOG_NOTICE, "%s: %m", path1);
1470 		return (B_FALSE);
1471 	}
1472 	if (stat64(path2, &st2) < 0) {
1473 		syslog(LOG_NOTICE, "%s: %m", path2);
1474 		return (B_FALSE);
1475 	}
1476 
1477 	if (st1.st_dev == st2.st_dev)
1478 		return (B_TRUE);
1479 
1480 	return (B_FALSE);
1481 }
1482 
1483 share_t *
1484 findentry(char *path)
1485 {
1486 	share_t *sh = NULL;
1487 	struct sh_list *shp;
1488 	register char *p1, *p2;
1489 
1490 	check_sharetab();
1491 
1492 	(void) rw_rdlock(&sharetab_lock);
1493 
1494 	for (shp = share_list; shp; shp = shp->shl_next) {
1495 		sh = shp->shl_sh;
1496 		for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1497 			if (*p1 == '\0')
1498 				goto done;	/* exact match */
1499 
1500 		/*
1501 		 * Now compare the pathnames for three cases:
1502 		 *
1503 		 * Parent: /export/foo		(no trailing slash on parent)
1504 		 * Child:  /export/foo/bar
1505 		 *
1506 		 * Parent: /export/foo/		(trailing slash on parent)
1507 		 * Child:  /export/foo/bar
1508 		 *
1509 		 * Parent: /export/foo/		(no trailing slash on child)
1510 		 * Child:  /export/foo
1511 		 */
1512 		if ((*p1 == '\0' && *p2 == '/') ||
1513 		    (*p1 == '\0' && *(p1-1) == '/') ||
1514 		    (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1515 			/*
1516 			 * We have a subdirectory.  Test whether the
1517 			 * subdirectory is in the same file system.
1518 			 */
1519 			if (same_file_system(path, sh->sh_path))
1520 				goto done;
1521 		}
1522 	}
1523 done:
1524 	sh = shp ? sharedup(sh) : NULL;
1525 
1526 	(void) rw_unlock(&sharetab_lock);
1527 
1528 	return (sh);
1529 }
1530 
1531 
1532 static int
1533 is_substring(char **mntp, char **path)
1534 {
1535 	char *p1 = *mntp, *p2 = *path;
1536 
1537 	if (*p1 == '\0' && *p2 == '\0') /* exact match */
1538 		return (1);
1539 	else if (*p1 == '\0' && *p2 == '/')
1540 		return (1);
1541 	else if (*p1 == '\0' && *(p1-1) == '/') {
1542 		*path = --p2; /* we need the slash in p2 */
1543 		return (1);
1544 	} else if (*p2 == '\0') {
1545 		while (*p1 == '/')
1546 			p1++;
1547 		if (*p1 == '\0') /* exact match */
1548 			return (1);
1549 	}
1550 	return (0);
1551 }
1552 
1553 /*
1554  * find_lofsentry() searches for the real path which this requested LOFS path
1555  * (rpath) shadows. If found, it will return the sharetab entry of
1556  * the real path that corresponds to the LOFS path.
1557  * We first search mnttab to see if the requested path is an automounted
1558  * path. If it is an automounted path, it will trigger the mount by stat()ing
1559  * the requested path. Note that it is important to check that this path is
1560  * actually an automounted path, otherwise we would stat() a path which may
1561  * turn out to be NFS and block indefinitely on a dead server. The automounter
1562  * times-out if the server is dead, so there's no risk of hanging this
1563  * thread waiting for stat().
1564  * After the mount has been triggered (if necessary), we look for a
1565  * mountpoint of type LOFS (by searching /etc/mnttab again) which
1566  * is a substring of the rpath. If found, we construct a new path by
1567  * concatenating the mnt_special and the remaining of rpath, call findentry()
1568  * to make sure the 'real path' is shared.
1569  */
1570 static share_t *
1571 find_lofsentry(char *rpath, int *done_flag)
1572 {
1573 	struct stat r_stbuf;
1574 	mntlist_t *ml, *mntl, *mntpnt = NULL;
1575 	share_t *retcode = NULL;
1576 	char tmp_path[MAXPATHLEN];
1577 	int mntpnt_len = 0, tmp;
1578 	char *p1, *p2;
1579 
1580 	if ((*done_flag)++)
1581 		return (retcode);
1582 
1583 	/*
1584 	 * While fsgetmntlist() uses lockf() to
1585 	 * lock the mnttab before reading it in,
1586 	 * the lock ignores threads in the same process.
1587 	 * Read in the mnttab with the protection of a mutex.
1588 	 */
1589 	(void) mutex_lock(&mnttab_lock);
1590 	mntl = fsgetmntlist();
1591 	(void) mutex_unlock(&mnttab_lock);
1592 
1593 	/*
1594 	 * Obtain the mountpoint for the requested path.
1595 	 */
1596 	for (ml = mntl; ml; ml = ml->mntl_next) {
1597 		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1598 		    *p1 == *p2 && *p1; p1++, p2++)
1599 			;
1600 		if (is_substring(&p1, &p2) &&
1601 		    (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1602 			mntpnt = ml;
1603 			mntpnt_len = tmp;
1604 		}
1605 	}
1606 
1607 	/*
1608 	 * If the path needs to be autoFS mounted, trigger the mount by
1609 	 * stat()ing it. This is determined by checking whether the
1610 	 * mountpoint we just found is of type autofs.
1611 	 */
1612 	if (mntpnt != NULL &&
1613 	    strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1614 		/*
1615 		 * The requested path is a substring of an autoFS filesystem.
1616 		 * Trigger the mount.
1617 		 */
1618 		if (stat(rpath, &r_stbuf) < 0) {
1619 			if (verbose)
1620 				syslog(LOG_NOTICE, "%s: %m", rpath);
1621 			goto done;
1622 		}
1623 		if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1624 			/*
1625 			 * The requested path is a directory, stat(2) it
1626 			 * again with a trailing '.' to force the autoFS
1627 			 * module to trigger the mount of indirect
1628 			 * automount entries, such as /net/jurassic/.
1629 			 */
1630 			if (strlen(rpath) + 2 > MAXPATHLEN) {
1631 				if (verbose) {
1632 					syslog(LOG_NOTICE,
1633 					    "%s/.: exceeds MAXPATHLEN %d",
1634 					    rpath, MAXPATHLEN);
1635 				}
1636 				goto done;
1637 			}
1638 			(void) strcpy(tmp_path, rpath);
1639 			(void) strcat(tmp_path, "/.");
1640 
1641 			if (stat(tmp_path, &r_stbuf) < 0) {
1642 				if (verbose)
1643 					syslog(LOG_NOTICE, "%s: %m", tmp_path);
1644 				goto done;
1645 			}
1646 		}
1647 
1648 		/*
1649 		 * The mount has been triggered, re-read mnttab to pick up
1650 		 * the changes made by autoFS.
1651 		 */
1652 		fsfreemntlist(mntl);
1653 		(void) mutex_lock(&mnttab_lock);
1654 		mntl = fsgetmntlist();
1655 		(void) mutex_unlock(&mnttab_lock);
1656 	}
1657 
1658 	/*
1659 	 * The autoFS mountpoint has been triggered if necessary,
1660 	 * now search mnttab again to determine if the requested path
1661 	 * is an LOFS mount of a shared path.
1662 	 */
1663 	mntpnt_len = 0;
1664 	for (ml = mntl; ml; ml = ml->mntl_next) {
1665 		if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1666 			continue;
1667 
1668 		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1669 		    *p1 == *p2 && *p1; p1++, p2++)
1670 			;
1671 
1672 		if (is_substring(&p1, &p2) &&
1673 		    ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1674 			mntpnt_len = tmp;
1675 
1676 			if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1677 			    MAXPATHLEN) {
1678 				if (verbose) {
1679 					syslog(LOG_NOTICE, "%s%s: exceeds %d",
1680 					    ml->mntl_mnt->mnt_special, p2,
1681 					    MAXPATHLEN);
1682 				}
1683 				if (retcode)
1684 					sharefree(retcode);
1685 				retcode = NULL;
1686 				goto done;
1687 			}
1688 
1689 			(void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1690 			(void) strcat(tmp_path, p2);
1691 			if (retcode)
1692 				sharefree(retcode);
1693 			retcode = findentry(tmp_path);
1694 		}
1695 	}
1696 
1697 	if (retcode) {
1698 		assert(strlen(tmp_path) > 0);
1699 		(void) strcpy(rpath, tmp_path);
1700 	}
1701 
1702 done:
1703 	fsfreemntlist(mntl);
1704 	return (retcode);
1705 }
1706 
1707 /*
1708  * Determine whether an access list grants rights to a particular host.
1709  * We match on aliases of the hostname as well as on the canonical name.
1710  * Names in the access list may be either hosts or netgroups;  they're
1711  * not distinguished syntactically.  We check for hosts first because
1712  * it's cheaper, then try netgroups.
1713  *
1714  * If pnb and pclnames are NULL, it means that we have to use transp
1715  * to resolve client IP address to hostname. If they aren't NULL
1716  * then transp argument won't be used and can be NULL.
1717  */
1718 int
1719 in_access_list(SVCXPRT *transp, struct netbuf **pnb,
1720     struct nd_hostservlist **pclnames,
1721     char *access_list)	/* N.B. we clobber this "input" parameter */
1722 {
1723 	char addr[INET_ADDRSTRLEN];
1724 	char buff[256];
1725 	int nentries = 0;
1726 	char *cstr = access_list;
1727 	char *gr = access_list;
1728 	char *host;
1729 	int off;
1730 	int i;
1731 	int response;
1732 	int sbr = 0;
1733 	struct nd_hostservlist *clnames;
1734 	struct netent n, *np;
1735 
1736 	/* If no access list - then it's unrestricted */
1737 	if (access_list == NULL || *access_list == '\0')
1738 		return (1);
1739 
1740 	assert(transp != NULL || (*pnb != NULL && *pclnames != NULL));
1741 
1742 	/* Get client address if it wasn't provided */
1743 	if (*pnb == NULL)
1744 		/* Don't grant access if client address isn't known */
1745 		if ((*pnb = svc_getrpccaller(transp)) == NULL)
1746 			return (0);
1747 
1748 	/* Try to lookup client hostname if it wasn't provided */
1749 	if (*pclnames == NULL)
1750 		getclientsnames(transp, pnb, pclnames);
1751 	clnames = *pclnames;
1752 
1753 	for (;;) {
1754 		if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
1755 			switch (*cstr) {
1756 			case '[':
1757 			case ']':
1758 				sbr = !sbr;
1759 				cstr++;
1760 				continue;
1761 			case ':':
1762 				if (sbr) {
1763 					cstr++;
1764 					continue;
1765 				}
1766 				*cstr = '\0';
1767 			}
1768 		}
1769 
1770 		/*
1771 		 * If the list name has a '-' prepended then a match of
1772 		 * the following name implies failure instead of success.
1773 		 */
1774 		if (*gr == '-') {
1775 			response = 0;
1776 			gr++;
1777 		} else {
1778 			response = 1;
1779 		}
1780 
1781 		/*
1782 		 * First check if we have '@' entry, as it doesn't
1783 		 * require client hostname.
1784 		 */
1785 		if (*gr == '@') {
1786 			gr++;
1787 
1788 			/* Netname support */
1789 			if (!isdigit(*gr) && *gr != '[') {
1790 				if ((np = getnetbyname_r(gr, &n, buff,
1791 				    sizeof (buff))) != NULL &&
1792 				    np->n_net != 0) {
1793 					while ((np->n_net & 0xFF000000u) == 0)
1794 						np->n_net <<= 8;
1795 					np->n_net = htonl(np->n_net);
1796 					if (inet_ntop(AF_INET, &np->n_net, addr,
1797 					    INET_ADDRSTRLEN) == NULL)
1798 						break;
1799 					if (inet_matchaddr((*pnb)->buf, addr))
1800 						return (response);
1801 				}
1802 			} else {
1803 				if (inet_matchaddr((*pnb)->buf, gr))
1804 					return (response);
1805 			}
1806 
1807 			if (cstr == NULL)
1808 				break;
1809 
1810 			gr = ++cstr;
1811 
1812 			continue;
1813 		}
1814 
1815 		/*
1816 		 * No other checks can be performed if client address
1817 		 * can't be resolved.
1818 		 */
1819 		if (clnames == NULL) {
1820 			if (cstr == NULL)
1821 				break;
1822 
1823 			gr = ++cstr;
1824 
1825 			continue;
1826 		}
1827 
1828 		/* Otherwise loop through all client hostname aliases */
1829 		for (i = 0; i < clnames->h_cnt; i++) {
1830 			host = clnames->h_hostservs[i].h_host;
1831 
1832 			/*
1833 			 * If the list name begins with a dot then
1834 			 * do a domain name suffix comparison.
1835 			 * A single dot matches any name with no
1836 			 * suffix.
1837 			 */
1838 			if (*gr == '.') {
1839 				if (*(gr + 1) == '\0') {  /* single dot */
1840 					if (strchr(host, '.') == NULL)
1841 						return (response);
1842 				} else {
1843 					off = strlen(host) - strlen(gr);
1844 					if (off > 0 &&
1845 					    strcasecmp(host + off, gr) == 0) {
1846 						return (response);
1847 					}
1848 				}
1849 			} else {
1850 				/* Just do a hostname match */
1851 				if (strcasecmp(gr, host) == 0)
1852 					return (response);
1853 			}
1854 		}
1855 
1856 		nentries++;
1857 
1858 		if (cstr == NULL)
1859 			break;
1860 
1861 		gr = ++cstr;
1862 	}
1863 
1864 	if (clnames == NULL)
1865 		return (0);
1866 
1867 	return (netgroup_check(clnames, access_list, nentries));
1868 }
1869 
1870 
1871 static char *optlist[] = {
1872 #define	OPT_RO		0
1873 	SHOPT_RO,
1874 #define	OPT_RW		1
1875 	SHOPT_RW,
1876 #define	OPT_ROOT	2
1877 	SHOPT_ROOT,
1878 #define	OPT_SECURE	3
1879 	SHOPT_SECURE,
1880 #define	OPT_ANON	4
1881 	SHOPT_ANON,
1882 #define	OPT_WINDOW	5
1883 	SHOPT_WINDOW,
1884 #define	OPT_NOSUID	6
1885 	SHOPT_NOSUID,
1886 #define	OPT_ACLOK	7
1887 	SHOPT_ACLOK,
1888 #define	OPT_SEC		8
1889 	SHOPT_SEC,
1890 #define	OPT_NONE	9
1891 	SHOPT_NONE,
1892 	NULL
1893 };
1894 
1895 static int
1896 map_flavor(char *str)
1897 {
1898 	seconfig_t sec;
1899 
1900 	if (nfs_getseconfig_byname(str, &sec))
1901 		return (-1);
1902 
1903 	return (sec.sc_nfsnum);
1904 }
1905 
1906 /*
1907  * If the option string contains a "sec="
1908  * option, then use new option syntax.
1909  */
1910 static int
1911 newopts(char *opts)
1912 {
1913 	char *head, *p, *val;
1914 
1915 	if (!opts || *opts == '\0')
1916 		return (0);
1917 
1918 	head = strdup(opts);
1919 	if (head == NULL) {
1920 		syslog(LOG_ERR, "opts: no memory");
1921 		return (0);
1922 	}
1923 
1924 	p = head;
1925 	while (*p) {
1926 		if (getsubopt(&p, optlist, &val) == OPT_SEC) {
1927 			free(head);
1928 			return (1);
1929 		}
1930 	}
1931 
1932 	free(head);
1933 	return (0);
1934 }
1935 
1936 /*
1937  * Given an export and the clients hostname(s)
1938  * determine the security flavors that this
1939  * client is permitted to use.
1940  *
1941  * This routine is called only for "old" syntax, i.e.
1942  * only one security flavor is allowed.  So we need
1943  * to determine two things: the particular flavor,
1944  * and whether the client is allowed to use this
1945  * flavor, i.e. is in the access list.
1946  *
1947  * Note that if there is no access list, then the
1948  * default is that access is granted.
1949  */
1950 static int
1951 getclientsflavors_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
1952     struct nd_hostservlist **clnames, int *flavors)
1953 {
1954 	char *opts, *p, *val;
1955 	boolean_t ok = B_FALSE;
1956 	int defaultaccess = 1;
1957 	boolean_t reject = B_FALSE;
1958 
1959 	opts = strdup(sh->sh_opts);
1960 	if (opts == NULL) {
1961 		syslog(LOG_ERR, "getclientsflavors: no memory");
1962 		return (0);
1963 	}
1964 
1965 	flavors[0] = AUTH_SYS;
1966 	p = opts;
1967 
1968 	while (*p) {
1969 
1970 		switch (getsubopt(&p, optlist, &val)) {
1971 		case OPT_SECURE:
1972 			flavors[0] = AUTH_DES;
1973 			break;
1974 
1975 		case OPT_RO:
1976 		case OPT_RW:
1977 			defaultaccess = 0;
1978 			if (in_access_list(transp, nb, clnames, val))
1979 				ok++;
1980 			break;
1981 
1982 		case OPT_NONE:
1983 			defaultaccess = 0;
1984 			if (in_access_list(transp, nb, clnames, val))
1985 				reject = B_TRUE;
1986 		}
1987 	}
1988 
1989 	free(opts);
1990 
1991 	/* none takes precedence over everything else */
1992 	if (reject)
1993 		ok = B_TRUE;
1994 
1995 	return (defaultaccess || ok);
1996 }
1997 
1998 /*
1999  * Given an export and the clients hostname(s)
2000  * determine the security flavors that this
2001  * client is permitted to use.
2002  *
2003  * This is somewhat more complicated than the "old"
2004  * routine because the options may contain multiple
2005  * security flavors (sec=) each with its own access
2006  * lists.  So a client could be granted access based
2007  * on a number of security flavors.  Note that the
2008  * type of access might not always be the same, the
2009  * client may get readonly access with one flavor
2010  * and readwrite with another, however the client
2011  * is not told this detail, it gets only the list
2012  * of flavors, and only if the client is using
2013  * version 3 of the mount protocol.
2014  */
2015 static int
2016 getclientsflavors_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2017     struct nd_hostservlist **clnames, int *flavors)
2018 {
2019 	char *opts, *p, *val;
2020 	char *lasts;
2021 	char *f;
2022 	boolean_t access_ok;
2023 	int count, c;
2024 	boolean_t reject = B_FALSE;
2025 
2026 	opts = strdup(sh->sh_opts);
2027 	if (opts == NULL) {
2028 		syslog(LOG_ERR, "getclientsflavors: no memory");
2029 		return (0);
2030 	}
2031 
2032 	p = opts;
2033 	count = c = 0;
2034 	/* default access is rw */
2035 	access_ok = B_TRUE;
2036 
2037 	while (*p) {
2038 		switch (getsubopt(&p, optlist, &val)) {
2039 		case OPT_SEC:
2040 			/*
2041 			 * Before a new sec=xxx option, check if we need
2042 			 * to move the c index back to the previous count.
2043 			 */
2044 			if (!access_ok) {
2045 				c = count;
2046 			}
2047 
2048 			/* get all the sec=f1[:f2] flavors */
2049 			while ((f = strtok_r(val, ":", &lasts))
2050 			    != NULL) {
2051 				flavors[c++] = map_flavor(f);
2052 				val = NULL;
2053 			}
2054 
2055 			/* for a new sec=xxx option, default is rw access */
2056 			access_ok = B_TRUE;
2057 			break;
2058 
2059 		case OPT_RO:
2060 		case OPT_RW:
2061 			if (in_access_list(transp, nb, clnames, val)) {
2062 				count = c;
2063 				access_ok = B_TRUE;
2064 			} else {
2065 				access_ok = B_FALSE;
2066 			}
2067 			break;
2068 
2069 		case OPT_NONE:
2070 			if (in_access_list(transp, nb, clnames, val))
2071 				reject = B_TRUE; /* none overides rw/ro */
2072 			break;
2073 		}
2074 	}
2075 
2076 	if (reject)
2077 		access_ok = B_FALSE;
2078 
2079 	if (!access_ok)
2080 		c = count;
2081 
2082 	free(opts);
2083 
2084 	return (c);
2085 }
2086 
2087 /*
2088  * This is a tricky piece of code that parses the
2089  * share options looking for a match on the auth
2090  * flavor that the client is using. If it finds
2091  * a match, then the client is given ro, rw, or
2092  * no access depending whether it is in the access
2093  * list.  There is a special case for "secure"
2094  * flavor.  Other flavors are values of the new "sec=" option.
2095  */
2096 int
2097 check_client(share_t *sh, struct netbuf *nb,
2098     struct nd_hostservlist *clnames, int flavor)
2099 {
2100 	if (newopts(sh->sh_opts))
2101 		return (check_client_new(sh, NULL, &nb, &clnames, flavor));
2102 	else
2103 		return (check_client_old(sh, NULL, &nb, &clnames, flavor));
2104 }
2105 
2106 static int
2107 check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2108     struct nd_hostservlist **clnames, int flavor)
2109 {
2110 	char *opts, *p, *val;
2111 	int match;	/* Set when a flavor is matched */
2112 	int perm = 0;	/* Set when "ro", "rw" or "root" is matched */
2113 	int list = 0;	/* Set when "ro", "rw" is found */
2114 	int ro_val = 0;	/* Set if ro option is 'ro=' */
2115 	int rw_val = 0;	/* Set if rw option is 'rw=' */
2116 	boolean_t reject = B_FALSE; /* if none= contains the host */
2117 
2118 	opts = strdup(sh->sh_opts);
2119 	if (opts == NULL) {
2120 		syslog(LOG_ERR, "check_client: no memory");
2121 		return (0);
2122 	}
2123 
2124 	p = opts;
2125 	match = AUTH_UNIX;
2126 
2127 	while (*p) {
2128 		switch (getsubopt(&p, optlist, &val)) {
2129 
2130 		case OPT_SECURE:
2131 			match = AUTH_DES;
2132 			break;
2133 
2134 		case OPT_RO:
2135 			list++;
2136 			if (val) ro_val++;
2137 			if (in_access_list(transp, nb, clnames, val))
2138 				perm |= NFSAUTH_RO;
2139 			break;
2140 
2141 		case OPT_RW:
2142 			list++;
2143 			if (val) rw_val++;
2144 			if (in_access_list(transp, nb, clnames, val))
2145 				perm |= NFSAUTH_RW;
2146 			break;
2147 
2148 		case OPT_ROOT:
2149 			/*
2150 			 * Check if the client is in
2151 			 * the root list. Only valid
2152 			 * for AUTH_SYS.
2153 			 */
2154 			if (flavor != AUTH_SYS)
2155 				break;
2156 
2157 			if (val == NULL || *val == '\0')
2158 				break;
2159 
2160 			if (in_access_list(transp, nb, clnames, val))
2161 				perm |= NFSAUTH_ROOT;
2162 			break;
2163 
2164 		case OPT_NONE:
2165 			/*
2166 			 * Check if  the client should have no access
2167 			 * to this share at all. This option behaves
2168 			 * more like "root" than either "rw" or "ro".
2169 			 */
2170 			if (in_access_list(transp, nb, clnames, val))
2171 				reject = B_TRUE;
2172 			break;
2173 		}
2174 	}
2175 
2176 	free(opts);
2177 
2178 	if (flavor != match || reject)
2179 		return (NFSAUTH_DENIED);
2180 
2181 	if (list) {
2182 		/*
2183 		 * If the client doesn't match an "ro" or "rw"
2184 		 * list then set no access.
2185 		 */
2186 		if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2187 			perm |= NFSAUTH_DENIED;
2188 	} else {
2189 		/*
2190 		 * The client matched a flavor entry that
2191 		 * has no explicit "rw" or "ro" determination.
2192 		 * Default it to "rw".
2193 		 */
2194 		perm |= NFSAUTH_RW;
2195 	}
2196 
2197 
2198 	/*
2199 	 * The client may show up in both ro= and rw=
2200 	 * lists.  If so, then turn off the RO access
2201 	 * bit leaving RW access.
2202 	 */
2203 	if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2204 		/*
2205 		 * Logically cover all permutations of rw=,ro=.
2206 		 * In the case where, rw,ro=<host> we would like
2207 		 * to remove RW access for the host.  In all other cases
2208 		 * RW wins the precedence battle.
2209 		 */
2210 		if (!rw_val && ro_val) {
2211 			perm &= ~(NFSAUTH_RW);
2212 		} else {
2213 			perm &= ~(NFSAUTH_RO);
2214 		}
2215 	}
2216 
2217 	return (perm);
2218 }
2219 
2220 /*
2221  * Check if the client has access by using a flavor different from
2222  * the given "flavor". If "flavor" is not in the flavor list,
2223  * return TRUE to indicate that this "flavor" is a wrong sec.
2224  */
2225 static bool_t
2226 is_wrongsec(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2227 	struct nd_hostservlist **clnames, int flavor)
2228 {
2229 	int flavor_list[MAX_FLAVORS];
2230 	int flavor_count, i;
2231 
2232 	/* get the flavor list that the client has access with */
2233 	flavor_count = getclientsflavors_new(sh, transp, nb,
2234 	    clnames, flavor_list);
2235 
2236 	if (flavor_count == 0)
2237 		return (FALSE);
2238 
2239 	/*
2240 	 * Check if the given "flavor" is in the flavor_list.
2241 	 */
2242 	for (i = 0; i < flavor_count; i++) {
2243 		if (flavor == flavor_list[i])
2244 			return (FALSE);
2245 	}
2246 
2247 	/*
2248 	 * If "flavor" is not in the flavor_list, return TRUE to indicate
2249 	 * that the client should have access by using a security flavor
2250 	 * different from this "flavor".
2251 	 */
2252 	return (TRUE);
2253 }
2254 
2255 /*
2256  * Given an export and the client's hostname, we
2257  * check the security options to see whether the
2258  * client is allowed to use the given security flavor.
2259  *
2260  * The strategy is to proceed through the options looking
2261  * for a flavor match, then pay attention to the ro, rw,
2262  * and root options.
2263  *
2264  * Note that an entry may list several flavors in a
2265  * single entry, e.g.
2266  *
2267  *   sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2268  *
2269  */
2270 
2271 static int
2272 check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2273     struct nd_hostservlist **clnames, int flavor)
2274 {
2275 	char *opts, *p, *val;
2276 	char *lasts;
2277 	char *f;
2278 	int match = 0;	/* Set when a flavor is matched */
2279 	int perm = 0;	/* Set when "ro", "rw" or "root" is matched */
2280 	int list = 0;	/* Set when "ro", "rw" is found */
2281 	int ro_val = 0;	/* Set if ro option is 'ro=' */
2282 	int rw_val = 0;	/* Set if rw option is 'rw=' */
2283 	boolean_t reject;
2284 
2285 	opts = strdup(sh->sh_opts);
2286 	if (opts == NULL) {
2287 		syslog(LOG_ERR, "check_client: no memory");
2288 		return (0);
2289 	}
2290 
2291 	p = opts;
2292 
2293 	while (*p) {
2294 		switch (getsubopt(&p, optlist, &val)) {
2295 
2296 		case OPT_SEC:
2297 			if (match)
2298 				goto done;
2299 
2300 			while ((f = strtok_r(val, ":", &lasts))
2301 			    != NULL) {
2302 				if (flavor == map_flavor(f)) {
2303 					match = 1;
2304 					break;
2305 				}
2306 				val = NULL;
2307 			}
2308 			break;
2309 
2310 		case OPT_RO:
2311 			if (!match)
2312 				break;
2313 
2314 			list++;
2315 			if (val) ro_val++;
2316 			if (in_access_list(transp, nb, clnames, val))
2317 				perm |= NFSAUTH_RO;
2318 			break;
2319 
2320 		case OPT_RW:
2321 			if (!match)
2322 				break;
2323 
2324 			list++;
2325 			if (val) rw_val++;
2326 			if (in_access_list(transp, nb, clnames, val))
2327 				perm |= NFSAUTH_RW;
2328 			break;
2329 
2330 		case OPT_ROOT:
2331 			/*
2332 			 * Check if the client is in
2333 			 * the root list. Only valid
2334 			 * for AUTH_SYS.
2335 			 */
2336 			if (flavor != AUTH_SYS)
2337 				break;
2338 
2339 			if (!match)
2340 				break;
2341 
2342 			if (val == NULL || *val == '\0')
2343 				break;
2344 
2345 			if (in_access_list(transp, nb, clnames, val))
2346 				perm |= NFSAUTH_ROOT;
2347 			break;
2348 
2349 		case OPT_NONE:
2350 			/*
2351 			 * Check if  the client should have no access
2352 			 * to this share at all. This option behaves
2353 			 * more like "root" than either "rw" or "ro".
2354 			 */
2355 			if (in_access_list(transp, nb, clnames, val))
2356 				perm |= NFSAUTH_DENIED;
2357 			break;
2358 		}
2359 	}
2360 
2361 done:
2362 	/*
2363 	 * If no match then set the perm accordingly
2364 	 */
2365 	if (!match || perm & NFSAUTH_DENIED)
2366 		return (NFSAUTH_DENIED);
2367 
2368 	if (list) {
2369 		/*
2370 		 * If the client doesn't match an "ro" or "rw" list then
2371 		 * check if it may have access by using a different flavor.
2372 		 * If so, return NFSAUTH_WRONGSEC.
2373 		 * If not, return NFSAUTH_DENIED.
2374 		 */
2375 		if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2376 			if (is_wrongsec(sh, transp, nb, clnames, flavor))
2377 				perm |= NFSAUTH_WRONGSEC;
2378 			else
2379 				perm |= NFSAUTH_DENIED;
2380 		}
2381 	} else {
2382 		/*
2383 		 * The client matched a flavor entry that
2384 		 * has no explicit "rw" or "ro" determination.
2385 		 * Make sure it defaults to "rw".
2386 		 */
2387 		perm |= NFSAUTH_RW;
2388 	}
2389 
2390 	/*
2391 	 * The client may show up in both ro= and rw=
2392 	 * lists.  If so, then turn off the RO access
2393 	 * bit leaving RW access.
2394 	 */
2395 	if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2396 		/*
2397 		 * Logically cover all permutations of rw=,ro=.
2398 		 * In the case where, rw,ro=<host> we would like
2399 		 * to remove RW access for the host.  In all other cases
2400 		 * RW wins the precedence battle.
2401 		 */
2402 		if (!rw_val && ro_val) {
2403 			perm &= ~(NFSAUTH_RW);
2404 		} else {
2405 			perm &= ~(NFSAUTH_RO);
2406 		}
2407 	}
2408 
2409 	free(opts);
2410 
2411 	return (perm);
2412 }
2413 
2414 void
2415 check_sharetab()
2416 {
2417 	FILE *f;
2418 	struct stat st;
2419 	static timestruc_t last_sharetab_time;
2420 	timestruc_t prev_sharetab_time;
2421 	share_t *sh;
2422 	struct sh_list *shp, *shp_prev;
2423 	int res, c = 0;
2424 
2425 	/*
2426 	 *  read in /etc/dfs/sharetab if it has changed
2427 	 */
2428 	if (stat(SHARETAB, &st) != 0) {
2429 		syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2430 		return;
2431 	}
2432 
2433 	if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
2434 	    st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
2435 		/*
2436 		 * No change.
2437 		 */
2438 		return;
2439 	}
2440 
2441 	/*
2442 	 * Remember the mod time, then after getting the
2443 	 * write lock check again.  If another thread
2444 	 * already did the update, then there's no
2445 	 * work to do.
2446 	 */
2447 	prev_sharetab_time = last_sharetab_time;
2448 
2449 	(void) rw_wrlock(&sharetab_lock);
2450 
2451 	if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
2452 	    prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
2453 		(void) rw_unlock(&sharetab_lock);
2454 		return;
2455 	}
2456 
2457 	/*
2458 	 * Note that since the sharetab is now in memory
2459 	 * and a snapshot is taken, we no longer have to
2460 	 * lock the file.
2461 	 */
2462 	f = fopen(SHARETAB, "r");
2463 	if (f == NULL) {
2464 		syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
2465 		(void) rw_unlock(&sharetab_lock);
2466 		return;
2467 	}
2468 
2469 	/*
2470 	 * Once we are sure /etc/dfs/sharetab has been
2471 	 * modified, flush netgroup cache entries.
2472 	 */
2473 	netgrp_cache_flush();
2474 
2475 	sh_free(share_list);			/* free old list */
2476 	share_list = NULL;
2477 
2478 	while ((res = getshare(f, &sh)) > 0) {
2479 		c++;
2480 		if (strcmp(sh->sh_fstype, "nfs") != 0)
2481 			continue;
2482 
2483 		shp = malloc(sizeof (*shp));
2484 		if (shp == NULL)
2485 			goto alloc_failed;
2486 		if (share_list == NULL)
2487 			share_list = shp;
2488 		else
2489 			/* LINTED not used before set */
2490 			shp_prev->shl_next = shp;
2491 		shp_prev = shp;
2492 		shp->shl_next = NULL;
2493 		shp->shl_sh = sharedup(sh);
2494 		if (shp->shl_sh == NULL)
2495 			goto alloc_failed;
2496 	}
2497 
2498 	if (res < 0)
2499 		syslog(LOG_ERR, "%s: invalid at line %d\n",
2500 		    SHARETAB, c + 1);
2501 
2502 	if (stat(SHARETAB, &st) != 0) {
2503 		syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2504 		(void) fclose(f);
2505 		(void) rw_unlock(&sharetab_lock);
2506 		return;
2507 	}
2508 
2509 	last_sharetab_time = st.st_mtim;
2510 	(void) fclose(f);
2511 	(void) rw_unlock(&sharetab_lock);
2512 
2513 	return;
2514 
2515 alloc_failed:
2516 
2517 	syslog(LOG_ERR, "check_sharetab: no memory");
2518 	sh_free(share_list);
2519 	share_list = NULL;
2520 	(void) fclose(f);
2521 	(void) rw_unlock(&sharetab_lock);
2522 }
2523 
2524 static void
2525 sh_free(struct sh_list *shp)
2526 {
2527 	register struct sh_list *next;
2528 
2529 	while (shp) {
2530 		sharefree(shp->shl_sh);
2531 		next = shp->shl_next;
2532 		free(shp);
2533 		shp = next;
2534 	}
2535 }
2536 
2537 
2538 /*
2539  * Remove an entry from mounted list
2540  */
2541 static void
2542 umount(struct svc_req *rqstp)
2543 {
2544 	char *host, *path, *remove_path;
2545 	char rpath[MAXPATHLEN];
2546 	struct nd_hostservlist *clnames = NULL;
2547 	SVCXPRT *transp;
2548 	struct netbuf *nb;
2549 
2550 	transp = rqstp->rq_xprt;
2551 	path = NULL;
2552 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
2553 		svcerr_decode(transp);
2554 		return;
2555 	}
2556 	errno = 0;
2557 	if (!svc_sendreply(transp, xdr_void, (char *)NULL))
2558 		log_cant_reply(transp);
2559 
2560 	if (getclientsnames(transp, &nb, &clnames) != 0) {
2561 		/*
2562 		 * Without the hostname we can't do audit or delete
2563 		 * this host from the mount entries.
2564 		 */
2565 		svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
2566 		return;
2567 	}
2568 	host = clnames->h_hostservs[0].h_host;
2569 
2570 	if (verbose)
2571 		syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
2572 
2573 	audit_mountd_umount(host, path);
2574 
2575 	remove_path = rpath;	/* assume we will use the cannonical path */
2576 	if (realpath(path, rpath) == NULL) {
2577 		if (verbose)
2578 			syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
2579 		remove_path = path;	/* use path provided instead */
2580 	}
2581 
2582 	mntlist_delete(host, remove_path);	/* remove from mount list */
2583 
2584 	svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
2585 	netdir_free(clnames, ND_HOSTSERVLIST);
2586 }
2587 
2588 /*
2589  * Remove all entries for one machine from mounted list
2590  */
2591 static void
2592 umountall(struct svc_req *rqstp)
2593 {
2594 	struct nd_hostservlist *clnames = NULL;
2595 	SVCXPRT *transp;
2596 	char *host;
2597 	struct netbuf *nb;
2598 
2599 	transp = rqstp->rq_xprt;
2600 	if (!svc_getargs(transp, xdr_void, NULL)) {
2601 		svcerr_decode(transp);
2602 		return;
2603 	}
2604 	/*
2605 	 * We assume that this call is asynchronous and made via rpcbind
2606 	 * callit routine.  Therefore return control immediately. The error
2607 	 * causes rpcbind to remain silent, as opposed to every machine
2608 	 * on the net blasting the requester with a response.
2609 	 */
2610 	svcerr_systemerr(transp);
2611 	if (getclientsnames(transp, &nb, &clnames) != 0) {
2612 		/* Can't do anything without the name of the client */
2613 		return;
2614 	}
2615 
2616 	host = clnames->h_hostservs[0].h_host;
2617 
2618 	/*
2619 	 * Remove all hosts entries from mount list
2620 	 */
2621 	mntlist_delete_all(host);
2622 
2623 	if (verbose)
2624 		syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
2625 
2626 	netdir_free(clnames, ND_HOSTSERVLIST);
2627 }
2628 
2629 void *
2630 exmalloc(size_t size)
2631 {
2632 	void *ret;
2633 
2634 	if ((ret = malloc(size)) == NULL) {
2635 		syslog(LOG_ERR, "Out of memory");
2636 		exit(1);
2637 	}
2638 	return (ret);
2639 }
2640 
2641 static void
2642 sigexit(int signum)
2643 {
2644 
2645 	if (signum == SIGHUP)
2646 		_exit(0);
2647 	_exit(1);
2648 }
2649 
2650 static tsol_tpent_t *
2651 get_client_template(struct sockaddr *sock)
2652 {
2653 	in_addr_t	v4client;
2654 	in6_addr_t	v6client;
2655 	char		v4_addr[INET_ADDRSTRLEN];
2656 	char		v6_addr[INET6_ADDRSTRLEN];
2657 	tsol_rhent_t	*rh;
2658 	tsol_tpent_t	*tp;
2659 
2660 	switch (sock->sa_family) {
2661 	case AF_INET:
2662 		v4client = ((struct sockaddr_in *)(void *)sock)->
2663 		    sin_addr.s_addr;
2664 		if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
2665 		    NULL)
2666 			return (NULL);
2667 		rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
2668 		if (rh == NULL)
2669 			return (NULL);
2670 		tp = tsol_gettpbyname(rh->rh_template);
2671 		tsol_freerhent(rh);
2672 		return (tp);
2673 		break;
2674 	case AF_INET6:
2675 		v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
2676 		if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
2677 		    NULL)
2678 			return (NULL);
2679 		rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
2680 		if (rh == NULL)
2681 			return (NULL);
2682 		tp = tsol_gettpbyname(rh->rh_template);
2683 		tsol_freerhent(rh);
2684 		return (tp);
2685 		break;
2686 	default:
2687 		return (NULL);
2688 	}
2689 }
2690