xref: /illumos-gate/usr/src/cmd/fs.d/nfs/mountd/mountd.c (revision 86147f89)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
527242a7cSthurlow  * Common Development and Distribution License (the "License").
627242a7cSthurlow  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
2089621fe1SMarcel Telka  */
2189621fe1SMarcel Telka 
2289621fe1SMarcel Telka /*
2389621fe1SMarcel Telka  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
242c3ccf74SGordon Ross  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
252c3ccf74SGordon Ross  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
2635a075c3SToomas Soome  * Copyright 2022 RackTop Systems.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
30544783caSToomas Soome /*	  All Rights Reserved	*/
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
347c478bd9Sstevel@tonic-gate  * under license from the Regents of the University of California.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <stdio.h>
3897adda44SMarcel Telka #include <stdio_ext.h>
397c478bd9Sstevel@tonic-gate #include <stdlib.h>
407c478bd9Sstevel@tonic-gate #include <ctype.h>
417c478bd9Sstevel@tonic-gate #include <sys/types.h>
427c478bd9Sstevel@tonic-gate #include <string.h>
437c478bd9Sstevel@tonic-gate #include <syslog.h>
447c478bd9Sstevel@tonic-gate #include <sys/param.h>
457c478bd9Sstevel@tonic-gate #include <rpc/rpc.h>
467c478bd9Sstevel@tonic-gate #include <sys/stat.h>
477c478bd9Sstevel@tonic-gate #include <netconfig.h>
487c478bd9Sstevel@tonic-gate #include <netdir.h>
497c478bd9Sstevel@tonic-gate #include <sys/file.h>
507c478bd9Sstevel@tonic-gate #include <sys/time.h>
517c478bd9Sstevel@tonic-gate #include <sys/errno.h>
527c478bd9Sstevel@tonic-gate #include <rpcsvc/mount.h>
537c478bd9Sstevel@tonic-gate #include <sys/pathconf.h>
547c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
557c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
56250a0733Sth #include <sys/wait.h>
5797adda44SMarcel Telka #include <sys/resource.h>
587c478bd9Sstevel@tonic-gate #include <signal.h>
597c478bd9Sstevel@tonic-gate #include <locale.h>
607c478bd9Sstevel@tonic-gate #include <unistd.h>
617c478bd9Sstevel@tonic-gate #include <errno.h>
627c478bd9Sstevel@tonic-gate #include <sys/socket.h>
637c478bd9Sstevel@tonic-gate #include <netinet/in.h>
647c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
657c478bd9Sstevel@tonic-gate #include <netdb.h>
667c478bd9Sstevel@tonic-gate #include <thread.h>
677c478bd9Sstevel@tonic-gate #include <assert.h>
687c478bd9Sstevel@tonic-gate #include <priv_utils.h>
691cc55349Srmesta #include <nfs/auth.h>
701cc55349Srmesta #include <nfs/nfssys.h>
717c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
727c478bd9Sstevel@tonic-gate #include <nfs/nfs_sec.h>
737c478bd9Sstevel@tonic-gate #include <rpcsvc/daemon_utils.h>
747c478bd9Sstevel@tonic-gate #include <deflt.h>
757c478bd9Sstevel@tonic-gate #include "../../fslib.h"
76a237e38eSth #include <sharefs/share.h>
77a237e38eSth #include <sharefs/sharetab.h>
787c478bd9Sstevel@tonic-gate #include "../lib/sharetab.h"
797c478bd9Sstevel@tonic-gate #include "mountd.h"
8003986916Sjarrett #include <tsol/label.h>
8103986916Sjarrett #include <sys/tsol/label_macro.h>
8203986916Sjarrett #include <libtsnet.h>
834a508a79SThomas Haynes #include <sys/sdt.h>
84dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include <libscf.h>
85dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include <limits.h>
866b086bafSSam Falkner #include <sys/nvpair.h>
876b086bafSSam Falkner #include <attr.h>
88dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include "smfcfg.h"
895cb0d679SMarcel Telka #include <pwd.h>
905cb0d679SMarcel Telka #include <grp.h>
9189621fe1SMarcel Telka #include <alloca.h>
9235a075c3SToomas Soome #include <libinetutil.h>
9335a075c3SToomas Soome #include <libsocket_priv.h>
947c478bd9Sstevel@tonic-gate 
95250a0733Sth extern int daemonize_init(void);
9654d34259SMarcel Telka extern void daemonize_fini(int);
9754d34259SMarcel Telka 
9854d34259SMarcel Telka extern int _nfssys(int, void *);
99250a0733Sth 
1007c478bd9Sstevel@tonic-gate struct sh_list *share_list;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate rwlock_t sharetab_lock;		/* lock to protect the cached sharetab */
1037c478bd9Sstevel@tonic-gate static mutex_t mnttab_lock;	/* prevent concurrent mnttab readers */
1047c478bd9Sstevel@tonic-gate 
1054a508a79SThomas Haynes static mutex_t logging_queue_lock;
1064a508a79SThomas Haynes static cond_t logging_queue_cv;
1074a508a79SThomas Haynes 
1084a508a79SThomas Haynes static share_t *find_lofsentry(char *, int *);
109a9685eaaSMarcel Telka static int getclientsflavors_old(share_t *, struct cln *, int *);
110a9685eaaSMarcel Telka static int getclientsflavors_new(share_t *, struct cln *, int *);
111a9685eaaSMarcel Telka static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
112a9685eaaSMarcel Telka     gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
113a9685eaaSMarcel Telka static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
114a9685eaaSMarcel Telka     gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
1157c478bd9Sstevel@tonic-gate static void mnt(struct svc_req *, SVCXPRT *);
1167c478bd9Sstevel@tonic-gate static void mnt_pathconf(struct svc_req *);
1174a508a79SThomas Haynes static int mount(struct svc_req *r);
1187c478bd9Sstevel@tonic-gate static void sh_free(struct sh_list *);
1197c478bd9Sstevel@tonic-gate static void umount(struct svc_req *);
1207c478bd9Sstevel@tonic-gate static void umountall(struct svc_req *);
1217c478bd9Sstevel@tonic-gate static int newopts(char *);
12203986916Sjarrett static tsol_tpent_t *get_client_template(struct sockaddr *);
1237c478bd9Sstevel@tonic-gate 
1242c3ccf74SGordon Ross static int debug;
1257c478bd9Sstevel@tonic-gate static int verbose;
1267c478bd9Sstevel@tonic-gate static int rejecting;
1277c478bd9Sstevel@tonic-gate static int mount_vers_min = MOUNTVERS;
1287c478bd9Sstevel@tonic-gate static int mount_vers_max = MOUNTVERS3;
1292c3ccf74SGordon Ross static int mountd_port = 0;
13035a075c3SToomas Soome static boolean_t mountd_remote_dump = B_FALSE;
1317c478bd9Sstevel@tonic-gate 
132b89a8333Snatalie li - Sun Microsystems - Irvine United States extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
133b89a8333Snatalie li - Sun Microsystems - Irvine United States 
1341cc55349Srmesta thread_t	nfsauth_thread;
135b89a8333Snatalie li - Sun Microsystems - Irvine United States thread_t	cmd_thread;
1364a508a79SThomas Haynes thread_t	logging_thread;
1374a508a79SThomas Haynes 
1384a508a79SThomas Haynes typedef struct logging_data {
1394a508a79SThomas Haynes 	char			*ld_host;
1404a508a79SThomas Haynes 	char			*ld_path;
1414a508a79SThomas Haynes 	char			*ld_rpath;
1424a508a79SThomas Haynes 	int			ld_status;
1434a508a79SThomas Haynes 	char			*ld_netid;
1444a508a79SThomas Haynes 	struct netbuf		*ld_nb;
1454a508a79SThomas Haynes 	struct logging_data	*ld_next;
1464a508a79SThomas Haynes } logging_data;
1474a508a79SThomas Haynes 
1484a508a79SThomas Haynes static logging_data *logging_head = NULL;
1494a508a79SThomas Haynes static logging_data *logging_tail = NULL;
1501cc55349Srmesta 
15189621fe1SMarcel Telka /*
15289621fe1SMarcel Telka  * Our copy of some system variables obtained using sysconf(3c)
15389621fe1SMarcel Telka  */
15489621fe1SMarcel Telka static long ngroups_max;	/* _SC_NGROUPS_MAX */
15589621fe1SMarcel Telka static long pw_size;		/* _SC_GETPW_R_SIZE_MAX */
15689621fe1SMarcel Telka 
15735a075c3SToomas Soome /* Cached address info for this host. */
15835a075c3SToomas Soome static struct addrinfo *host_ai = NULL;
15935a075c3SToomas Soome 
1601cc55349Srmesta static void *
nfsauth_svc(void * arg __unused)161544783caSToomas Soome nfsauth_svc(void *arg __unused)
1621cc55349Srmesta {
1631cc55349Srmesta 	int	doorfd = -1;
1641cc55349Srmesta 	uint_t	darg;
1651cc55349Srmesta #ifdef DEBUG
1661cc55349Srmesta 	int	dfd;
1671cc55349Srmesta #endif
1681cc55349Srmesta 
1691cc55349Srmesta 	if ((doorfd = door_create(nfsauth_func, NULL,
1701cc55349Srmesta 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1711cc55349Srmesta 		syslog(LOG_ERR, "Unable to create door: %m\n");
1721cc55349Srmesta 		exit(10);
1731cc55349Srmesta 	}
1741cc55349Srmesta 
1751cc55349Srmesta #ifdef DEBUG
1761cc55349Srmesta 	/*
1771cc55349Srmesta 	 * Create a file system path for the door
1781cc55349Srmesta 	 */
1791cc55349Srmesta 	if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
1801cc55349Srmesta 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
1811cc55349Srmesta 		syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
1821cc55349Srmesta 		(void) close(doorfd);
1831cc55349Srmesta 		exit(11);
1841cc55349Srmesta 	}
1851cc55349Srmesta 
1861cc55349Srmesta 	/*
1871cc55349Srmesta 	 * Clean up any stale namespace associations
1881cc55349Srmesta 	 */
1891cc55349Srmesta 	(void) fdetach(MOUNTD_DOOR);
1901cc55349Srmesta 
1911cc55349Srmesta 	/*
1921cc55349Srmesta 	 * Register in namespace to pass to the kernel to door_ki_open
1931cc55349Srmesta 	 */
1941cc55349Srmesta 	if (fattach(doorfd, MOUNTD_DOOR) == -1) {
1951cc55349Srmesta 		syslog(LOG_ERR, "Unable to fattach door: %m\n");
1961cc55349Srmesta 		(void) close(dfd);
1971cc55349Srmesta 		(void) close(doorfd);
1981cc55349Srmesta 		exit(12);
1991cc55349Srmesta 	}
2001cc55349Srmesta 	(void) close(dfd);
2011cc55349Srmesta #endif
2021cc55349Srmesta 
2031cc55349Srmesta 	/*
2041cc55349Srmesta 	 * Must pass the doorfd down to the kernel.
2051cc55349Srmesta 	 */
2061cc55349Srmesta 	darg = doorfd;
2071cc55349Srmesta 	(void) _nfssys(MOUNTD_ARGS, &darg);
2081cc55349Srmesta 
2091cc55349Srmesta 	/*
2101cc55349Srmesta 	 * Wait for incoming calls
2111cc55349Srmesta 	 */
2121cc55349Srmesta 	/*CONSTCOND*/
2131cc55349Srmesta 	for (;;)
2141cc55349Srmesta 		(void) pause();
2151cc55349Srmesta 
2161cc55349Srmesta 	/*NOTREACHED*/
2171cc55349Srmesta 	syslog(LOG_ERR, gettext("Door server exited"));
2181cc55349Srmesta 	return (NULL);
2191cc55349Srmesta }
2201cc55349Srmesta 
221b89a8333Snatalie li - Sun Microsystems - Irvine United States /*
222b89a8333Snatalie li - Sun Microsystems - Irvine United States  * NFS command service thread code for setup and handling of the
223b89a8333Snatalie li - Sun Microsystems - Irvine United States  * nfs_cmd requests for character set conversion and other future
224b89a8333Snatalie li - Sun Microsystems - Irvine United States  * events.
225b89a8333Snatalie li - Sun Microsystems - Irvine United States  */
226b89a8333Snatalie li - Sun Microsystems - Irvine United States 
227b89a8333Snatalie li - Sun Microsystems - Irvine United States static void *
cmd_svc(void * arg)228b89a8333Snatalie li - Sun Microsystems - Irvine United States cmd_svc(void *arg)
229b89a8333Snatalie li - Sun Microsystems - Irvine United States {
230b89a8333Snatalie li - Sun Microsystems - Irvine United States 	int	doorfd = -1;
231b89a8333Snatalie li - Sun Microsystems - Irvine United States 	uint_t	darg;
232b89a8333Snatalie li - Sun Microsystems - Irvine United States 
233b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if ((doorfd = door_create(nfscmd_func, NULL,
234b89a8333Snatalie li - Sun Microsystems - Irvine United States 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
235b89a8333Snatalie li - Sun Microsystems - Irvine United States 		syslog(LOG_ERR, "Unable to create cmd door: %m\n");
236b89a8333Snatalie li - Sun Microsystems - Irvine United States 		exit(10);
237b89a8333Snatalie li - Sun Microsystems - Irvine United States 	}
238b89a8333Snatalie li - Sun Microsystems - Irvine United States 
239b89a8333Snatalie li - Sun Microsystems - Irvine United States 	/*
240b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 * Must pass the doorfd down to the kernel.
241b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 */
242b89a8333Snatalie li - Sun Microsystems - Irvine United States 	darg = doorfd;
243b89a8333Snatalie li - Sun Microsystems - Irvine United States 	(void) _nfssys(NFSCMD_ARGS, &darg);
244b89a8333Snatalie li - Sun Microsystems - Irvine United States 
245b89a8333Snatalie li - Sun Microsystems - Irvine United States 	/*
246b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 * Wait for incoming calls
247b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 */
248b89a8333Snatalie li - Sun Microsystems - Irvine United States 	/*CONSTCOND*/
249b89a8333Snatalie li - Sun Microsystems - Irvine United States 	for (;;)
250b89a8333Snatalie li - Sun Microsystems - Irvine United States 		(void) pause();
251b89a8333Snatalie li - Sun Microsystems - Irvine United States 
252b89a8333Snatalie li - Sun Microsystems - Irvine United States 	/*NOTREACHED*/
253b89a8333Snatalie li - Sun Microsystems - Irvine United States 	syslog(LOG_ERR, gettext("Cmd door server exited"));
254b89a8333Snatalie li - Sun Microsystems - Irvine United States 	return (NULL);
255b89a8333Snatalie li - Sun Microsystems - Irvine United States }
256250a0733Sth 
2574a508a79SThomas Haynes static void
free_logging_data(logging_data * lq)2584a508a79SThomas Haynes free_logging_data(logging_data *lq)
2594a508a79SThomas Haynes {
2604a508a79SThomas Haynes 	if (lq != NULL) {
2614a508a79SThomas Haynes 		free(lq->ld_host);
2624a508a79SThomas Haynes 		free(lq->ld_netid);
2634a508a79SThomas Haynes 
2644a508a79SThomas Haynes 		if (lq->ld_nb != NULL) {
2654a508a79SThomas Haynes 			free(lq->ld_nb->buf);
2664a508a79SThomas Haynes 			free(lq->ld_nb);
2674a508a79SThomas Haynes 		}
2684a508a79SThomas Haynes 
2694a508a79SThomas Haynes 		free(lq->ld_path);
2704a508a79SThomas Haynes 		free(lq->ld_rpath);
2714a508a79SThomas Haynes 
2724a508a79SThomas Haynes 		free(lq);
2734a508a79SThomas Haynes 	}
2744a508a79SThomas Haynes }
2754a508a79SThomas Haynes 
2764a508a79SThomas Haynes static logging_data *
remove_head_of_queue(void)2774a508a79SThomas Haynes remove_head_of_queue(void)
2784a508a79SThomas Haynes {
2794a508a79SThomas Haynes 	logging_data    *lq;
2804a508a79SThomas Haynes 
2814a508a79SThomas Haynes 	/*
2824a508a79SThomas Haynes 	 * Pull it off the queue.
2834a508a79SThomas Haynes 	 */
2844a508a79SThomas Haynes 	lq = logging_head;
2854a508a79SThomas Haynes 	if (lq) {
2864a508a79SThomas Haynes 		logging_head = lq->ld_next;
2874a508a79SThomas Haynes 
2884a508a79SThomas Haynes 		/*
2894a508a79SThomas Haynes 		 * Drained it.
2904a508a79SThomas Haynes 		 */
2914a508a79SThomas Haynes 		if (logging_head == NULL) {
2924a508a79SThomas Haynes 			logging_tail = NULL;
2934a508a79SThomas Haynes 		}
2944a508a79SThomas Haynes 	}
2954a508a79SThomas Haynes 
2964a508a79SThomas Haynes 	return (lq);
2974a508a79SThomas Haynes }
2984a508a79SThomas Haynes 
2994a508a79SThomas Haynes static void
do_logging_queue(logging_data * lq)3004a508a79SThomas Haynes do_logging_queue(logging_data *lq)
3014a508a79SThomas Haynes {
3024a508a79SThomas Haynes 	int		cleared = 0;
3034a508a79SThomas Haynes 	char		*host;
3044a508a79SThomas Haynes 
3054a508a79SThomas Haynes 	while (lq) {
306a9685eaaSMarcel Telka 		struct cln cln;
307a9685eaaSMarcel Telka 
3084a508a79SThomas Haynes 		if (lq->ld_host == NULL) {
3094a508a79SThomas Haynes 			DTRACE_PROBE(mountd, name_by_lazy);
310a9685eaaSMarcel Telka 			cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
311a9685eaaSMarcel Telka 			host = cln_gethost(&cln);
3124a508a79SThomas Haynes 		} else
3134a508a79SThomas Haynes 			host = lq->ld_host;
3144a508a79SThomas Haynes 
3154a508a79SThomas Haynes 		audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
3164a508a79SThomas Haynes 
3174a508a79SThomas Haynes 		/* add entry to mount list */
3184a508a79SThomas Haynes 		if (lq->ld_rpath)
3194a508a79SThomas Haynes 			mntlist_new(host, lq->ld_rpath);
3204a508a79SThomas Haynes 
321a9685eaaSMarcel Telka 		if (lq->ld_host == NULL)
322a9685eaaSMarcel Telka 			cln_fini(&cln);
323d383eb7aSMarcel Telka 
324c596e866SMarcel Telka 		free_logging_data(lq);
325c596e866SMarcel Telka 		cleared++;
3264a508a79SThomas Haynes 
3274a508a79SThomas Haynes 		(void) mutex_lock(&logging_queue_lock);
3284a508a79SThomas Haynes 		lq = remove_head_of_queue();
3294a508a79SThomas Haynes 		(void) mutex_unlock(&logging_queue_lock);
3304a508a79SThomas Haynes 	}
3314a508a79SThomas Haynes 
3324a508a79SThomas Haynes 	DTRACE_PROBE1(mountd, logging_cleared, cleared);
3334a508a79SThomas Haynes }
3344a508a79SThomas Haynes 
3354a508a79SThomas Haynes static void *
logging_svc(void * arg __unused)336544783caSToomas Soome logging_svc(void *arg __unused)
3374a508a79SThomas Haynes {
3384a508a79SThomas Haynes 	logging_data	*lq;
3394a508a79SThomas Haynes 
3404a508a79SThomas Haynes 	for (;;) {
3414a508a79SThomas Haynes 		(void) mutex_lock(&logging_queue_lock);
3424a508a79SThomas Haynes 		while (logging_head == NULL) {
3434a508a79SThomas Haynes 			(void) cond_wait(&logging_queue_cv,
3444a508a79SThomas Haynes 			    &logging_queue_lock);
3454a508a79SThomas Haynes 		}
3464a508a79SThomas Haynes 
3474a508a79SThomas Haynes 		lq = remove_head_of_queue();
3484a508a79SThomas Haynes 		(void) mutex_unlock(&logging_queue_lock);
3494a508a79SThomas Haynes 
3504a508a79SThomas Haynes 		do_logging_queue(lq);
3514a508a79SThomas Haynes 	}
3524a508a79SThomas Haynes 
3534a508a79SThomas Haynes 	/*NOTREACHED*/
3544a508a79SThomas Haynes 	syslog(LOG_ERR, gettext("Logging server exited"));
3554a508a79SThomas Haynes 	return (NULL);
3564a508a79SThomas Haynes }
3574a508a79SThomas Haynes 
3582c3ccf74SGordon Ross /*
3592c3ccf74SGordon Ross  * This function is called for each configured network type to
3602c3ccf74SGordon Ross  * bind and register our RPC service programs.
3612c3ccf74SGordon Ross  *
3622c3ccf74SGordon Ross  * On TCP or UDP, we may want to bind MOUNTPROG on a specific port
3632c3ccf74SGordon Ross  * (when mountd_port is specified) in which case we'll use the
3642c3ccf74SGordon Ross  * variant of svc_tp_create() that lets us pass a bind address.
3652c3ccf74SGordon Ross  */
3662c3ccf74SGordon Ross static void
md_svc_tp_create(struct netconfig * nconf)3672c3ccf74SGordon Ross md_svc_tp_create(struct netconfig *nconf)
3682c3ccf74SGordon Ross {
3692c3ccf74SGordon Ross 	char port_str[8];
3702c3ccf74SGordon Ross 	struct nd_hostserv hs;
3712c3ccf74SGordon Ross 	struct nd_addrlist *al = NULL;
3722c3ccf74SGordon Ross 	SVCXPRT *xprt = NULL;
3732c3ccf74SGordon Ross 	rpcvers_t vers;
3742c3ccf74SGordon Ross 
3752c3ccf74SGordon Ross 	vers = mount_vers_max;
3762c3ccf74SGordon Ross 
3772c3ccf74SGordon Ross 	/*
3782c3ccf74SGordon Ross 	 * If mountd_port is set and this is an inet transport,
3792c3ccf74SGordon Ross 	 * bind this service on the specified port.  The TLI way
3802c3ccf74SGordon Ross 	 * to create such a bind address is netdir_getbyname()
3812c3ccf74SGordon Ross 	 * with the special "host" HOST_SELF_BIND.  This builds
3822c3ccf74SGordon Ross 	 * an all-zeros IP address with the specified port.
3832c3ccf74SGordon Ross 	 */
3842c3ccf74SGordon Ross 	if (mountd_port != 0 &&
3852c3ccf74SGordon Ross 	    (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
3862c3ccf74SGordon Ross 	    strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
3872c3ccf74SGordon Ross 		int err;
3882c3ccf74SGordon Ross 
389544783caSToomas Soome 		(void) snprintf(port_str, sizeof (port_str), "%u",
3902c3ccf74SGordon Ross 		    (unsigned short)mountd_port);
3912c3ccf74SGordon Ross 
3922c3ccf74SGordon Ross 		hs.h_host = HOST_SELF_BIND;
3932c3ccf74SGordon Ross 		hs.h_serv = port_str;
3942c3ccf74SGordon Ross 		err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
3952c3ccf74SGordon Ross 		if (err == 0 && al != NULL) {
3962c3ccf74SGordon Ross 			xprt = svc_tp_create_addr(mnt, MOUNTPROG, vers,
3972c3ccf74SGordon Ross 			    nconf, al->n_addrs);
3982c3ccf74SGordon Ross 			netdir_free(al, ND_ADDRLIST);
3992c3ccf74SGordon Ross 		}
4002c3ccf74SGordon Ross 		if (xprt == NULL) {
4012c3ccf74SGordon Ross 			syslog(LOG_ERR, "mountd: unable to create "
4022c3ccf74SGordon Ross 			    "(MOUNTD,%d) on transport %s (port %d)",
4032c3ccf74SGordon Ross 			    vers, nconf->nc_netid, mountd_port);
4042c3ccf74SGordon Ross 		}
4052c3ccf74SGordon Ross 		/* fall-back to default bind */
4062c3ccf74SGordon Ross 	}
4072c3ccf74SGordon Ross 	if (xprt == NULL) {
4082c3ccf74SGordon Ross 		/*
4092c3ccf74SGordon Ross 		 * Had mountd_port=0, or non-inet transport,
4102c3ccf74SGordon Ross 		 * or the bind to a specific port failed.
4112c3ccf74SGordon Ross 		 * Do a default bind.
4122c3ccf74SGordon Ross 		 */
4132c3ccf74SGordon Ross 		xprt = svc_tp_create(mnt, MOUNTPROG, vers, nconf);
4142c3ccf74SGordon Ross 	}
4152c3ccf74SGordon Ross 	if (xprt == NULL) {
4162c3ccf74SGordon Ross 		syslog(LOG_ERR, "mountd: unable to create "
4172c3ccf74SGordon Ross 		    "(MOUNTD,%d) on transport %s",
4182c3ccf74SGordon Ross 		    vers, nconf->nc_netid);
4192c3ccf74SGordon Ross 		return;
4202c3ccf74SGordon Ross 	}
4212c3ccf74SGordon Ross 
4222c3ccf74SGordon Ross 	/*
4232c3ccf74SGordon Ross 	 * Register additional versions on this transport.
4242c3ccf74SGordon Ross 	 */
4252c3ccf74SGordon Ross 	while (--vers >= mount_vers_min) {
4262c3ccf74SGordon Ross 		if (!svc_reg(xprt, MOUNTPROG, vers, mnt, nconf)) {
4272c3ccf74SGordon Ross 			(void) syslog(LOG_ERR, "mountd: "
4282c3ccf74SGordon Ross 			    "failed to register vers %d on %s",
4292c3ccf74SGordon Ross 			    vers, nconf->nc_netid);
4302c3ccf74SGordon Ross 		}
4312c3ccf74SGordon Ross 	}
4322c3ccf74SGordon Ross }
4332c3ccf74SGordon Ross 
43411606941Sjwahlig int
main(int argc,char * argv[])43511606941Sjwahlig main(int argc, char *argv[])
4367c478bd9Sstevel@tonic-gate {
4371cc55349Srmesta 	int	pid;
4381cc55349Srmesta 	int	c;
4392f416683SMarcel Telka 	int	rpc_svc_fdunlim = 1;
4401cc55349Srmesta 	int	rpc_svc_mode = RPC_SVC_MT_AUTO;
4411cc55349Srmesta 	int	maxrecsz = RPC_MAXDATASIZE;
4421cc55349Srmesta 	bool_t	exclbind = TRUE;
4431cc55349Srmesta 	bool_t	can_do_mlp;
4441cc55349Srmesta 	long	thr_flags = (THR_NEW_LWP|THR_DAEMON);
44535a075c3SToomas Soome 	char defval[5];
4463ed679c0SGordon Ross 	int ret, bufsz;
44797adda44SMarcel Telka 	struct rlimit rl;
448361f55a5SMarcel Telka 	int listen_backlog = 0;
449361f55a5SMarcel Telka 	int max_threads = 0;
450361f55a5SMarcel Telka 	int tmp;
4512c3ccf74SGordon Ross 	struct netconfig *nconf;
4522c3ccf74SGordon Ross 	NCONF_HANDLE *nc;
453544783caSToomas Soome 	const char *errstr;
454250a0733Sth 	int	pipe_fd = -1;
45535a075c3SToomas Soome 	char	hostbuf[256];
456250a0733Sth 
4577c478bd9Sstevel@tonic-gate 	/*
4587c478bd9Sstevel@tonic-gate 	 * Mountd requires uid 0 for:
4597c478bd9Sstevel@tonic-gate 	 *	/etc/rmtab updates (we could chown it to daemon)
4607c478bd9Sstevel@tonic-gate 	 *	/etc/dfs/dfstab reading (it wants to lock out share which
4617c478bd9Sstevel@tonic-gate 	 *		doesn't do any locking before first truncate;
4627c478bd9Sstevel@tonic-gate 	 *		NFS share does; should use fcntl locking instead)
4637c478bd9Sstevel@tonic-gate 	 *	Needed privileges:
4647c478bd9Sstevel@tonic-gate 	 *		auditing
4657c478bd9Sstevel@tonic-gate 	 *		nfs syscall
4667c478bd9Sstevel@tonic-gate 	 *		file dac search (so it can stat all files)
46745916cd2Sjpk 	 *	Optional privileges:
46845916cd2Sjpk 	 *		MLP
4697c478bd9Sstevel@tonic-gate 	 */
47045916cd2Sjpk 	can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
4717c478bd9Sstevel@tonic-gate 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
47245916cd2Sjpk 	    PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
4732c3ccf74SGordon Ross 	    PRIV_NET_PRIVADDR,
47445916cd2Sjpk 	    can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
4757c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
476250a0733Sth 		    "%s: must be run with sufficient privileges\n",
477250a0733Sth 		    argv[0]);
4787c478bd9Sstevel@tonic-gate 		exit(1);
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 
48197adda44SMarcel Telka 	if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
48297adda44SMarcel Telka 		syslog(LOG_ERR, "getrlimit failed");
48397adda44SMarcel Telka 	} else {
48497adda44SMarcel Telka 		rl.rlim_cur = rl.rlim_max;
48597adda44SMarcel Telka 		if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
48697adda44SMarcel Telka 			syslog(LOG_ERR, "setrlimit failed");
48797adda44SMarcel Telka 	}
48897adda44SMarcel Telka 
48997adda44SMarcel Telka 	(void) enable_extended_FILE_stdio(-1, -1);
49097adda44SMarcel Telka 
491*86147f89SToomas Soome 	/* Upgrade SMF settings, if necessary. */
492*86147f89SToomas Soome 	nfs_config_upgrade(NFSD);
493*86147f89SToomas Soome 
494361f55a5SMarcel Telka 	ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
495361f55a5SMarcel Telka 	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
496361f55a5SMarcel Telka 	if (ret != SA_OK) {
497361f55a5SMarcel Telka 		syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
498361f55a5SMarcel Telka 		    "failed, using default value");
499361f55a5SMarcel Telka 	}
5007c478bd9Sstevel@tonic-gate 
5012c3ccf74SGordon Ross 	ret = nfs_smf_get_iprop("mountd_port", &mountd_port,
5022c3ccf74SGordon Ross 	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
5032c3ccf74SGordon Ross 	if (ret != SA_OK) {
5042c3ccf74SGordon Ross 		syslog(LOG_ERR, "Reading of mountd_port from SMF "
5052c3ccf74SGordon Ross 		    "failed, using default value");
5062c3ccf74SGordon Ross 	}
5072c3ccf74SGordon Ross 
5082c3ccf74SGordon Ross 	while ((c = getopt(argc, argv, "dvrm:p:")) != EOF) {
5097c478bd9Sstevel@tonic-gate 		switch (c) {
5102c3ccf74SGordon Ross 		case 'd':
5112c3ccf74SGordon Ross 			debug++;
5122c3ccf74SGordon Ross 			break;
5137c478bd9Sstevel@tonic-gate 		case 'v':
5147c478bd9Sstevel@tonic-gate 			verbose++;
5157c478bd9Sstevel@tonic-gate 			break;
5167c478bd9Sstevel@tonic-gate 		case 'r':
5177c478bd9Sstevel@tonic-gate 			rejecting = 1;
5187c478bd9Sstevel@tonic-gate 			break;
5197c478bd9Sstevel@tonic-gate 		case 'm':
520544783caSToomas Soome 			tmp = strtonum(optarg, 1, INT_MAX, &errstr);
521544783caSToomas Soome 			if (errstr != NULL) {
522361f55a5SMarcel Telka 				(void) fprintf(stderr, "%s: invalid "
523544783caSToomas Soome 				    "max_threads option: %s, using defaults\n",
524544783caSToomas Soome 				    argv[0], errstr);
525361f55a5SMarcel Telka 				break;
5267c478bd9Sstevel@tonic-gate 			}
527361f55a5SMarcel Telka 			max_threads = tmp;
5287c478bd9Sstevel@tonic-gate 			break;
5292c3ccf74SGordon Ross 		case 'p':
530544783caSToomas Soome 			tmp = strtonum(optarg, 1, UINT16_MAX, &errstr);
531544783caSToomas Soome 			if (errstr != NULL) {
5322c3ccf74SGordon Ross 				(void) fprintf(stderr, "%s: invalid port "
533544783caSToomas Soome 				    "number: %s\n", argv[0], errstr);
5342c3ccf74SGordon Ross 				break;
5352c3ccf74SGordon Ross 			}
5362c3ccf74SGordon Ross 			mountd_port = tmp;
5372c3ccf74SGordon Ross 			break;
538361f55a5SMarcel Telka 		default:
539361f55a5SMarcel Telka 			fprintf(stderr, "usage: mountd [-v] [-r]\n");
540361f55a5SMarcel Telka 			exit(1);
5417c478bd9Sstevel@tonic-gate 		}
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/*
5453ed679c0SGordon Ross 	 * One might be tempted to use the NFS configuration values
5463ed679c0SGordon Ross 	 * server_versmin, server_versmax to constrain the range of
5473ed679c0SGordon Ross 	 * mountd versions supported here.  However, older clients
5483ed679c0SGordon Ross 	 * use mountd V1 for showmount, so just leave all mountd
5493ed679c0SGordon Ross 	 * versions enabled here. (mount_vers_min, mount_vers_max)
5507c478bd9Sstevel@tonic-gate 	 */
5517c478bd9Sstevel@tonic-gate 
552361f55a5SMarcel Telka 	ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
553361f55a5SMarcel Telka 	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
554361f55a5SMarcel Telka 	if (ret != SA_OK) {
555361f55a5SMarcel Telka 		syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
556361f55a5SMarcel Telka 		    "failed, using default value");
557361f55a5SMarcel Telka 	}
558361f55a5SMarcel Telka 
55935a075c3SToomas Soome 	bufsz = sizeof (defval);
56035a075c3SToomas Soome 	ret = nfs_smf_get_prop("mountd_remote_dump", defval, DEFAULT_INSTANCE,
56135a075c3SToomas Soome 	    SCF_TYPE_BOOLEAN, NFSD, &bufsz);
56235a075c3SToomas Soome 	if (ret == SA_OK) {
56335a075c3SToomas Soome 		mountd_remote_dump = string_to_boolean(defval);
56435a075c3SToomas Soome 	}
56535a075c3SToomas Soome 	if (!mountd_remote_dump) {
56635a075c3SToomas Soome 		/* Cache host address list */
56735a075c3SToomas Soome 		if (gethostname(hostbuf, sizeof (hostbuf)) < 0) {
56835a075c3SToomas Soome 			syslog(LOG_ERR, "gethostname() failed");
56935a075c3SToomas Soome 			exit(1);
57035a075c3SToomas Soome 		}
57135a075c3SToomas Soome 		if (getaddrinfo(hostbuf, NULL, NULL, &host_ai) != 0) {
57235a075c3SToomas Soome 			syslog(LOG_ERR, "getaddrinfo() failed");
57335a075c3SToomas Soome 			exit(1);
57435a075c3SToomas Soome 		}
57535a075c3SToomas Soome 	}
57635a075c3SToomas Soome 
5777c478bd9Sstevel@tonic-gate 	/*
5787c478bd9Sstevel@tonic-gate 	 * Sanity check versions,
5797c478bd9Sstevel@tonic-gate 	 * even though we may get versions > MOUNTVERS3, we still need
5807c478bd9Sstevel@tonic-gate 	 * to start nfsauth service, so continue on regardless of values.
5817c478bd9Sstevel@tonic-gate 	 */
5822c3ccf74SGordon Ross 	if (mount_vers_max > MOUNTVERS3)
5832c3ccf74SGordon Ross 		mount_vers_max = MOUNTVERS3;
5847c478bd9Sstevel@tonic-gate 	if (mount_vers_min > mount_vers_max) {
585361f55a5SMarcel Telka 		fprintf(stderr, "server_versmin > server_versmax\n");
5867c478bd9Sstevel@tonic-gate 		mount_vers_max = mount_vers_min;
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
5897c478bd9Sstevel@tonic-gate 	(void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
5907c478bd9Sstevel@tonic-gate 	(void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
5914a508a79SThomas Haynes 	(void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
5924a508a79SThomas Haynes 	(void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
5934a508a79SThomas Haynes 
5947c478bd9Sstevel@tonic-gate 	netgroup_init();
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
5977c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
5987c478bd9Sstevel@tonic-gate #endif
5997c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/* Don't drop core if the NFS module isn't loaded. */
6027c478bd9Sstevel@tonic-gate 	(void) signal(SIGSYS, SIG_IGN);
6037c478bd9Sstevel@tonic-gate 
6042c3ccf74SGordon Ross 	if (!debug)
6052c3ccf74SGordon Ross 		pipe_fd = daemonize_init();
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	/*
6087c478bd9Sstevel@tonic-gate 	 * If we coredump it'll be in /core
6097c478bd9Sstevel@tonic-gate 	 */
6107c478bd9Sstevel@tonic-gate 	if (chdir("/") < 0)
611361f55a5SMarcel Telka 		fprintf(stderr, "chdir /: %s\n", strerror(errno));
6127c478bd9Sstevel@tonic-gate 
6132c3ccf74SGordon Ross 	if (!debug)
6142c3ccf74SGordon Ross 		openlog("mountd", LOG_PID, LOG_DAEMON);
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	/*
6177c478bd9Sstevel@tonic-gate 	 * establish our lock on the lock file and write our pid to it.
6187c478bd9Sstevel@tonic-gate 	 * exit if some other process holds the lock, or if there's any
6197c478bd9Sstevel@tonic-gate 	 * error in writing/locking the file.
6207c478bd9Sstevel@tonic-gate 	 */
6217c478bd9Sstevel@tonic-gate 	pid = _enter_daemon_lock(MOUNTD);
6227c478bd9Sstevel@tonic-gate 	switch (pid) {
6237c478bd9Sstevel@tonic-gate 	case 0:
6247c478bd9Sstevel@tonic-gate 		break;
6257c478bd9Sstevel@tonic-gate 	case -1:
626361f55a5SMarcel Telka 		fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
6277c478bd9Sstevel@tonic-gate 		    strerror(errno));
6287c478bd9Sstevel@tonic-gate 		exit(2);
6297c478bd9Sstevel@tonic-gate 	default:
6307c478bd9Sstevel@tonic-gate 		/* daemon was already running */
6317c478bd9Sstevel@tonic-gate 		exit(0);
6327c478bd9Sstevel@tonic-gate 	}
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	audit_mountd_setup();	/* BSM */
6357c478bd9Sstevel@tonic-gate 
63689621fe1SMarcel Telka 	/*
63789621fe1SMarcel Telka 	 * Get required system variables
63889621fe1SMarcel Telka 	 */
63989621fe1SMarcel Telka 	if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
64089621fe1SMarcel Telka 		syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
64189621fe1SMarcel Telka 		exit(1);
64289621fe1SMarcel Telka 	}
64389621fe1SMarcel Telka 	if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
64489621fe1SMarcel Telka 		syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
64589621fe1SMarcel Telka 		exit(1);
64689621fe1SMarcel Telka 	}
64789621fe1SMarcel Telka 
6482f416683SMarcel Telka 	/*
6492f416683SMarcel Telka 	 * Set number of file descriptors to unlimited
6502f416683SMarcel Telka 	 */
6512f416683SMarcel Telka 	if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
6522f416683SMarcel Telka 		syslog(LOG_INFO, "unable to set number of FDs to unlimited");
6532f416683SMarcel Telka 	}
6542f416683SMarcel Telka 
6557c478bd9Sstevel@tonic-gate 	/*
6567c478bd9Sstevel@tonic-gate 	 * Tell RPC that we want automatic thread mode.
6577c478bd9Sstevel@tonic-gate 	 * A new thread will be spawned for each request.
6587c478bd9Sstevel@tonic-gate 	 */
6597c478bd9Sstevel@tonic-gate 	if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
660361f55a5SMarcel Telka 		fprintf(stderr, "unable to set automatic MT mode\n");
6617c478bd9Sstevel@tonic-gate 		exit(1);
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	/*
6657c478bd9Sstevel@tonic-gate 	 * Enable non-blocking mode and maximum record size checks for
6667c478bd9Sstevel@tonic-gate 	 * connection oriented transports.
6677c478bd9Sstevel@tonic-gate 	 */
6687c478bd9Sstevel@tonic-gate 	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
669361f55a5SMarcel Telka 		fprintf(stderr, "unable to set RPC max record size\n");
6707c478bd9Sstevel@tonic-gate 	}
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	/*
6737c478bd9Sstevel@tonic-gate 	 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
6747c478bd9Sstevel@tonic-gate 	 * from being hijacked by a bind to a more specific addr.
6757c478bd9Sstevel@tonic-gate 	 */
6767c478bd9Sstevel@tonic-gate 	if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
677361f55a5SMarcel Telka 		fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
678361f55a5SMarcel Telka 	}
679361f55a5SMarcel Telka 
680361f55a5SMarcel Telka 	/*
681361f55a5SMarcel Telka 	 * Set the maximum number of outstanding connection
682361f55a5SMarcel Telka 	 * indications (listen backlog) to the value specified.
683361f55a5SMarcel Telka 	 */
684361f55a5SMarcel Telka 	if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
685361f55a5SMarcel Telka 	    &listen_backlog)) {
686361f55a5SMarcel Telka 		fprintf(stderr, "unable to set listen backlog\n");
687361f55a5SMarcel Telka 		exit(1);
6887c478bd9Sstevel@tonic-gate 	}
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	/*
691361f55a5SMarcel Telka 	 * If max_threads was specified, then set the
6927c478bd9Sstevel@tonic-gate 	 * maximum number of threads to the value specified.
6937c478bd9Sstevel@tonic-gate 	 */
694361f55a5SMarcel Telka 	if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
695361f55a5SMarcel Telka 		fprintf(stderr, "unable to set max_threads\n");
6967c478bd9Sstevel@tonic-gate 		exit(1);
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate 
6992c3ccf74SGordon Ross 	if (mountd_port < 0 || mountd_port > UINT16_MAX) {
7002c3ccf74SGordon Ross 		fprintf(stderr, "unable to use specified port\n");
7012c3ccf74SGordon Ross 		exit(1);
7022c3ccf74SGordon Ross 	}
7032c3ccf74SGordon Ross 
7047c478bd9Sstevel@tonic-gate 	/*
7057c478bd9Sstevel@tonic-gate 	 * Make sure to unregister any previous versions in case the
7067c478bd9Sstevel@tonic-gate 	 * user is reconfiguring the server in interesting ways.
7077c478bd9Sstevel@tonic-gate 	 */
7087c478bd9Sstevel@tonic-gate 	svc_unreg(MOUNTPROG, MOUNTVERS);
7097c478bd9Sstevel@tonic-gate 	svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
7107c478bd9Sstevel@tonic-gate 	svc_unreg(MOUNTPROG, MOUNTVERS3);
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 	/*
7131cc55349Srmesta 	 * Create the nfsauth thread with same signal disposition
7141cc55349Srmesta 	 * as the main thread. We need to create a separate thread
7151cc55349Srmesta 	 * since mountd() will be both an RPC server (for remote
7161cc55349Srmesta 	 * traffic) _and_ a doors server (for kernel upcalls).
7177c478bd9Sstevel@tonic-gate 	 */
7181cc55349Srmesta 	if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
719361f55a5SMarcel Telka 		fprintf(stderr,
720361f55a5SMarcel Telka 		    gettext("Failed to create NFSAUTH svc thread\n"));
7211cc55349Srmesta 		exit(2);
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
724b89a8333Snatalie li - Sun Microsystems - Irvine United States 	/*
725b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 * Create the cmd service thread with same signal disposition
726b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 * as the main thread. We need to create a separate thread
727b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 * since mountd() will be both an RPC server (for remote
728b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 * traffic) _and_ a doors server (for kernel upcalls).
729b89a8333Snatalie li - Sun Microsystems - Irvine United States 	 */
730b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
731b89a8333Snatalie li - Sun Microsystems - Irvine United States 		syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
732b89a8333Snatalie li - Sun Microsystems - Irvine United States 		exit(2);
733b89a8333Snatalie li - Sun Microsystems - Irvine United States 	}
734b89a8333Snatalie li - Sun Microsystems - Irvine United States 
7354a508a79SThomas Haynes 	/*
7364a508a79SThomas Haynes 	 * Create an additional thread to service the rmtab and
7374a508a79SThomas Haynes 	 * audit_mountd_mount logging for mount requests. Use the same
7384a508a79SThomas Haynes 	 * signal disposition as the main thread. We create
7394a508a79SThomas Haynes 	 * a separate thread to allow the mount request threads to
7404a508a79SThomas Haynes 	 * clear as soon as possible.
7414a508a79SThomas Haynes 	 */
7424a508a79SThomas Haynes 	if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
7434a508a79SThomas Haynes 		syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
7444a508a79SThomas Haynes 		exit(2);
7454a508a79SThomas Haynes 	}
7464a508a79SThomas Haynes 
7477c478bd9Sstevel@tonic-gate 	/*
7482c3ccf74SGordon Ross 	 * Enumerate network transports and create service listeners
7492c3ccf74SGordon Ross 	 * as appropriate for each.
7507c478bd9Sstevel@tonic-gate 	 */
7512c3ccf74SGordon Ross 	if ((nc = setnetconfig()) == NULL) {
7522c3ccf74SGordon Ross 		syslog(LOG_ERR, "setnetconfig failed: %m");
7532c3ccf74SGordon Ross 		return (-1);
7547c478bd9Sstevel@tonic-gate 	}
7552c3ccf74SGordon Ross 	while ((nconf = getnetconfig(nc)) != NULL) {
7562c3ccf74SGordon Ross 		/*
7572c3ccf74SGordon Ross 		 * Skip things like tpi_raw, invisible...
7582c3ccf74SGordon Ross 		 */
7592c3ccf74SGordon Ross 		if ((nconf->nc_flag & NC_VISIBLE) == 0)
7602c3ccf74SGordon Ross 			continue;
7612c3ccf74SGordon Ross 		if (nconf->nc_semantics != NC_TPI_CLTS &&
7622c3ccf74SGordon Ross 		    nconf->nc_semantics != NC_TPI_COTS &&
7632c3ccf74SGordon Ross 		    nconf->nc_semantics != NC_TPI_COTS_ORD)
7642c3ccf74SGordon Ross 			continue;
7652c3ccf74SGordon Ross 
7662c3ccf74SGordon Ross 		md_svc_tp_create(nconf);
7677c478bd9Sstevel@tonic-gate 	}
7682c3ccf74SGordon Ross 	(void) endnetconfig(nc);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	/*
7717c478bd9Sstevel@tonic-gate 	 * Start serving
7727c478bd9Sstevel@tonic-gate 	 */
7737c478bd9Sstevel@tonic-gate 	rmtab_load();
774250a0733Sth 
775250a0733Sth 	daemonize_fini(pipe_fd);
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	/* Get rid of the most dangerous basic privileges. */
7787c478bd9Sstevel@tonic-gate 	__fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
7797c478bd9Sstevel@tonic-gate 	    (char *)NULL);
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 	svc_run();
7827c478bd9Sstevel@tonic-gate 	syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
7837c478bd9Sstevel@tonic-gate 	abort();
784250a0733Sth 
7857c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
78611606941Sjwahlig 	return (0);
7877c478bd9Sstevel@tonic-gate }
7887c478bd9Sstevel@tonic-gate 
78935a075c3SToomas Soome /*
79035a075c3SToomas Soome  * copied from usr/src/uts/common/klm/nlm_impl.c
79135a075c3SToomas Soome  */
79235a075c3SToomas Soome static bool_t
caller_is_local(SVCXPRT * transp)79335a075c3SToomas Soome caller_is_local(SVCXPRT *transp)
79435a075c3SToomas Soome {
79535a075c3SToomas Soome 	struct addrinfo *a;
79635a075c3SToomas Soome 	char *netid;
79735a075c3SToomas Soome 	struct netbuf *rtaddr;
79835a075c3SToomas Soome 	struct sockaddr_storage addr;
79935a075c3SToomas Soome 	bool_t rv = FALSE;
80035a075c3SToomas Soome 
80135a075c3SToomas Soome 	netid = transp->xp_netid;
80235a075c3SToomas Soome 	rtaddr = svc_getrpccaller(transp);
80335a075c3SToomas Soome 
80435a075c3SToomas Soome 	if (netid == NULL)
80535a075c3SToomas Soome 		return (FALSE);
80635a075c3SToomas Soome 
80735a075c3SToomas Soome 	if (strcmp(netid, "ticlts") == 0 ||
80835a075c3SToomas Soome 	    strcmp(netid, "ticotsord") == 0)
80935a075c3SToomas Soome 		return (TRUE);
81035a075c3SToomas Soome 
81135a075c3SToomas Soome 	if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) {
81235a075c3SToomas Soome 		struct sockaddr_in *sin = (void *)rtaddr->buf;
81335a075c3SToomas Soome 
81435a075c3SToomas Soome 		if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
81535a075c3SToomas Soome 			return (TRUE);
81635a075c3SToomas Soome 
81735a075c3SToomas Soome 		memmove(&addr, sin, sizeof (*sin));
81835a075c3SToomas Soome 	}
81935a075c3SToomas Soome 	if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) {
82035a075c3SToomas Soome 		struct sockaddr_in6 *sin6 = (void *)rtaddr->buf;
82135a075c3SToomas Soome 
82235a075c3SToomas Soome 		if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
82335a075c3SToomas Soome 			return (TRUE);
82435a075c3SToomas Soome 
82535a075c3SToomas Soome 		memmove(&addr, sin6, sizeof (*sin6));
82635a075c3SToomas Soome 	}
82735a075c3SToomas Soome 
82835a075c3SToomas Soome 	for (a = host_ai; a != NULL; a = a->ai_next) {
82935a075c3SToomas Soome 		if (sockaddrcmp(&addr,
83035a075c3SToomas Soome 		    (struct sockaddr_storage *)a->ai_addr)) {
83135a075c3SToomas Soome 			rv = TRUE;
83235a075c3SToomas Soome 			break;
83335a075c3SToomas Soome 		}
83435a075c3SToomas Soome 	}
83535a075c3SToomas Soome 	return (rv);
83635a075c3SToomas Soome }
83735a075c3SToomas Soome 
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate  * Server procedure switch routine
8407c478bd9Sstevel@tonic-gate  */
8417c478bd9Sstevel@tonic-gate void
mnt(struct svc_req * rqstp,SVCXPRT * transp)8427c478bd9Sstevel@tonic-gate mnt(struct svc_req *rqstp, SVCXPRT *transp)
8437c478bd9Sstevel@tonic-gate {
8447c478bd9Sstevel@tonic-gate 	switch (rqstp->rq_proc) {
8457c478bd9Sstevel@tonic-gate 	case NULLPROC:
8467c478bd9Sstevel@tonic-gate 		errno = 0;
8477c478bd9Sstevel@tonic-gate 		if (!svc_sendreply(transp, xdr_void, (char *)0))
8487c478bd9Sstevel@tonic-gate 			log_cant_reply(transp);
8497c478bd9Sstevel@tonic-gate 		return;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	case MOUNTPROC_MNT:
8524a508a79SThomas Haynes 		(void) mount(rqstp);
8537c478bd9Sstevel@tonic-gate 		return;
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	case MOUNTPROC_DUMP:
85635a075c3SToomas Soome 		if (mountd_remote_dump || caller_is_local(transp))
85735a075c3SToomas Soome 			mntlist_send(transp);
85835a075c3SToomas Soome 		else
85935a075c3SToomas Soome 			svcerr_noproc(transp);
8607c478bd9Sstevel@tonic-gate 		return;
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	case MOUNTPROC_UMNT:
8637c478bd9Sstevel@tonic-gate 		umount(rqstp);
8647c478bd9Sstevel@tonic-gate 		return;
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	case MOUNTPROC_UMNTALL:
8677c478bd9Sstevel@tonic-gate 		umountall(rqstp);
8687c478bd9Sstevel@tonic-gate 		return;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	case MOUNTPROC_EXPORT:
8717c478bd9Sstevel@tonic-gate 	case MOUNTPROC_EXPORTALL:
8727c478bd9Sstevel@tonic-gate 		export(rqstp);
8737c478bd9Sstevel@tonic-gate 		return;
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	case MOUNTPROC_PATHCONF:
8767c478bd9Sstevel@tonic-gate 		if (rqstp->rq_vers == MOUNTVERS_POSIX)
8777c478bd9Sstevel@tonic-gate 			mnt_pathconf(rqstp);
8787c478bd9Sstevel@tonic-gate 		else
8797c478bd9Sstevel@tonic-gate 			svcerr_noproc(transp);
8807c478bd9Sstevel@tonic-gate 		return;
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	default:
8837c478bd9Sstevel@tonic-gate 		svcerr_noproc(transp);
8847c478bd9Sstevel@tonic-gate 		return;
8857c478bd9Sstevel@tonic-gate 	}
8867c478bd9Sstevel@tonic-gate }
8877c478bd9Sstevel@tonic-gate 
888a9685eaaSMarcel Telka void
log_cant_reply_cln(struct cln * cln)889a9685eaaSMarcel Telka log_cant_reply_cln(struct cln *cln)
8907c478bd9Sstevel@tonic-gate {
891a9685eaaSMarcel Telka 	int saverrno;
892a9685eaaSMarcel Telka 	char *host;
8937c478bd9Sstevel@tonic-gate 
894a9685eaaSMarcel Telka 	saverrno = errno;	/* save error code */
8957c478bd9Sstevel@tonic-gate 
896a9685eaaSMarcel Telka 	host = cln_gethost(cln);
8977c478bd9Sstevel@tonic-gate 	if (host == NULL)
898a9685eaaSMarcel Telka 		return;
8994a508a79SThomas Haynes 
900a9685eaaSMarcel Telka 	errno = saverrno;
901a9685eaaSMarcel Telka 	if (errno == 0)
902a9685eaaSMarcel Telka 		syslog(LOG_ERR, "couldn't send reply to %s", host);
903a9685eaaSMarcel Telka 	else
904a9685eaaSMarcel Telka 		syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
9057c478bd9Sstevel@tonic-gate }
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate void
log_cant_reply(SVCXPRT * transp)9087c478bd9Sstevel@tonic-gate log_cant_reply(SVCXPRT *transp)
9097c478bd9Sstevel@tonic-gate {
9107c478bd9Sstevel@tonic-gate 	int saverrno;
911a9685eaaSMarcel Telka 	struct cln cln;
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	saverrno = errno;	/* save error code */
914a9685eaaSMarcel Telka 	cln_init(&cln, transp);
9157c478bd9Sstevel@tonic-gate 	errno = saverrno;
9167c478bd9Sstevel@tonic-gate 
917a9685eaaSMarcel Telka 	log_cant_reply_cln(&cln);
918a9685eaaSMarcel Telka 
919a9685eaaSMarcel Telka 	cln_fini(&cln);
9207c478bd9Sstevel@tonic-gate }
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate /*
9237c478bd9Sstevel@tonic-gate  * Answer pathconf questions for the mount point fs
9247c478bd9Sstevel@tonic-gate  */
9257c478bd9Sstevel@tonic-gate static void
mnt_pathconf(struct svc_req * rqstp)9267c478bd9Sstevel@tonic-gate mnt_pathconf(struct svc_req *rqstp)
9277c478bd9Sstevel@tonic-gate {
9287c478bd9Sstevel@tonic-gate 	SVCXPRT *transp;
9297c478bd9Sstevel@tonic-gate 	struct pathcnf p;
9307c478bd9Sstevel@tonic-gate 	char *path, rpath[MAXPATHLEN];
9317c478bd9Sstevel@tonic-gate 	struct stat st;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	transp = rqstp->rq_xprt;
9347c478bd9Sstevel@tonic-gate 	path = NULL;
9357c478bd9Sstevel@tonic-gate 	(void) memset((caddr_t)&p, 0, sizeof (p));
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
9387c478bd9Sstevel@tonic-gate 		svcerr_decode(transp);
9397c478bd9Sstevel@tonic-gate 		return;
9407c478bd9Sstevel@tonic-gate 	}
9417c478bd9Sstevel@tonic-gate 	if (lstat(path, &st) < 0) {
9427c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_ERROR, p.pc_mask);
9437c478bd9Sstevel@tonic-gate 		goto done;
9447c478bd9Sstevel@tonic-gate 	}
9457c478bd9Sstevel@tonic-gate 	/*
9467c478bd9Sstevel@tonic-gate 	 * Get a path without symbolic links.
9477c478bd9Sstevel@tonic-gate 	 */
9487c478bd9Sstevel@tonic-gate 	if (realpath(path, rpath) == NULL) {
9497c478bd9Sstevel@tonic-gate 		syslog(LOG_DEBUG,
950250a0733Sth 		    "mount request: realpath failed on %s: %m",
951250a0733Sth 		    path);
9527c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_ERROR, p.pc_mask);
9537c478bd9Sstevel@tonic-gate 		goto done;
9547c478bd9Sstevel@tonic-gate 	}
9557c478bd9Sstevel@tonic-gate 	(void) memset((caddr_t)&p, 0, sizeof (p));
9567c478bd9Sstevel@tonic-gate 	/*
9577c478bd9Sstevel@tonic-gate 	 * can't ask about devices over NFS
9587c478bd9Sstevel@tonic-gate 	 */
9597c478bd9Sstevel@tonic-gate 	_PC_SET(_PC_MAX_CANON, p.pc_mask);
9607c478bd9Sstevel@tonic-gate 	_PC_SET(_PC_MAX_INPUT, p.pc_mask);
9617c478bd9Sstevel@tonic-gate 	_PC_SET(_PC_PIPE_BUF, p.pc_mask);
9627c478bd9Sstevel@tonic-gate 	_PC_SET(_PC_VDISABLE, p.pc_mask);
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	errno = 0;
9657c478bd9Sstevel@tonic-gate 	p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
9667c478bd9Sstevel@tonic-gate 	if (errno)
9677c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_LINK_MAX, p.pc_mask);
9687c478bd9Sstevel@tonic-gate 	p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
9697c478bd9Sstevel@tonic-gate 	if (errno)
9707c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_NAME_MAX, p.pc_mask);
9717c478bd9Sstevel@tonic-gate 	p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
9727c478bd9Sstevel@tonic-gate 	if (errno)
9737c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_PATH_MAX, p.pc_mask);
9747c478bd9Sstevel@tonic-gate 	if (pathconf(rpath, _PC_NO_TRUNC) == 1)
9757c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_NO_TRUNC, p.pc_mask);
9767c478bd9Sstevel@tonic-gate 	if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
9777c478bd9Sstevel@tonic-gate 		_PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate done:
9807c478bd9Sstevel@tonic-gate 	errno = 0;
9817c478bd9Sstevel@tonic-gate 	if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
9827c478bd9Sstevel@tonic-gate 		log_cant_reply(transp);
9837c478bd9Sstevel@tonic-gate 	if (path != NULL)
984544783caSToomas Soome 		(void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
9857c478bd9Sstevel@tonic-gate }
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate /*
9887c478bd9Sstevel@tonic-gate  * If the rootmount (export) option is specified, the all mount requests for
9897c478bd9Sstevel@tonic-gate  * subdirectories return EACCES.
9907c478bd9Sstevel@tonic-gate  */
9917c478bd9Sstevel@tonic-gate static int
checkrootmount(share_t * sh,char * rpath)9924a508a79SThomas Haynes checkrootmount(share_t *sh, char *rpath)
9937c478bd9Sstevel@tonic-gate {
9947c478bd9Sstevel@tonic-gate 	char *val;
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 	if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
9977c478bd9Sstevel@tonic-gate 		free(val);
9987c478bd9Sstevel@tonic-gate 		if (strcmp(sh->sh_path, rpath) != 0)
9997c478bd9Sstevel@tonic-gate 			return (0);
10007c478bd9Sstevel@tonic-gate 		else
10017c478bd9Sstevel@tonic-gate 			return (1);
10027c478bd9Sstevel@tonic-gate 	} else
10037c478bd9Sstevel@tonic-gate 		return (1);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate #define	MAX_FLAVORS	128
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate /*
10097c478bd9Sstevel@tonic-gate  * Return only EACCES if client does not have access
10107c478bd9Sstevel@tonic-gate  *  to this directory.
10117c478bd9Sstevel@tonic-gate  * "If the server exports only /a/b, an attempt to
10127c478bd9Sstevel@tonic-gate  *  mount a/b/c will fail with ENOENT if the directory
10137c478bd9Sstevel@tonic-gate  *  does not exist"... However, if the client
10147c478bd9Sstevel@tonic-gate  *  does not have access to /a/b, an attacker can
10157c478bd9Sstevel@tonic-gate  *  determine whether the directory exists.
10167c478bd9Sstevel@tonic-gate  * This routine checks either existence of the file or
10177c478bd9Sstevel@tonic-gate  * existence of the file name entry in the mount table.
10187c478bd9Sstevel@tonic-gate  * If the file exists and there is no file name entry,
10197c478bd9Sstevel@tonic-gate  * the error returned should be EACCES.
10207c478bd9Sstevel@tonic-gate  * If the file does not exist, it must be determined
10217c478bd9Sstevel@tonic-gate  * whether the client has access to a parent
10227c478bd9Sstevel@tonic-gate  * directory.  If the client has access to a parent
10237c478bd9Sstevel@tonic-gate  * directory, the error returned should be ENOENT,
10247c478bd9Sstevel@tonic-gate  * otherwise EACCES.
10257c478bd9Sstevel@tonic-gate  */
10267c478bd9Sstevel@tonic-gate static int
mount_enoent_error(struct cln * cln,char * path,char * rpath,int * flavor_list)1027a9685eaaSMarcel Telka mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
10287c478bd9Sstevel@tonic-gate {
10297c478bd9Sstevel@tonic-gate 	char *checkpath, *dp;
10304a508a79SThomas Haynes 	share_t *sh = NULL;
10317c478bd9Sstevel@tonic-gate 	int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
10327c478bd9Sstevel@tonic-gate 	int flavor_count;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	checkpath = strdup(path);
10357c478bd9Sstevel@tonic-gate 	if (checkpath == NULL) {
10367c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "mount_enoent: no memory");
10377c478bd9Sstevel@tonic-gate 		return (EACCES);
10387c478bd9Sstevel@tonic-gate 	}
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	/* CONSTCOND */
10417c478bd9Sstevel@tonic-gate 	while (1) {
10427c478bd9Sstevel@tonic-gate 		if (sh) {
10437c478bd9Sstevel@tonic-gate 			sharefree(sh);
10447c478bd9Sstevel@tonic-gate 			sh = NULL;
10457c478bd9Sstevel@tonic-gate 		}
10464a508a79SThomas Haynes 
10477c478bd9Sstevel@tonic-gate 		if ((sh = findentry(rpath)) == NULL &&
1048250a0733Sth 		    (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
10497c478bd9Sstevel@tonic-gate 			/*
10507c478bd9Sstevel@tonic-gate 			 * There is no file name entry.
10517c478bd9Sstevel@tonic-gate 			 * If the file (with symbolic links resolved) exists,
10527c478bd9Sstevel@tonic-gate 			 * the error returned should be EACCES.
10537c478bd9Sstevel@tonic-gate 			 */
10547c478bd9Sstevel@tonic-gate 			if (realpath_error == 0)
10557c478bd9Sstevel@tonic-gate 				break;
10567c478bd9Sstevel@tonic-gate 		} else if (checkrootmount(sh, rpath) == 0) {
10577c478bd9Sstevel@tonic-gate 			/*
10587c478bd9Sstevel@tonic-gate 			 * This is a "nosub" only export, in which case,
10597c478bd9Sstevel@tonic-gate 			 * mounting subdirectories isn't allowed.
10607c478bd9Sstevel@tonic-gate 			 * If the file (with symbolic links resolved) exists,
10617c478bd9Sstevel@tonic-gate 			 * the error returned should be EACCES.
10627c478bd9Sstevel@tonic-gate 			 */
10637c478bd9Sstevel@tonic-gate 			if (realpath_error == 0)
10647c478bd9Sstevel@tonic-gate 				break;
10657c478bd9Sstevel@tonic-gate 		} else {
10667c478bd9Sstevel@tonic-gate 			/*
10677c478bd9Sstevel@tonic-gate 			 * Check permissions in mount table.
10687c478bd9Sstevel@tonic-gate 			 */
10697c478bd9Sstevel@tonic-gate 			if (newopts(sh->sh_opts))
1070a9685eaaSMarcel Telka 				flavor_count = getclientsflavors_new(sh, cln,
1071a9685eaaSMarcel Telka 				    flavor_list);
10727c478bd9Sstevel@tonic-gate 			else
1073a9685eaaSMarcel Telka 				flavor_count = getclientsflavors_old(sh, cln,
1074a9685eaaSMarcel Telka 				    flavor_list);
10757c478bd9Sstevel@tonic-gate 			if (flavor_count != 0) {
10767c478bd9Sstevel@tonic-gate 				/*
10777c478bd9Sstevel@tonic-gate 				 * Found entry in table and
10787c478bd9Sstevel@tonic-gate 				 * client has correct permissions.
10797c478bd9Sstevel@tonic-gate 				 */
10807c478bd9Sstevel@tonic-gate 				reply_error = ENOENT;
10817c478bd9Sstevel@tonic-gate 				break;
10827c478bd9Sstevel@tonic-gate 			}
10837c478bd9Sstevel@tonic-gate 		}
10844a508a79SThomas Haynes 
10857c478bd9Sstevel@tonic-gate 		/*
10867c478bd9Sstevel@tonic-gate 		 * Check all parent directories.
10877c478bd9Sstevel@tonic-gate 		 */
10887c478bd9Sstevel@tonic-gate 		dp = strrchr(checkpath, '/');
10897c478bd9Sstevel@tonic-gate 		if (dp == NULL)
10907c478bd9Sstevel@tonic-gate 			break;
10917c478bd9Sstevel@tonic-gate 		*dp = '\0';
10927c478bd9Sstevel@tonic-gate 		if (strlen(checkpath) == 0)
10937c478bd9Sstevel@tonic-gate 			break;
10947c478bd9Sstevel@tonic-gate 		/*
10957c478bd9Sstevel@tonic-gate 		 * Get the real path (no symbolic links in it)
10967c478bd9Sstevel@tonic-gate 		 */
10977c478bd9Sstevel@tonic-gate 		if (realpath(checkpath, rpath) == NULL) {
10987c478bd9Sstevel@tonic-gate 			if (errno != ENOENT)
10997c478bd9Sstevel@tonic-gate 				break;
11007c478bd9Sstevel@tonic-gate 		} else {
11017c478bd9Sstevel@tonic-gate 			realpath_error = 0;
11027c478bd9Sstevel@tonic-gate 		}
11037c478bd9Sstevel@tonic-gate 	}
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 	if (sh)
11067c478bd9Sstevel@tonic-gate 		sharefree(sh);
11077c478bd9Sstevel@tonic-gate 	free(checkpath);
11087c478bd9Sstevel@tonic-gate 	return (reply_error);
11097c478bd9Sstevel@tonic-gate }
11107c478bd9Sstevel@tonic-gate 
11114a508a79SThomas Haynes /*
11124a508a79SThomas Haynes  * We need to inform the caller whether or not we were
11134a508a79SThomas Haynes  * able to add a node to the queue. If we are not, then
11144a508a79SThomas Haynes  * it is up to the caller to go ahead and log the data.
11154a508a79SThomas Haynes  */
11164a508a79SThomas Haynes static int
enqueue_logging_data(char * host,SVCXPRT * transp,char * path,char * rpath,int status,int error)11174a508a79SThomas Haynes enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
11184a508a79SThomas Haynes     char *rpath, int status, int error)
11194a508a79SThomas Haynes {
11204a508a79SThomas Haynes 	logging_data	*lq;
11214a508a79SThomas Haynes 	struct netbuf	*nb;
11224a508a79SThomas Haynes 
11234a508a79SThomas Haynes 	lq = (logging_data *)calloc(1, sizeof (logging_data));
11244a508a79SThomas Haynes 	if (lq == NULL)
11254a508a79SThomas Haynes 		goto cleanup;
11264a508a79SThomas Haynes 
11274a508a79SThomas Haynes 	/*
11284a508a79SThomas Haynes 	 * We might not yet have the host...
11294a508a79SThomas Haynes 	 */
11304a508a79SThomas Haynes 	if (host) {
11314a508a79SThomas Haynes 		DTRACE_PROBE1(mountd, log_host, host);
11324a508a79SThomas Haynes 		lq->ld_host = strdup(host);
11334a508a79SThomas Haynes 		if (lq->ld_host == NULL)
11344a508a79SThomas Haynes 			goto cleanup;
11354a508a79SThomas Haynes 	} else {
11364a508a79SThomas Haynes 		DTRACE_PROBE(mountd, log_no_host);
11374a508a79SThomas Haynes 
11384a508a79SThomas Haynes 		lq->ld_netid = strdup(transp->xp_netid);
11394a508a79SThomas Haynes 		if (lq->ld_netid == NULL)
11404a508a79SThomas Haynes 			goto cleanup;
11414a508a79SThomas Haynes 
11424a508a79SThomas Haynes 		lq->ld_nb = calloc(1, sizeof (struct netbuf));
11434a508a79SThomas Haynes 		if (lq->ld_nb == NULL)
11444a508a79SThomas Haynes 			goto cleanup;
11454a508a79SThomas Haynes 
11464a508a79SThomas Haynes 		nb = svc_getrpccaller(transp);
11474a508a79SThomas Haynes 		if (nb == NULL) {
11484a508a79SThomas Haynes 			DTRACE_PROBE(mountd, e__nb__enqueue);
11494a508a79SThomas Haynes 			goto cleanup;
11504a508a79SThomas Haynes 		}
11514a508a79SThomas Haynes 
11524a508a79SThomas Haynes 		DTRACE_PROBE(mountd, nb_set_enqueue);
11534a508a79SThomas Haynes 
11544a508a79SThomas Haynes 		lq->ld_nb->maxlen = nb->maxlen;
11554a508a79SThomas Haynes 		lq->ld_nb->len = nb->len;
11564a508a79SThomas Haynes 
11574a508a79SThomas Haynes 		lq->ld_nb->buf = malloc(lq->ld_nb->len);
11584a508a79SThomas Haynes 		if (lq->ld_nb->buf == NULL)
11594a508a79SThomas Haynes 			goto cleanup;
11604a508a79SThomas Haynes 
11614a508a79SThomas Haynes 		bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
11624a508a79SThomas Haynes 	}
11634a508a79SThomas Haynes 
11644a508a79SThomas Haynes 	lq->ld_path = strdup(path);
11654a508a79SThomas Haynes 	if (lq->ld_path == NULL)
11664a508a79SThomas Haynes 		goto cleanup;
11674a508a79SThomas Haynes 
11684a508a79SThomas Haynes 	if (!error) {
11694a508a79SThomas Haynes 		lq->ld_rpath = strdup(rpath);
11704a508a79SThomas Haynes 		if (lq->ld_rpath == NULL)
11714a508a79SThomas Haynes 			goto cleanup;
11724a508a79SThomas Haynes 	}
11734a508a79SThomas Haynes 
11744a508a79SThomas Haynes 	lq->ld_status = status;
11754a508a79SThomas Haynes 
11764a508a79SThomas Haynes 	/*
11774a508a79SThomas Haynes 	 * Add to the tail of the logging queue.
11784a508a79SThomas Haynes 	 */
11794a508a79SThomas Haynes 	(void) mutex_lock(&logging_queue_lock);
11804a508a79SThomas Haynes 	if (logging_tail == NULL) {
11814a508a79SThomas Haynes 		logging_tail = logging_head = lq;
11824a508a79SThomas Haynes 	} else {
11834a508a79SThomas Haynes 		logging_tail->ld_next = lq;
11844a508a79SThomas Haynes 		logging_tail = lq;
11854a508a79SThomas Haynes 	}
11864a508a79SThomas Haynes 	(void) cond_signal(&logging_queue_cv);
11874a508a79SThomas Haynes 	(void) mutex_unlock(&logging_queue_lock);
11884a508a79SThomas Haynes 
11894a508a79SThomas Haynes 	return (TRUE);
11904a508a79SThomas Haynes 
11914a508a79SThomas Haynes cleanup:
11924a508a79SThomas Haynes 
11934a508a79SThomas Haynes 	free_logging_data(lq);
11944a508a79SThomas Haynes 
11954a508a79SThomas Haynes 	return (FALSE);
11964a508a79SThomas Haynes }
11974a508a79SThomas Haynes 
1198a9685eaaSMarcel Telka 
1199a9685eaaSMarcel Telka #define	CLN_CLNAMES	(1 << 0)
1200a9685eaaSMarcel Telka #define	CLN_HOST	(1 << 1)
1201a9685eaaSMarcel Telka 
1202a9685eaaSMarcel Telka static void
cln_init_common(struct cln * cln,SVCXPRT * transp,char * netid,struct netbuf * nbuf)1203a9685eaaSMarcel Telka cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1204a9685eaaSMarcel Telka     struct netbuf *nbuf)
1205a9685eaaSMarcel Telka {
1206a9685eaaSMarcel Telka 	if ((cln->transp = transp) != NULL) {
1207a9685eaaSMarcel Telka 		assert(netid == NULL && nbuf == NULL);
1208a9685eaaSMarcel Telka 		cln->netid = transp->xp_netid;
1209a9685eaaSMarcel Telka 		cln->nbuf = svc_getrpccaller(transp);
1210a9685eaaSMarcel Telka 	} else {
1211a9685eaaSMarcel Telka 		cln->netid = netid;
1212a9685eaaSMarcel Telka 		cln->nbuf = nbuf;
1213a9685eaaSMarcel Telka 	}
1214a9685eaaSMarcel Telka 
1215a9685eaaSMarcel Telka 	cln->nconf = NULL;
1216a9685eaaSMarcel Telka 	cln->clnames = NULL;
1217a9685eaaSMarcel Telka 	cln->host = NULL;
1218a9685eaaSMarcel Telka 
1219a9685eaaSMarcel Telka 	cln->flags = 0;
1220a9685eaaSMarcel Telka }
1221a9685eaaSMarcel Telka 
1222a9685eaaSMarcel Telka void
cln_init(struct cln * cln,SVCXPRT * transp)1223a9685eaaSMarcel Telka cln_init(struct cln *cln, SVCXPRT *transp)
1224a9685eaaSMarcel Telka {
1225a9685eaaSMarcel Telka 	cln_init_common(cln, transp, NULL, NULL);
1226a9685eaaSMarcel Telka }
1227a9685eaaSMarcel Telka 
1228a9685eaaSMarcel Telka void
cln_init_lazy(struct cln * cln,char * netid,struct netbuf * nbuf)1229a9685eaaSMarcel Telka cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1230a9685eaaSMarcel Telka {
1231a9685eaaSMarcel Telka 	cln_init_common(cln, NULL, netid, nbuf);
1232a9685eaaSMarcel Telka }
1233a9685eaaSMarcel Telka 
1234a9685eaaSMarcel Telka void
cln_fini(struct cln * cln)1235a9685eaaSMarcel Telka cln_fini(struct cln *cln)
1236a9685eaaSMarcel Telka {
1237a9685eaaSMarcel Telka 	if (cln->nconf != NULL)
1238a9685eaaSMarcel Telka 		freenetconfigent(cln->nconf);
1239a9685eaaSMarcel Telka 
1240a9685eaaSMarcel Telka 	if (cln->clnames != NULL)
1241a9685eaaSMarcel Telka 		netdir_free(cln->clnames, ND_HOSTSERVLIST);
1242a9685eaaSMarcel Telka 
1243a9685eaaSMarcel Telka 	free(cln->host);
1244a9685eaaSMarcel Telka }
1245a9685eaaSMarcel Telka 
1246a9685eaaSMarcel Telka struct netbuf *
cln_getnbuf(struct cln * cln)1247a9685eaaSMarcel Telka cln_getnbuf(struct cln *cln)
1248a9685eaaSMarcel Telka {
1249a9685eaaSMarcel Telka 	return (cln->nbuf);
1250a9685eaaSMarcel Telka }
1251a9685eaaSMarcel Telka 
1252a9685eaaSMarcel Telka struct nd_hostservlist *
cln_getclientsnames(struct cln * cln)1253a9685eaaSMarcel Telka cln_getclientsnames(struct cln *cln)
1254a9685eaaSMarcel Telka {
1255a9685eaaSMarcel Telka 	if ((cln->flags & CLN_CLNAMES) == 0) {
1256a9685eaaSMarcel Telka 		/*
1257a9685eaaSMarcel Telka 		 * nconf is not needed if we do not have nbuf (see
1258a9685eaaSMarcel Telka 		 * cln_gethost() too), so we check for nbuf and in a case it is
1259a9685eaaSMarcel Telka 		 * NULL we do not try to get nconf.
1260a9685eaaSMarcel Telka 		 */
1261a9685eaaSMarcel Telka 		if (cln->netid != NULL && cln->nbuf != NULL) {
1262a9685eaaSMarcel Telka 			cln->nconf = getnetconfigent(cln->netid);
1263a9685eaaSMarcel Telka 			if (cln->nconf == NULL)
1264a9685eaaSMarcel Telka 				syslog(LOG_ERR, "%s: getnetconfigent failed",
1265a9685eaaSMarcel Telka 				    cln->netid);
1266a9685eaaSMarcel Telka 		}
1267a9685eaaSMarcel Telka 
1268a9685eaaSMarcel Telka 		if (cln->nconf != NULL && cln->nbuf != NULL)
1269a9685eaaSMarcel Telka 			(void) __netdir_getbyaddr_nosrv(cln->nconf,
1270a9685eaaSMarcel Telka 			    &cln->clnames, cln->nbuf);
1271a9685eaaSMarcel Telka 
1272a9685eaaSMarcel Telka 		cln->flags |= CLN_CLNAMES;
1273a9685eaaSMarcel Telka 	}
1274a9685eaaSMarcel Telka 
1275a9685eaaSMarcel Telka 	return (cln->clnames);
1276a9685eaaSMarcel Telka }
1277a9685eaaSMarcel Telka 
1278a9685eaaSMarcel Telka /*
1279a9685eaaSMarcel Telka  * Return B_TRUE if the host is already available at no cost
1280a9685eaaSMarcel Telka  */
1281a9685eaaSMarcel Telka boolean_t
cln_havehost(struct cln * cln)1282a9685eaaSMarcel Telka cln_havehost(struct cln *cln)
1283a9685eaaSMarcel Telka {
1284a9685eaaSMarcel Telka 	return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1285a9685eaaSMarcel Telka }
1286a9685eaaSMarcel Telka 
1287a9685eaaSMarcel Telka char *
cln_gethost(struct cln * cln)1288a9685eaaSMarcel Telka cln_gethost(struct cln *cln)
1289a9685eaaSMarcel Telka {
1290a9685eaaSMarcel Telka 	if (cln_getclientsnames(cln) != NULL)
1291a9685eaaSMarcel Telka 		return (cln->clnames->h_hostservs[0].h_host);
1292a9685eaaSMarcel Telka 
1293a9685eaaSMarcel Telka 	if ((cln->flags & CLN_HOST) == 0) {
1294a9685eaaSMarcel Telka 		if (cln->nconf == NULL || cln->nbuf == NULL) {
1295a9685eaaSMarcel Telka 			cln->host = strdup("(anon)");
1296a9685eaaSMarcel Telka 		} else {
1297a9685eaaSMarcel Telka 			char host[MAXIPADDRLEN];
1298a9685eaaSMarcel Telka 
1299a9685eaaSMarcel Telka 			if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1300a9685eaaSMarcel Telka 				struct sockaddr_in *sa;
1301a9685eaaSMarcel Telka 
1302a9685eaaSMarcel Telka 				/* LINTED pointer alignment */
1303a9685eaaSMarcel Telka 				sa = (struct sockaddr_in *)(cln->nbuf->buf);
1304a9685eaaSMarcel Telka 				(void) inet_ntoa_r(sa->sin_addr, host);
1305a9685eaaSMarcel Telka 
1306a9685eaaSMarcel Telka 				cln->host = strdup(host);
1307a9685eaaSMarcel Telka 			} else if (strcmp(cln->nconf->nc_protofmly,
1308a9685eaaSMarcel Telka 			    NC_INET6) == 0) {
1309a9685eaaSMarcel Telka 				struct sockaddr_in6 *sa;
1310a9685eaaSMarcel Telka 
1311a9685eaaSMarcel Telka 				/* LINTED pointer alignment */
1312a9685eaaSMarcel Telka 				sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1313a9685eaaSMarcel Telka 				(void) inet_ntop(AF_INET6,
1314a9685eaaSMarcel Telka 				    sa->sin6_addr.s6_addr,
1315a9685eaaSMarcel Telka 				    host, INET6_ADDRSTRLEN);
1316a9685eaaSMarcel Telka 
1317a9685eaaSMarcel Telka 				cln->host = strdup(host);
1318a9685eaaSMarcel Telka 			} else {
1319a9685eaaSMarcel Telka 				syslog(LOG_ERR, gettext("Client's address is "
1320a9685eaaSMarcel Telka 				    "neither IPv4 nor IPv6"));
1321a9685eaaSMarcel Telka 
1322a9685eaaSMarcel Telka 				cln->host = strdup("(anon)");
1323a9685eaaSMarcel Telka 			}
1324a9685eaaSMarcel Telka 		}
1325a9685eaaSMarcel Telka 
1326a9685eaaSMarcel Telka 		cln->flags |= CLN_HOST;
1327a9685eaaSMarcel Telka 	}
1328a9685eaaSMarcel Telka 
1329a9685eaaSMarcel Telka 	return (cln->host);
1330a9685eaaSMarcel Telka }
1331a9685eaaSMarcel Telka 
13327c478bd9Sstevel@tonic-gate /*
13337c478bd9Sstevel@tonic-gate  * Check mount requests, add to mounted list if ok
13347c478bd9Sstevel@tonic-gate  */
13354a508a79SThomas Haynes static int
mount(struct svc_req * rqstp)13367c478bd9Sstevel@tonic-gate mount(struct svc_req *rqstp)
13377c478bd9Sstevel@tonic-gate {
13387c478bd9Sstevel@tonic-gate 	SVCXPRT *transp;
133927242a7cSthurlow 	int version, vers;
13407c478bd9Sstevel@tonic-gate 	struct fhstatus fhs;
13417c478bd9Sstevel@tonic-gate 	struct mountres3 mountres3;
134227242a7cSthurlow 	char fh[FHSIZE3];
134327242a7cSthurlow 	int len = FHSIZE3;
13447c478bd9Sstevel@tonic-gate 	char *path, rpath[MAXPATHLEN];
13454a508a79SThomas Haynes 	share_t *sh = NULL;
1346a9685eaaSMarcel Telka 	struct cln cln;
13477c478bd9Sstevel@tonic-gate 	char *host = NULL;
13484a508a79SThomas Haynes 	int error = 0, lofs_tried = 0, enqueued;
13497c478bd9Sstevel@tonic-gate 	int flavor_list[MAX_FLAVORS];
13507c478bd9Sstevel@tonic-gate 	int flavor_count;
135103986916Sjarrett 	ucred_t	*uc = NULL;
13527c478bd9Sstevel@tonic-gate 
13534a508a79SThomas Haynes 	int audit_status;
13544a508a79SThomas Haynes 
13557c478bd9Sstevel@tonic-gate 	transp = rqstp->rq_xprt;
13567c478bd9Sstevel@tonic-gate 	version = rqstp->rq_vers;
13577c478bd9Sstevel@tonic-gate 	path = NULL;
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
13607c478bd9Sstevel@tonic-gate 		svcerr_decode(transp);
13614a508a79SThomas Haynes 		return (EACCES);
13627c478bd9Sstevel@tonic-gate 	}
13637c478bd9Sstevel@tonic-gate 
1364a9685eaaSMarcel Telka 	cln_init(&cln, transp);
1365a9685eaaSMarcel Telka 
13664a508a79SThomas Haynes 	/*
13674a508a79SThomas Haynes 	 * Put off getting the name for the client until we
13684a508a79SThomas Haynes 	 * need it. This is a performance gain. If we are logging,
13694a508a79SThomas Haynes 	 * then we don't care about performance and might as well
13704a508a79SThomas Haynes 	 * get the host name now in case we need to spit out an
13714a508a79SThomas Haynes 	 * error message.
13724a508a79SThomas Haynes 	 */
13734a508a79SThomas Haynes 	if (verbose) {
13744a508a79SThomas Haynes 		DTRACE_PROBE(mountd, name_by_verbose);
1375a9685eaaSMarcel Telka 		if ((host = cln_gethost(&cln)) == NULL) {
13764a508a79SThomas Haynes 			/*
13774a508a79SThomas Haynes 			 * We failed to get a name for the client, even
13784a508a79SThomas Haynes 			 * 'anon', probably because we ran out of memory.
13794a508a79SThomas Haynes 			 * In this situation it doesn't make sense to
13804a508a79SThomas Haynes 			 * allow the mount to succeed.
13814a508a79SThomas Haynes 			 */
13824a508a79SThomas Haynes 			error = EACCES;
13834a508a79SThomas Haynes 			goto reply;
13844a508a79SThomas Haynes 		}
13857c478bd9Sstevel@tonic-gate 	}
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate 	/*
13887c478bd9Sstevel@tonic-gate 	 * If the version being used is less than the minimum version,
13897c478bd9Sstevel@tonic-gate 	 * the filehandle translation should not be provided to the
13907c478bd9Sstevel@tonic-gate 	 * client.
13917c478bd9Sstevel@tonic-gate 	 */
13927c478bd9Sstevel@tonic-gate 	if (rejecting || version < mount_vers_min) {
13937c478bd9Sstevel@tonic-gate 		if (verbose)
13947c478bd9Sstevel@tonic-gate 			syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1395250a0733Sth 			    host, path);
13967c478bd9Sstevel@tonic-gate 		error = EACCES;
13977c478bd9Sstevel@tonic-gate 		goto reply;
13987c478bd9Sstevel@tonic-gate 	}
13997c478bd9Sstevel@tonic-gate 
140045916cd2Sjpk 	/*
140103986916Sjarrett 	 * Trusted Extension doesn't support nfsv2. nfsv2 client
140203986916Sjarrett 	 * uses MOUNT protocol v1 and v2. To prevent circumventing
140303986916Sjarrett 	 * TX label policy via using nfsv2 client, reject a mount
140403986916Sjarrett 	 * request with version less than 3 and log an error.
140545916cd2Sjpk 	 */
140645916cd2Sjpk 	if (is_system_labeled()) {
140703986916Sjarrett 		if (version < 3) {
140803986916Sjarrett 			if (verbose)
140903986916Sjarrett 				syslog(LOG_ERR,
141003986916Sjarrett 				    "Rejected mount: TX doesn't support NFSv2");
141103986916Sjarrett 			error = EACCES;
141203986916Sjarrett 			goto reply;
141303986916Sjarrett 		}
141445916cd2Sjpk 	}
141545916cd2Sjpk 
14167c478bd9Sstevel@tonic-gate 	/*
14177c478bd9Sstevel@tonic-gate 	 * Get the real path (no symbolic links in it)
14187c478bd9Sstevel@tonic-gate 	 */
14197c478bd9Sstevel@tonic-gate 	if (realpath(path, rpath) == NULL) {
14207c478bd9Sstevel@tonic-gate 		error = errno;
14217c478bd9Sstevel@tonic-gate 		if (verbose)
14227c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR,
1423250a0733Sth 			    "mount request: realpath: %s: %m", path);
14247c478bd9Sstevel@tonic-gate 		if (error == ENOENT)
1425a9685eaaSMarcel Telka 			error = mount_enoent_error(&cln, path, rpath,
1426a9685eaaSMarcel Telka 			    flavor_list);
14277c478bd9Sstevel@tonic-gate 		goto reply;
14287c478bd9Sstevel@tonic-gate 	}
14297c478bd9Sstevel@tonic-gate 
14307c478bd9Sstevel@tonic-gate 	if ((sh = findentry(rpath)) == NULL &&
1431250a0733Sth 	    (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
14327c478bd9Sstevel@tonic-gate 		error = EACCES;
14337c478bd9Sstevel@tonic-gate 		goto reply;
14347c478bd9Sstevel@tonic-gate 	}
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	/*
14377c478bd9Sstevel@tonic-gate 	 * Check if this is a "nosub" only export, in which case, mounting
14387c478bd9Sstevel@tonic-gate 	 * subdirectories isn't allowed. Bug 1184573.
14397c478bd9Sstevel@tonic-gate 	 */
14407c478bd9Sstevel@tonic-gate 	if (checkrootmount(sh, rpath) == 0) {
14417c478bd9Sstevel@tonic-gate 		error = EACCES;
14427c478bd9Sstevel@tonic-gate 		goto reply;
14437c478bd9Sstevel@tonic-gate 	}
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	if (newopts(sh->sh_opts))
1446a9685eaaSMarcel Telka 		flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
14477c478bd9Sstevel@tonic-gate 	else
1448a9685eaaSMarcel Telka 		flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
14494a508a79SThomas Haynes 
14507c478bd9Sstevel@tonic-gate 	if (flavor_count == 0) {
14517c478bd9Sstevel@tonic-gate 		error = EACCES;
14527c478bd9Sstevel@tonic-gate 		goto reply;
14537c478bd9Sstevel@tonic-gate 	}
14547c478bd9Sstevel@tonic-gate 
145503986916Sjarrett 	/*
145603986916Sjarrett 	 * Check MAC policy here. The server side policy should be
145703986916Sjarrett 	 * consistent with client side mount policy, i.e.
145803986916Sjarrett 	 * - we disallow an admin_low unlabeled client to mount
145903986916Sjarrett 	 * - we disallow mount from a lower labeled client.
146003986916Sjarrett 	 */
146103986916Sjarrett 	if (is_system_labeled()) {
146203986916Sjarrett 		m_label_t *clabel = NULL;
146303986916Sjarrett 		m_label_t *slabel = NULL;
146403986916Sjarrett 		m_label_t admin_low;
146503986916Sjarrett 
146603986916Sjarrett 		if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
146703986916Sjarrett 			syslog(LOG_ERR,
146803986916Sjarrett 			    "mount request: Failed to get caller's ucred : %m");
146903986916Sjarrett 			error = EACCES;
147003986916Sjarrett 			goto reply;
147103986916Sjarrett 		}
147203986916Sjarrett 		if ((clabel = ucred_getlabel(uc)) == NULL) {
147303986916Sjarrett 			syslog(LOG_ERR,
147403986916Sjarrett 			    "mount request: can't get client label from ucred");
147503986916Sjarrett 			error = EACCES;
147603986916Sjarrett 			goto reply;
147703986916Sjarrett 		}
147803986916Sjarrett 
147903986916Sjarrett 		bsllow(&admin_low);
148003986916Sjarrett 		if (blequal(&admin_low, clabel)) {
148103986916Sjarrett 			struct sockaddr *ca;
148203986916Sjarrett 			tsol_tpent_t	*tp;
148303986916Sjarrett 
148403986916Sjarrett 			ca = (struct sockaddr *)(void *)svc_getrpccaller(
148503986916Sjarrett 			    rqstp->rq_xprt)->buf;
148603986916Sjarrett 			if (ca == NULL) {
148703986916Sjarrett 				error = EACCES;
148803986916Sjarrett 				goto reply;
148903986916Sjarrett 			}
149003986916Sjarrett 			/*
149103986916Sjarrett 			 * get trusted network template associated
149203986916Sjarrett 			 * with the client.
149303986916Sjarrett 			 */
149403986916Sjarrett 			tp = get_client_template(ca);
149503986916Sjarrett 			if (tp == NULL || tp->host_type != SUN_CIPSO) {
149603986916Sjarrett 				if (tp != NULL)
149703986916Sjarrett 					tsol_freetpent(tp);
149803986916Sjarrett 				error = EACCES;
149903986916Sjarrett 				goto reply;
150003986916Sjarrett 			}
150103986916Sjarrett 			tsol_freetpent(tp);
150203986916Sjarrett 		} else {
150303986916Sjarrett 			if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
150403986916Sjarrett 				error = EACCES;
150503986916Sjarrett 				goto reply;
150603986916Sjarrett 			}
150703986916Sjarrett 
150803986916Sjarrett 			if (getlabel(rpath, slabel) != 0) {
150903986916Sjarrett 				m_label_free(slabel);
151003986916Sjarrett 				error = EACCES;
151103986916Sjarrett 				goto reply;
151203986916Sjarrett 			}
151303986916Sjarrett 
151403986916Sjarrett 			if (!bldominates(clabel, slabel)) {
151503986916Sjarrett 				m_label_free(slabel);
151603986916Sjarrett 				error = EACCES;
151703986916Sjarrett 				goto reply;
151803986916Sjarrett 			}
151903986916Sjarrett 			m_label_free(slabel);
152003986916Sjarrett 		}
152103986916Sjarrett 	}
152203986916Sjarrett 
15237c478bd9Sstevel@tonic-gate 	/*
15247c478bd9Sstevel@tonic-gate 	 * Now get the filehandle.
15257c478bd9Sstevel@tonic-gate 	 *
152627242a7cSthurlow 	 * NFS V2 clients get a 32 byte filehandle.
152727242a7cSthurlow 	 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
152827242a7cSthurlow 	 * the embedded FIDs.
15297c478bd9Sstevel@tonic-gate 	 */
153027242a7cSthurlow 	vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
15317c478bd9Sstevel@tonic-gate 
15327c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
153327242a7cSthurlow 	while (nfs_getfh(rpath, vers, &len, fh) < 0) {
15347c478bd9Sstevel@tonic-gate 		if (errno == EINVAL &&
1535250a0733Sth 		    (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
15367c478bd9Sstevel@tonic-gate 			errno = 0;
15377c478bd9Sstevel@tonic-gate 			continue;
15387c478bd9Sstevel@tonic-gate 		}
15397c478bd9Sstevel@tonic-gate 		error = errno == EINVAL ? EACCES : errno;
15407c478bd9Sstevel@tonic-gate 		syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1541250a0733Sth 		    path);
15427c478bd9Sstevel@tonic-gate 		break;
15437c478bd9Sstevel@tonic-gate 	}
15447c478bd9Sstevel@tonic-gate 
154527242a7cSthurlow 	if (version == MOUNTVERS3) {
154627242a7cSthurlow 		mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
154727242a7cSthurlow 		mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
154827242a7cSthurlow 	} else {
154927242a7cSthurlow 		bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
155027242a7cSthurlow 	}
155127242a7cSthurlow 
15527c478bd9Sstevel@tonic-gate reply:
155303986916Sjarrett 	if (uc != NULL)
155403986916Sjarrett 		ucred_free(uc);
15554a508a79SThomas Haynes 
15567c478bd9Sstevel@tonic-gate 	switch (version) {
15577c478bd9Sstevel@tonic-gate 	case MOUNTVERS:
15587c478bd9Sstevel@tonic-gate 	case MOUNTVERS_POSIX:
15597c478bd9Sstevel@tonic-gate 		if (error == EINVAL)
15607c478bd9Sstevel@tonic-gate 			fhs.fhs_status = NFSERR_ACCES;
15617c478bd9Sstevel@tonic-gate 		else if (error == EREMOTE)
15627c478bd9Sstevel@tonic-gate 			fhs.fhs_status = NFSERR_REMOTE;
15637c478bd9Sstevel@tonic-gate 		else
15647c478bd9Sstevel@tonic-gate 			fhs.fhs_status = error;
15654a508a79SThomas Haynes 
15667c478bd9Sstevel@tonic-gate 		if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1567a9685eaaSMarcel Telka 			log_cant_reply_cln(&cln);
15684a508a79SThomas Haynes 
15694a508a79SThomas Haynes 		audit_status = fhs.fhs_status;
15707c478bd9Sstevel@tonic-gate 		break;
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate 	case MOUNTVERS3:
15737c478bd9Sstevel@tonic-gate 		if (!error) {
15747c478bd9Sstevel@tonic-gate 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1575250a0733Sth 		    flavor_list;
15767c478bd9Sstevel@tonic-gate 		mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1577250a0733Sth 		    flavor_count;
15787c478bd9Sstevel@tonic-gate 
15797c478bd9Sstevel@tonic-gate 		} else if (error == ENAMETOOLONG)
15807c478bd9Sstevel@tonic-gate 			error = MNT3ERR_NAMETOOLONG;
15817c478bd9Sstevel@tonic-gate 
15827c478bd9Sstevel@tonic-gate 		mountres3.fhs_status = error;
15837c478bd9Sstevel@tonic-gate 		if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1584a9685eaaSMarcel Telka 			log_cant_reply_cln(&cln);
15857c478bd9Sstevel@tonic-gate 
15864a508a79SThomas Haynes 		audit_status = mountres3.fhs_status;
15877c478bd9Sstevel@tonic-gate 		break;
15887c478bd9Sstevel@tonic-gate 	}
15897c478bd9Sstevel@tonic-gate 
1590a9685eaaSMarcel Telka 	if (cln_havehost(&cln))
1591a9685eaaSMarcel Telka 		host = cln_gethost(&cln);
1592a9685eaaSMarcel Telka 
15937c478bd9Sstevel@tonic-gate 	if (verbose)
15947c478bd9Sstevel@tonic-gate 		syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1595250a0733Sth 		    (host == NULL) ? "unknown host" : host,
1596250a0733Sth 		    error ? "denied" : "mounted", path);
15977c478bd9Sstevel@tonic-gate 
15984a508a79SThomas Haynes 	/*
15994a508a79SThomas Haynes 	 * If we can not create a queue entry, go ahead and do it
16004a508a79SThomas Haynes 	 * in the context of this thread.
16014a508a79SThomas Haynes 	 */
16024a508a79SThomas Haynes 	enqueued = enqueue_logging_data(host, transp, path, rpath,
16034a508a79SThomas Haynes 	    audit_status, error);
16044a508a79SThomas Haynes 	if (enqueued == FALSE) {
16054a508a79SThomas Haynes 		if (host == NULL) {
16064a508a79SThomas Haynes 			DTRACE_PROBE(mountd, name_by_in_thread);
1607a9685eaaSMarcel Telka 			host = cln_gethost(&cln);
16084a508a79SThomas Haynes 		}
16094a508a79SThomas Haynes 
16104a508a79SThomas Haynes 		DTRACE_PROBE(mountd, logged_in_thread);
16114a508a79SThomas Haynes 		audit_mountd_mount(host, path, audit_status); /* BSM */
16124a508a79SThomas Haynes 		if (!error)
16134a508a79SThomas Haynes 			mntlist_new(host, rpath); /* add entry to mount list */
16144a508a79SThomas Haynes 	}
16154a508a79SThomas Haynes 
16167c478bd9Sstevel@tonic-gate 	if (path != NULL)
1617544783caSToomas Soome 		(void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	if (sh)
16207c478bd9Sstevel@tonic-gate 		sharefree(sh);
1621a9685eaaSMarcel Telka 
1622a9685eaaSMarcel Telka 	cln_fini(&cln);
16234a508a79SThomas Haynes 
16244a508a79SThomas Haynes 	return (error);
16257c478bd9Sstevel@tonic-gate }
16267c478bd9Sstevel@tonic-gate 
16276b086bafSSam Falkner /*
16286b086bafSSam Falkner  * Determine whether two paths are within the same file system.
16296b086bafSSam Falkner  * Returns nonzero (true) if paths are the same, zero (false) if
16306b086bafSSam Falkner  * they are different.  If an error occurs, return false.
16316b086bafSSam Falkner  *
16326b086bafSSam Falkner  * Use the actual FSID if it's available (via getattrat()); otherwise,
16336b086bafSSam Falkner  * fall back on st_dev.
16346b086bafSSam Falkner  *
16356b086bafSSam Falkner  * With ZFS snapshots, st_dev differs from the regular file system
16366b086bafSSam Falkner  * versus the snapshot.  But the fsid is the same throughout.  Thus
16376b086bafSSam Falkner  * the fsid is a better test.
16386b086bafSSam Falkner  */
16396b086bafSSam Falkner static int
same_file_system(const char * path1,const char * path2)16406b086bafSSam Falkner same_file_system(const char *path1, const char *path2)
16416b086bafSSam Falkner {
16426b086bafSSam Falkner 	uint64_t fsid1, fsid2;
16436b086bafSSam Falkner 	struct stat64 st1, st2;
16446b086bafSSam Falkner 	nvlist_t *nvl1 = NULL;
16456b086bafSSam Falkner 	nvlist_t *nvl2 = NULL;
16466b086bafSSam Falkner 
16476b086bafSSam Falkner 	if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
16486b086bafSSam Falkner 	    (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
16496b086bafSSam Falkner 	    (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
16506b086bafSSam Falkner 	    (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
16516b086bafSSam Falkner 		nvlist_free(nvl1);
16526b086bafSSam Falkner 		nvlist_free(nvl2);
16536b086bafSSam Falkner 		/*
16546b086bafSSam Falkner 		 * We have found fsid's for both paths.
16556b086bafSSam Falkner 		 */
16566b086bafSSam Falkner 
16576b086bafSSam Falkner 		if (fsid1 == fsid2)
16586b086bafSSam Falkner 			return (B_TRUE);
16596b086bafSSam Falkner 
16606b086bafSSam Falkner 		return (B_FALSE);
16616b086bafSSam Falkner 	}
16626b086bafSSam Falkner 
1663aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(nvl1);
1664aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(nvl2);
16656b086bafSSam Falkner 
16666b086bafSSam Falkner 	/*
16676b086bafSSam Falkner 	 * We were unable to find fsid's for at least one of the paths.
16686b086bafSSam Falkner 	 * fall back on st_dev.
16696b086bafSSam Falkner 	 */
16706b086bafSSam Falkner 
16716b086bafSSam Falkner 	if (stat64(path1, &st1) < 0) {
16726b086bafSSam Falkner 		syslog(LOG_NOTICE, "%s: %m", path1);
16736b086bafSSam Falkner 		return (B_FALSE);
16746b086bafSSam Falkner 	}
16756b086bafSSam Falkner 	if (stat64(path2, &st2) < 0) {
16766b086bafSSam Falkner 		syslog(LOG_NOTICE, "%s: %m", path2);
16776b086bafSSam Falkner 		return (B_FALSE);
16786b086bafSSam Falkner 	}
16796b086bafSSam Falkner 
16806b086bafSSam Falkner 	if (st1.st_dev == st2.st_dev)
16816b086bafSSam Falkner 		return (B_TRUE);
16826b086bafSSam Falkner 
16836b086bafSSam Falkner 	return (B_FALSE);
16846b086bafSSam Falkner }
16856b086bafSSam Falkner 
16864a508a79SThomas Haynes share_t *
findentry(char * path)16877c478bd9Sstevel@tonic-gate findentry(char *path)
16887c478bd9Sstevel@tonic-gate {
16894a508a79SThomas Haynes 	share_t *sh = NULL;
16907c478bd9Sstevel@tonic-gate 	struct sh_list *shp;
169154d34259SMarcel Telka 	char *p1, *p2;
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 	check_sharetab();
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 	(void) rw_rdlock(&sharetab_lock);
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 	for (shp = share_list; shp; shp = shp->shl_next) {
16987c478bd9Sstevel@tonic-gate 		sh = shp->shl_sh;
16997c478bd9Sstevel@tonic-gate 		for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
17007c478bd9Sstevel@tonic-gate 			if (*p1 == '\0')
17017c478bd9Sstevel@tonic-gate 				goto done;	/* exact match */
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate 		/*
17047c478bd9Sstevel@tonic-gate 		 * Now compare the pathnames for three cases:
17057c478bd9Sstevel@tonic-gate 		 *
17067c478bd9Sstevel@tonic-gate 		 * Parent: /export/foo		(no trailing slash on parent)
17077c478bd9Sstevel@tonic-gate 		 * Child:  /export/foo/bar
17087c478bd9Sstevel@tonic-gate 		 *
17097c478bd9Sstevel@tonic-gate 		 * Parent: /export/foo/		(trailing slash on parent)
17107c478bd9Sstevel@tonic-gate 		 * Child:  /export/foo/bar
17117c478bd9Sstevel@tonic-gate 		 *
17127c478bd9Sstevel@tonic-gate 		 * Parent: /export/foo/		(no trailing slash on child)
17137c478bd9Sstevel@tonic-gate 		 * Child:  /export/foo
17147c478bd9Sstevel@tonic-gate 		 */
17157c478bd9Sstevel@tonic-gate 		if ((*p1 == '\0' && *p2 == '/') ||
17167c478bd9Sstevel@tonic-gate 		    (*p1 == '\0' && *(p1-1) == '/') ||
17177c478bd9Sstevel@tonic-gate 		    (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
17187c478bd9Sstevel@tonic-gate 			/*
17196b086bafSSam Falkner 			 * We have a subdirectory.  Test whether the
17206b086bafSSam Falkner 			 * subdirectory is in the same file system.
17217c478bd9Sstevel@tonic-gate 			 */
17226b086bafSSam Falkner 			if (same_file_system(path, sh->sh_path))
17237c478bd9Sstevel@tonic-gate 				goto done;
17247c478bd9Sstevel@tonic-gate 		}
17257c478bd9Sstevel@tonic-gate 	}
17267c478bd9Sstevel@tonic-gate done:
17277c478bd9Sstevel@tonic-gate 	sh = shp ? sharedup(sh) : NULL;
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate 	(void) rw_unlock(&sharetab_lock);
17307c478bd9Sstevel@tonic-gate 
17317c478bd9Sstevel@tonic-gate 	return (sh);
17327c478bd9Sstevel@tonic-gate }
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate static int
is_substring(char ** mntp,char ** path)17367c478bd9Sstevel@tonic-gate is_substring(char **mntp, char **path)
17377c478bd9Sstevel@tonic-gate {
17387c478bd9Sstevel@tonic-gate 	char *p1 = *mntp, *p2 = *path;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 	if (*p1 == '\0' && *p2 == '\0') /* exact match */
17417c478bd9Sstevel@tonic-gate 		return (1);
17427c478bd9Sstevel@tonic-gate 	else if (*p1 == '\0' && *p2 == '/')
17437c478bd9Sstevel@tonic-gate 		return (1);
17447c478bd9Sstevel@tonic-gate 	else if (*p1 == '\0' && *(p1-1) == '/') {
17457c478bd9Sstevel@tonic-gate 		*path = --p2; /* we need the slash in p2 */
17467c478bd9Sstevel@tonic-gate 		return (1);
17477c478bd9Sstevel@tonic-gate 	} else if (*p2 == '\0') {
17487c478bd9Sstevel@tonic-gate 		while (*p1 == '/')
17497c478bd9Sstevel@tonic-gate 			p1++;
17507c478bd9Sstevel@tonic-gate 		if (*p1 == '\0') /* exact match */
17517c478bd9Sstevel@tonic-gate 			return (1);
17527c478bd9Sstevel@tonic-gate 	}
17537c478bd9Sstevel@tonic-gate 	return (0);
17547c478bd9Sstevel@tonic-gate }
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate /*
17577c478bd9Sstevel@tonic-gate  * find_lofsentry() searches for the real path which this requested LOFS path
17587c478bd9Sstevel@tonic-gate  * (rpath) shadows. If found, it will return the sharetab entry of
17597c478bd9Sstevel@tonic-gate  * the real path that corresponds to the LOFS path.
17607c478bd9Sstevel@tonic-gate  * We first search mnttab to see if the requested path is an automounted
17617c478bd9Sstevel@tonic-gate  * path. If it is an automounted path, it will trigger the mount by stat()ing
17627c478bd9Sstevel@tonic-gate  * the requested path. Note that it is important to check that this path is
17637c478bd9Sstevel@tonic-gate  * actually an automounted path, otherwise we would stat() a path which may
17647c478bd9Sstevel@tonic-gate  * turn out to be NFS and block indefinitely on a dead server. The automounter
17657c478bd9Sstevel@tonic-gate  * times-out if the server is dead, so there's no risk of hanging this
17667c478bd9Sstevel@tonic-gate  * thread waiting for stat().
17677c478bd9Sstevel@tonic-gate  * After the mount has been triggered (if necessary), we look for a
17687c478bd9Sstevel@tonic-gate  * mountpoint of type LOFS (by searching /etc/mnttab again) which
17697c478bd9Sstevel@tonic-gate  * is a substring of the rpath. If found, we construct a new path by
17707c478bd9Sstevel@tonic-gate  * concatenating the mnt_special and the remaining of rpath, call findentry()
17717c478bd9Sstevel@tonic-gate  * to make sure the 'real path' is shared.
17727c478bd9Sstevel@tonic-gate  */
17734a508a79SThomas Haynes static share_t *
find_lofsentry(char * rpath,int * done_flag)17747c478bd9Sstevel@tonic-gate find_lofsentry(char *rpath, int *done_flag)
17757c478bd9Sstevel@tonic-gate {
17767c478bd9Sstevel@tonic-gate 	struct stat r_stbuf;
17777c478bd9Sstevel@tonic-gate 	mntlist_t *ml, *mntl, *mntpnt = NULL;
17784a508a79SThomas Haynes 	share_t *retcode = NULL;
17797c478bd9Sstevel@tonic-gate 	char tmp_path[MAXPATHLEN];
17807c478bd9Sstevel@tonic-gate 	int mntpnt_len = 0, tmp;
17817c478bd9Sstevel@tonic-gate 	char *p1, *p2;
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate 	if ((*done_flag)++)
17847c478bd9Sstevel@tonic-gate 		return (retcode);
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 	/*
17877c478bd9Sstevel@tonic-gate 	 * While fsgetmntlist() uses lockf() to
17887c478bd9Sstevel@tonic-gate 	 * lock the mnttab before reading it in,
17897c478bd9Sstevel@tonic-gate 	 * the lock ignores threads in the same process.
17907c478bd9Sstevel@tonic-gate 	 * Read in the mnttab with the protection of a mutex.
17917c478bd9Sstevel@tonic-gate 	 */
17927c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&mnttab_lock);
17937c478bd9Sstevel@tonic-gate 	mntl = fsgetmntlist();
17947c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&mnttab_lock);
17957c478bd9Sstevel@tonic-gate 
17967c478bd9Sstevel@tonic-gate 	/*
17977c478bd9Sstevel@tonic-gate 	 * Obtain the mountpoint for the requested path.
17987c478bd9Sstevel@tonic-gate 	 */
17997c478bd9Sstevel@tonic-gate 	for (ml = mntl; ml; ml = ml->mntl_next) {
18007c478bd9Sstevel@tonic-gate 		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1801250a0733Sth 		    *p1 == *p2 && *p1; p1++, p2++)
1802250a0733Sth 			;
18037c478bd9Sstevel@tonic-gate 		if (is_substring(&p1, &p2) &&
18047c478bd9Sstevel@tonic-gate 		    (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
18057c478bd9Sstevel@tonic-gate 			mntpnt = ml;
18067c478bd9Sstevel@tonic-gate 			mntpnt_len = tmp;
18077c478bd9Sstevel@tonic-gate 		}
18087c478bd9Sstevel@tonic-gate 	}
18097c478bd9Sstevel@tonic-gate 
18107c478bd9Sstevel@tonic-gate 	/*
18117c478bd9Sstevel@tonic-gate 	 * If the path needs to be autoFS mounted, trigger the mount by
18127c478bd9Sstevel@tonic-gate 	 * stat()ing it. This is determined by checking whether the
18137c478bd9Sstevel@tonic-gate 	 * mountpoint we just found is of type autofs.
18147c478bd9Sstevel@tonic-gate 	 */
18157c478bd9Sstevel@tonic-gate 	if (mntpnt != NULL &&
18167c478bd9Sstevel@tonic-gate 	    strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
18177c478bd9Sstevel@tonic-gate 		/*
18187c478bd9Sstevel@tonic-gate 		 * The requested path is a substring of an autoFS filesystem.
18197c478bd9Sstevel@tonic-gate 		 * Trigger the mount.
18207c478bd9Sstevel@tonic-gate 		 */
18217c478bd9Sstevel@tonic-gate 		if (stat(rpath, &r_stbuf) < 0) {
18227c478bd9Sstevel@tonic-gate 			if (verbose)
18237c478bd9Sstevel@tonic-gate 				syslog(LOG_NOTICE, "%s: %m", rpath);
18247c478bd9Sstevel@tonic-gate 			goto done;
18257c478bd9Sstevel@tonic-gate 		}
18267c478bd9Sstevel@tonic-gate 		if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
18277c478bd9Sstevel@tonic-gate 			/*
18287c478bd9Sstevel@tonic-gate 			 * The requested path is a directory, stat(2) it
18297c478bd9Sstevel@tonic-gate 			 * again with a trailing '.' to force the autoFS
18307c478bd9Sstevel@tonic-gate 			 * module to trigger the mount of indirect
18317c478bd9Sstevel@tonic-gate 			 * automount entries, such as /net/jurassic/.
18327c478bd9Sstevel@tonic-gate 			 */
18337c478bd9Sstevel@tonic-gate 			if (strlen(rpath) + 2 > MAXPATHLEN) {
18347c478bd9Sstevel@tonic-gate 				if (verbose) {
18357c478bd9Sstevel@tonic-gate 					syslog(LOG_NOTICE,
1836250a0733Sth 					    "%s/.: exceeds MAXPATHLEN %d",
1837250a0733Sth 					    rpath, MAXPATHLEN);
18387c478bd9Sstevel@tonic-gate 				}
18397c478bd9Sstevel@tonic-gate 				goto done;
18407c478bd9Sstevel@tonic-gate 			}
18417c478bd9Sstevel@tonic-gate 			(void) strcpy(tmp_path, rpath);
18427c478bd9Sstevel@tonic-gate 			(void) strcat(tmp_path, "/.");
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate 			if (stat(tmp_path, &r_stbuf) < 0) {
18457c478bd9Sstevel@tonic-gate 				if (verbose)
18467c478bd9Sstevel@tonic-gate 					syslog(LOG_NOTICE, "%s: %m", tmp_path);
18477c478bd9Sstevel@tonic-gate 				goto done;
18487c478bd9Sstevel@tonic-gate 			}
18497c478bd9Sstevel@tonic-gate 		}
18504a508a79SThomas Haynes 
18517c478bd9Sstevel@tonic-gate 		/*
18527c478bd9Sstevel@tonic-gate 		 * The mount has been triggered, re-read mnttab to pick up
18537c478bd9Sstevel@tonic-gate 		 * the changes made by autoFS.
18547c478bd9Sstevel@tonic-gate 		 */
18557c478bd9Sstevel@tonic-gate 		fsfreemntlist(mntl);
18567c478bd9Sstevel@tonic-gate 		(void) mutex_lock(&mnttab_lock);
18577c478bd9Sstevel@tonic-gate 		mntl = fsgetmntlist();
18587c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&mnttab_lock);
18597c478bd9Sstevel@tonic-gate 	}
18607c478bd9Sstevel@tonic-gate 
18617c478bd9Sstevel@tonic-gate 	/*
18627c478bd9Sstevel@tonic-gate 	 * The autoFS mountpoint has been triggered if necessary,
18637c478bd9Sstevel@tonic-gate 	 * now search mnttab again to determine if the requested path
18647c478bd9Sstevel@tonic-gate 	 * is an LOFS mount of a shared path.
18657c478bd9Sstevel@tonic-gate 	 */
18667c478bd9Sstevel@tonic-gate 	mntpnt_len = 0;
18677c478bd9Sstevel@tonic-gate 	for (ml = mntl; ml; ml = ml->mntl_next) {
18687c478bd9Sstevel@tonic-gate 		if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
18697c478bd9Sstevel@tonic-gate 			continue;
18707c478bd9Sstevel@tonic-gate 
18717c478bd9Sstevel@tonic-gate 		for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1872250a0733Sth 		    *p1 == *p2 && *p1; p1++, p2++)
1873250a0733Sth 			;
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 		if (is_substring(&p1, &p2) &&
18767c478bd9Sstevel@tonic-gate 		    ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
18777c478bd9Sstevel@tonic-gate 			mntpnt_len = tmp;
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 			if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
18807c478bd9Sstevel@tonic-gate 			    MAXPATHLEN) {
18817c478bd9Sstevel@tonic-gate 				if (verbose) {
18827c478bd9Sstevel@tonic-gate 					syslog(LOG_NOTICE, "%s%s: exceeds %d",
1883250a0733Sth 					    ml->mntl_mnt->mnt_special, p2,
1884250a0733Sth 					    MAXPATHLEN);
18857c478bd9Sstevel@tonic-gate 				}
18867c478bd9Sstevel@tonic-gate 				if (retcode)
18877c478bd9Sstevel@tonic-gate 					sharefree(retcode);
18887c478bd9Sstevel@tonic-gate 				retcode = NULL;
18897c478bd9Sstevel@tonic-gate 				goto done;
18907c478bd9Sstevel@tonic-gate 			}
18917c478bd9Sstevel@tonic-gate 
18927c478bd9Sstevel@tonic-gate 			(void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
18937c478bd9Sstevel@tonic-gate 			(void) strcat(tmp_path, p2);
18947c478bd9Sstevel@tonic-gate 			if (retcode)
18957c478bd9Sstevel@tonic-gate 				sharefree(retcode);
18967c478bd9Sstevel@tonic-gate 			retcode = findentry(tmp_path);
18977c478bd9Sstevel@tonic-gate 		}
18987c478bd9Sstevel@tonic-gate 	}
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	if (retcode) {
19017c478bd9Sstevel@tonic-gate 		assert(strlen(tmp_path) > 0);
19027c478bd9Sstevel@tonic-gate 		(void) strcpy(rpath, tmp_path);
19037c478bd9Sstevel@tonic-gate 	}
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate done:
19067c478bd9Sstevel@tonic-gate 	fsfreemntlist(mntl);
19077c478bd9Sstevel@tonic-gate 	return (retcode);
19087c478bd9Sstevel@tonic-gate }
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate /*
19117c478bd9Sstevel@tonic-gate  * Determine whether an access list grants rights to a particular host.
19127c478bd9Sstevel@tonic-gate  * We match on aliases of the hostname as well as on the canonical name.
19137c478bd9Sstevel@tonic-gate  * Names in the access list may be either hosts or netgroups;  they're
19147c478bd9Sstevel@tonic-gate  * not distinguished syntactically.  We check for hosts first because
19159b241b4eSYuri Pankov  * it's cheaper, then try netgroups.
1916d34083bdSJan Kryl  *
1917a9685eaaSMarcel Telka  * Return values:
1918a9685eaaSMarcel Telka  *  1 - access is granted
1919a9685eaaSMarcel Telka  *  0 - access is denied
1920a9685eaaSMarcel Telka  * -1 - an error occured
19217c478bd9Sstevel@tonic-gate  */
192211606941Sjwahlig int
in_access_list(struct cln * cln,char * access_list)1923a9685eaaSMarcel Telka in_access_list(struct cln *cln,
19247c478bd9Sstevel@tonic-gate     char *access_list)	/* N.B. we clobber this "input" parameter */
19257c478bd9Sstevel@tonic-gate {
19269b241b4eSYuri Pankov 	char addr[INET_ADDRSTRLEN];
19279b241b4eSYuri Pankov 	char buff[256];
19289b241b4eSYuri Pankov 	int nentries = 0;
19299b241b4eSYuri Pankov 	char *cstr = access_list;
19309b241b4eSYuri Pankov 	char *gr = access_list;
19317c478bd9Sstevel@tonic-gate 	int i;
19327c478bd9Sstevel@tonic-gate 	int response;
193303b59f78SYuri Pankov 	int ret;
1934a9685eaaSMarcel Telka 	struct netbuf *pnb;
1935a9685eaaSMarcel Telka 	struct nd_hostservlist *clnames = NULL;
19364a508a79SThomas Haynes 
19379b241b4eSYuri Pankov 	/* If no access list - then it's unrestricted */
19387c478bd9Sstevel@tonic-gate 	if (access_list == NULL || *access_list == '\0')
19397c478bd9Sstevel@tonic-gate 		return (1);
19407c478bd9Sstevel@tonic-gate 
1941a9685eaaSMarcel Telka 	if ((pnb = cln_getnbuf(cln)) == NULL)
1942a9685eaaSMarcel Telka 		return (-1);
19437c478bd9Sstevel@tonic-gate 
19449b241b4eSYuri Pankov 	for (;;) {
1945a9685eaaSMarcel Telka 		if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1946a9685eaaSMarcel Telka 			if (*cstr == ':') {
1947a9685eaaSMarcel Telka 				*cstr = '\0';
1948a9685eaaSMarcel Telka 			} else {
1949a9685eaaSMarcel Telka 				assert(*cstr == '[');
1950a9685eaaSMarcel Telka 				cstr = strchr(cstr + 1, ']');
1951a9685eaaSMarcel Telka 				if (cstr == NULL)
1952a9685eaaSMarcel Telka 					return (-1);
19539b241b4eSYuri Pankov 				cstr++;
19549b241b4eSYuri Pankov 				continue;
19559b241b4eSYuri Pankov 			}
19569b241b4eSYuri Pankov 		}
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 		/*
19599b241b4eSYuri Pankov 		 * If the list name has a '-' prepended then a match of
19609b241b4eSYuri Pankov 		 * the following name implies failure instead of success.
19617c478bd9Sstevel@tonic-gate 		 */
19627c478bd9Sstevel@tonic-gate 		if (*gr == '-') {
19637c478bd9Sstevel@tonic-gate 			response = 0;
19647c478bd9Sstevel@tonic-gate 			gr++;
19659b241b4eSYuri Pankov 		} else {
19667c478bd9Sstevel@tonic-gate 			response = 1;
19679b241b4eSYuri Pankov 		}
19687c478bd9Sstevel@tonic-gate 
19694a508a79SThomas Haynes 		/*
19709b241b4eSYuri Pankov 		 * First check if we have '@' entry, as it doesn't
19719b241b4eSYuri Pankov 		 * require client hostname.
19724a508a79SThomas Haynes 		 */
19734a508a79SThomas Haynes 		if (*gr == '@') {
19749b241b4eSYuri Pankov 			gr++;
19759b241b4eSYuri Pankov 
19769b241b4eSYuri Pankov 			/* Netname support */
19779b241b4eSYuri Pankov 			if (!isdigit(*gr) && *gr != '[') {
1978a9685eaaSMarcel Telka 				struct netent n, *np;
1979a9685eaaSMarcel Telka 
19809b241b4eSYuri Pankov 				if ((np = getnetbyname_r(gr, &n, buff,
19819b241b4eSYuri Pankov 				    sizeof (buff))) != NULL &&
19829b241b4eSYuri Pankov 				    np->n_net != 0) {
19839b241b4eSYuri Pankov 					while ((np->n_net & 0xFF000000u) == 0)
19849b241b4eSYuri Pankov 						np->n_net <<= 8;
19859b241b4eSYuri Pankov 					np->n_net = htonl(np->n_net);
19869b241b4eSYuri Pankov 					if (inet_ntop(AF_INET, &np->n_net, addr,
19879b241b4eSYuri Pankov 					    INET_ADDRSTRLEN) == NULL)
19889b241b4eSYuri Pankov 						break;
198903b59f78SYuri Pankov 					ret = inet_matchaddr(pnb->buf, addr);
199003b59f78SYuri Pankov 					if (ret == -1) {
199103b59f78SYuri Pankov 						if (errno == EINVAL) {
199203b59f78SYuri Pankov 							syslog(LOG_WARNING,
199303b59f78SYuri Pankov 							    "invalid access "
199403b59f78SYuri Pankov 							    "list entry: %s",
199503b59f78SYuri Pankov 							    addr);
199603b59f78SYuri Pankov 						}
199703b59f78SYuri Pankov 						return (-1);
199803b59f78SYuri Pankov 					} else if (ret == 1) {
19999b241b4eSYuri Pankov 						return (response);
200003b59f78SYuri Pankov 					}
20019b241b4eSYuri Pankov 				}
20029b241b4eSYuri Pankov 			} else {
200303b59f78SYuri Pankov 				ret = inet_matchaddr(pnb->buf, gr);
200403b59f78SYuri Pankov 				if (ret == -1) {
200503b59f78SYuri Pankov 					if (errno == EINVAL) {
200603b59f78SYuri Pankov 						syslog(LOG_WARNING,
200703b59f78SYuri Pankov 						    "invalid access list "
200803b59f78SYuri Pankov 						    "entry: %s", gr);
200903b59f78SYuri Pankov 					}
201003b59f78SYuri Pankov 					return (-1);
201103b59f78SYuri Pankov 				} else if (ret == 1) {
20129b241b4eSYuri Pankov 					return (response);
201303b59f78SYuri Pankov 				}
2014d34083bdSJan Kryl 			}
2015d34083bdSJan Kryl 
2016a9685eaaSMarcel Telka 			goto next;
20174a508a79SThomas Haynes 		}
20184a508a79SThomas Haynes 
20194a508a79SThomas Haynes 		/*
20209b241b4eSYuri Pankov 		 * No other checks can be performed if client address
20219b241b4eSYuri Pankov 		 * can't be resolved.
20224a508a79SThomas Haynes 		 */
2023a9685eaaSMarcel Telka 		if ((clnames = cln_getclientsnames(cln)) == NULL)
2024a9685eaaSMarcel Telka 			goto next;
20259b241b4eSYuri Pankov 
20269b241b4eSYuri Pankov 		/* Otherwise loop through all client hostname aliases */
20277c478bd9Sstevel@tonic-gate 		for (i = 0; i < clnames->h_cnt; i++) {
2028a9685eaaSMarcel Telka 			char *host = clnames->h_hostservs[i].h_host;
20297c478bd9Sstevel@tonic-gate 
20307c478bd9Sstevel@tonic-gate 			/*
20317c478bd9Sstevel@tonic-gate 			 * If the list name begins with a dot then
20327c478bd9Sstevel@tonic-gate 			 * do a domain name suffix comparison.
20337c478bd9Sstevel@tonic-gate 			 * A single dot matches any name with no
20347c478bd9Sstevel@tonic-gate 			 * suffix.
20357c478bd9Sstevel@tonic-gate 			 */
20367c478bd9Sstevel@tonic-gate 			if (*gr == '.') {
20377c478bd9Sstevel@tonic-gate 				if (*(gr + 1) == '\0') {  /* single dot */
20387c478bd9Sstevel@tonic-gate 					if (strchr(host, '.') == NULL)
20397c478bd9Sstevel@tonic-gate 						return (response);
20407c478bd9Sstevel@tonic-gate 				} else {
2041a9685eaaSMarcel Telka 					int off = strlen(host) - strlen(gr);
20427c478bd9Sstevel@tonic-gate 					if (off > 0 &&
20437c478bd9Sstevel@tonic-gate 					    strcasecmp(host + off, gr) == 0) {
20447c478bd9Sstevel@tonic-gate 						return (response);
20457c478bd9Sstevel@tonic-gate 					}
20467c478bd9Sstevel@tonic-gate 				}
20479b241b4eSYuri Pankov 			} else {
20489b241b4eSYuri Pankov 				/* Just do a hostname match */
20499b241b4eSYuri Pankov 				if (strcasecmp(gr, host) == 0)
20509b241b4eSYuri Pankov 					return (response);
20517c478bd9Sstevel@tonic-gate 			}
20527c478bd9Sstevel@tonic-gate 		}
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate 		nentries++;
20557c478bd9Sstevel@tonic-gate 
2056a9685eaaSMarcel Telka next:
20579b241b4eSYuri Pankov 		if (cstr == NULL)
20589b241b4eSYuri Pankov 			break;
20597c478bd9Sstevel@tonic-gate 
20609b241b4eSYuri Pankov 		gr = ++cstr;
20617c478bd9Sstevel@tonic-gate 	}
20627c478bd9Sstevel@tonic-gate 
20639b241b4eSYuri Pankov 	if (clnames == NULL)
20649b241b4eSYuri Pankov 		return (0);
20657c478bd9Sstevel@tonic-gate 
20669b241b4eSYuri Pankov 	return (netgroup_check(clnames, access_list, nentries));
20677c478bd9Sstevel@tonic-gate }
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 
20707c478bd9Sstevel@tonic-gate static char *optlist[] = {
20717c478bd9Sstevel@tonic-gate #define	OPT_RO		0
20727c478bd9Sstevel@tonic-gate 	SHOPT_RO,
20737c478bd9Sstevel@tonic-gate #define	OPT_RW		1
20747c478bd9Sstevel@tonic-gate 	SHOPT_RW,
20757c478bd9Sstevel@tonic-gate #define	OPT_ROOT	2
20767c478bd9Sstevel@tonic-gate 	SHOPT_ROOT,
20777c478bd9Sstevel@tonic-gate #define	OPT_SECURE	3
20787c478bd9Sstevel@tonic-gate 	SHOPT_SECURE,
20797c478bd9Sstevel@tonic-gate #define	OPT_ANON	4
20807c478bd9Sstevel@tonic-gate 	SHOPT_ANON,
20817c478bd9Sstevel@tonic-gate #define	OPT_WINDOW	5
20827c478bd9Sstevel@tonic-gate 	SHOPT_WINDOW,
20837c478bd9Sstevel@tonic-gate #define	OPT_NOSUID	6
20847c478bd9Sstevel@tonic-gate 	SHOPT_NOSUID,
20857c478bd9Sstevel@tonic-gate #define	OPT_ACLOK	7
20867c478bd9Sstevel@tonic-gate 	SHOPT_ACLOK,
20877c478bd9Sstevel@tonic-gate #define	OPT_SEC		8
20887c478bd9Sstevel@tonic-gate 	SHOPT_SEC,
2089b89a8333Snatalie li - Sun Microsystems - Irvine United States #define	OPT_NONE	9
2090b89a8333Snatalie li - Sun Microsystems - Irvine United States 	SHOPT_NONE,
20915cb0d679SMarcel Telka #define	OPT_UIDMAP	10
20925cb0d679SMarcel Telka 	SHOPT_UIDMAP,
20935cb0d679SMarcel Telka #define	OPT_GIDMAP	11
20945cb0d679SMarcel Telka 	SHOPT_GIDMAP,
20957c478bd9Sstevel@tonic-gate 	NULL
20967c478bd9Sstevel@tonic-gate };
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate static int
map_flavor(char * str)20997c478bd9Sstevel@tonic-gate map_flavor(char *str)
21007c478bd9Sstevel@tonic-gate {
21017c478bd9Sstevel@tonic-gate 	seconfig_t sec;
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 	if (nfs_getseconfig_byname(str, &sec))
21047c478bd9Sstevel@tonic-gate 		return (-1);
21057c478bd9Sstevel@tonic-gate 
21067c478bd9Sstevel@tonic-gate 	return (sec.sc_nfsnum);
21077c478bd9Sstevel@tonic-gate }
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate /*
21107c478bd9Sstevel@tonic-gate  * If the option string contains a "sec="
21117c478bd9Sstevel@tonic-gate  * option, then use new option syntax.
21127c478bd9Sstevel@tonic-gate  */
21137c478bd9Sstevel@tonic-gate static int
newopts(char * opts)21147c478bd9Sstevel@tonic-gate newopts(char *opts)
21157c478bd9Sstevel@tonic-gate {
21167c478bd9Sstevel@tonic-gate 	char *head, *p, *val;
21177c478bd9Sstevel@tonic-gate 
21187c478bd9Sstevel@tonic-gate 	if (!opts || *opts == '\0')
21197c478bd9Sstevel@tonic-gate 		return (0);
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate 	head = strdup(opts);
21227c478bd9Sstevel@tonic-gate 	if (head == NULL) {
21237c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "opts: no memory");
21247c478bd9Sstevel@tonic-gate 		return (0);
21257c478bd9Sstevel@tonic-gate 	}
21267c478bd9Sstevel@tonic-gate 
21277c478bd9Sstevel@tonic-gate 	p = head;
21287c478bd9Sstevel@tonic-gate 	while (*p) {
21297c478bd9Sstevel@tonic-gate 		if (getsubopt(&p, optlist, &val) == OPT_SEC) {
21307c478bd9Sstevel@tonic-gate 			free(head);
21317c478bd9Sstevel@tonic-gate 			return (1);
21327c478bd9Sstevel@tonic-gate 		}
21337c478bd9Sstevel@tonic-gate 	}
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate 	free(head);
21367c478bd9Sstevel@tonic-gate 	return (0);
21377c478bd9Sstevel@tonic-gate }
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate /*
21407c478bd9Sstevel@tonic-gate  * Given an export and the clients hostname(s)
21417c478bd9Sstevel@tonic-gate  * determine the security flavors that this
21427c478bd9Sstevel@tonic-gate  * client is permitted to use.
21437c478bd9Sstevel@tonic-gate  *
21447c478bd9Sstevel@tonic-gate  * This routine is called only for "old" syntax, i.e.
21457c478bd9Sstevel@tonic-gate  * only one security flavor is allowed.  So we need
21467c478bd9Sstevel@tonic-gate  * to determine two things: the particular flavor,
21477c478bd9Sstevel@tonic-gate  * and whether the client is allowed to use this
21487c478bd9Sstevel@tonic-gate  * flavor, i.e. is in the access list.
21497c478bd9Sstevel@tonic-gate  *
21507c478bd9Sstevel@tonic-gate  * Note that if there is no access list, then the
21517c478bd9Sstevel@tonic-gate  * default is that access is granted.
21527c478bd9Sstevel@tonic-gate  */
21537c478bd9Sstevel@tonic-gate static int
getclientsflavors_old(share_t * sh,struct cln * cln,int * flavors)2154a9685eaaSMarcel Telka getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
21557c478bd9Sstevel@tonic-gate {
21567c478bd9Sstevel@tonic-gate 	char *opts, *p, *val;
2157b89a8333Snatalie li - Sun Microsystems - Irvine United States 	boolean_t ok = B_FALSE;
21587c478bd9Sstevel@tonic-gate 	int defaultaccess = 1;
2159b89a8333Snatalie li - Sun Microsystems - Irvine United States 	boolean_t reject = B_FALSE;
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 	opts = strdup(sh->sh_opts);
21627c478bd9Sstevel@tonic-gate 	if (opts == NULL) {
21637c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "getclientsflavors: no memory");
21647c478bd9Sstevel@tonic-gate 		return (0);
21657c478bd9Sstevel@tonic-gate 	}
21667c478bd9Sstevel@tonic-gate 
21677c478bd9Sstevel@tonic-gate 	flavors[0] = AUTH_SYS;
21687c478bd9Sstevel@tonic-gate 	p = opts;
21697c478bd9Sstevel@tonic-gate 
21707c478bd9Sstevel@tonic-gate 	while (*p) {
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 		switch (getsubopt(&p, optlist, &val)) {
21737c478bd9Sstevel@tonic-gate 		case OPT_SECURE:
21747c478bd9Sstevel@tonic-gate 			flavors[0] = AUTH_DES;
21757c478bd9Sstevel@tonic-gate 			break;
21767c478bd9Sstevel@tonic-gate 
21777c478bd9Sstevel@tonic-gate 		case OPT_RO:
21787c478bd9Sstevel@tonic-gate 		case OPT_RW:
21797c478bd9Sstevel@tonic-gate 			defaultaccess = 0;
2180a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
2181a9685eaaSMarcel Telka 				ok = B_TRUE;
21827c478bd9Sstevel@tonic-gate 			break;
2183b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2184b89a8333Snatalie li - Sun Microsystems - Irvine United States 		case OPT_NONE:
2185b89a8333Snatalie li - Sun Microsystems - Irvine United States 			defaultaccess = 0;
2186a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
2187b89a8333Snatalie li - Sun Microsystems - Irvine United States 				reject = B_TRUE;
21887c478bd9Sstevel@tonic-gate 		}
21897c478bd9Sstevel@tonic-gate 	}
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 	free(opts);
21927c478bd9Sstevel@tonic-gate 
2193b89a8333Snatalie li - Sun Microsystems - Irvine United States 	/* none takes precedence over everything else */
2194b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if (reject)
2195a9685eaaSMarcel Telka 		ok = B_FALSE;
2196b89a8333Snatalie li - Sun Microsystems - Irvine United States 
21977c478bd9Sstevel@tonic-gate 	return (defaultaccess || ok);
21987c478bd9Sstevel@tonic-gate }
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate /*
22017c478bd9Sstevel@tonic-gate  * Given an export and the clients hostname(s)
22027c478bd9Sstevel@tonic-gate  * determine the security flavors that this
22037c478bd9Sstevel@tonic-gate  * client is permitted to use.
22047c478bd9Sstevel@tonic-gate  *
22057c478bd9Sstevel@tonic-gate  * This is somewhat more complicated than the "old"
22067c478bd9Sstevel@tonic-gate  * routine because the options may contain multiple
22077c478bd9Sstevel@tonic-gate  * security flavors (sec=) each with its own access
22087c478bd9Sstevel@tonic-gate  * lists.  So a client could be granted access based
22097c478bd9Sstevel@tonic-gate  * on a number of security flavors.  Note that the
22107c478bd9Sstevel@tonic-gate  * type of access might not always be the same, the
22117c478bd9Sstevel@tonic-gate  * client may get readonly access with one flavor
22127c478bd9Sstevel@tonic-gate  * and readwrite with another, however the client
22137c478bd9Sstevel@tonic-gate  * is not told this detail, it gets only the list
22147c478bd9Sstevel@tonic-gate  * of flavors, and only if the client is using
22157c478bd9Sstevel@tonic-gate  * version 3 of the mount protocol.
22167c478bd9Sstevel@tonic-gate  */
22177c478bd9Sstevel@tonic-gate static int
getclientsflavors_new(share_t * sh,struct cln * cln,int * flavors)2218a9685eaaSMarcel Telka getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
22197c478bd9Sstevel@tonic-gate {
22207c478bd9Sstevel@tonic-gate 	char *opts, *p, *val;
22217c478bd9Sstevel@tonic-gate 	char *lasts;
22227c478bd9Sstevel@tonic-gate 	char *f;
2223a9685eaaSMarcel Telka 	boolean_t defaultaccess = B_TRUE;	/* default access is rw */
2224a9685eaaSMarcel Telka 	boolean_t access_ok = B_FALSE;
2225cf7e209dSIgor Kozhukhov 	int count, c;
2226b89a8333Snatalie li - Sun Microsystems - Irvine United States 	boolean_t reject = B_FALSE;
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 	opts = strdup(sh->sh_opts);
22297c478bd9Sstevel@tonic-gate 	if (opts == NULL) {
22307c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "getclientsflavors: no memory");
22317c478bd9Sstevel@tonic-gate 		return (0);
22327c478bd9Sstevel@tonic-gate 	}
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 	p = opts;
2235cf7e209dSIgor Kozhukhov 	count = c = 0;
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 	while (*p) {
22387c478bd9Sstevel@tonic-gate 		switch (getsubopt(&p, optlist, &val)) {
22397c478bd9Sstevel@tonic-gate 		case OPT_SEC:
2240a9685eaaSMarcel Telka 			if (reject)
2241a9685eaaSMarcel Telka 				access_ok = B_FALSE;
2242a9685eaaSMarcel Telka 
22437c478bd9Sstevel@tonic-gate 			/*
22447c478bd9Sstevel@tonic-gate 			 * Before a new sec=xxx option, check if we need
22457c478bd9Sstevel@tonic-gate 			 * to move the c index back to the previous count.
22467c478bd9Sstevel@tonic-gate 			 */
2247a9685eaaSMarcel Telka 			if (!defaultaccess && !access_ok) {
22487c478bd9Sstevel@tonic-gate 				c = count;
22497c478bd9Sstevel@tonic-gate 			}
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 			/* get all the sec=f1[:f2] flavors */
2252a9685eaaSMarcel Telka 			while ((f = strtok_r(val, ":", &lasts)) != NULL) {
22537c478bd9Sstevel@tonic-gate 				flavors[c++] = map_flavor(f);
22547c478bd9Sstevel@tonic-gate 				val = NULL;
22557c478bd9Sstevel@tonic-gate 			}
22564a508a79SThomas Haynes 
22577c478bd9Sstevel@tonic-gate 			/* for a new sec=xxx option, default is rw access */
2258a9685eaaSMarcel Telka 			defaultaccess = B_TRUE;
2259a9685eaaSMarcel Telka 			access_ok = B_FALSE;
2260a9685eaaSMarcel Telka 			reject = B_FALSE;
22617c478bd9Sstevel@tonic-gate 			break;
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 		case OPT_RO:
22647c478bd9Sstevel@tonic-gate 		case OPT_RW:
2265a9685eaaSMarcel Telka 			defaultaccess = B_FALSE;
2266a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
2267b89a8333Snatalie li - Sun Microsystems - Irvine United States 				access_ok = B_TRUE;
22687c478bd9Sstevel@tonic-gate 			break;
2269b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2270b89a8333Snatalie li - Sun Microsystems - Irvine United States 		case OPT_NONE:
2271a9685eaaSMarcel Telka 			defaultaccess = B_FALSE;
2272a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
2273b89a8333Snatalie li - Sun Microsystems - Irvine United States 				reject = B_TRUE; /* none overides rw/ro */
2274b89a8333Snatalie li - Sun Microsystems - Irvine United States 			break;
22757c478bd9Sstevel@tonic-gate 		}
22767c478bd9Sstevel@tonic-gate 	}
22777c478bd9Sstevel@tonic-gate 
2278b89a8333Snatalie li - Sun Microsystems - Irvine United States 	if (reject)
2279b89a8333Snatalie li - Sun Microsystems - Irvine United States 		access_ok = B_FALSE;
2280b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2281a9685eaaSMarcel Telka 	if (!defaultaccess && !access_ok)
22827c478bd9Sstevel@tonic-gate 		c = count;
2283b89a8333Snatalie li - Sun Microsystems - Irvine United States 
22847c478bd9Sstevel@tonic-gate 	free(opts);
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate 	return (c);
22877c478bd9Sstevel@tonic-gate }
22887c478bd9Sstevel@tonic-gate 
22897c478bd9Sstevel@tonic-gate /*
22907c478bd9Sstevel@tonic-gate  * This is a tricky piece of code that parses the
22917c478bd9Sstevel@tonic-gate  * share options looking for a match on the auth
22927c478bd9Sstevel@tonic-gate  * flavor that the client is using. If it finds
22937c478bd9Sstevel@tonic-gate  * a match, then the client is given ro, rw, or
22947c478bd9Sstevel@tonic-gate  * no access depending whether it is in the access
22957c478bd9Sstevel@tonic-gate  * list.  There is a special case for "secure"
22967c478bd9Sstevel@tonic-gate  * flavor.  Other flavors are values of the new "sec=" option.
22977c478bd9Sstevel@tonic-gate  */
22987c478bd9Sstevel@tonic-gate int
check_client(share_t * sh,struct cln * cln,int flavor,uid_t clnt_uid,gid_t clnt_gid,uint_t clnt_ngids,gid_t * clnt_gids,uid_t * srv_uid,gid_t * srv_gid,uint_t * srv_ngids,gid_t ** srv_gids)2299a9685eaaSMarcel Telka check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2300a9685eaaSMarcel Telka     gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2301a9685eaaSMarcel Telka     gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
23027c478bd9Sstevel@tonic-gate {
23037c478bd9Sstevel@tonic-gate 	if (newopts(sh->sh_opts))
2304a9685eaaSMarcel Telka 		return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2305a9685eaaSMarcel Telka 		    clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2306a9685eaaSMarcel Telka 		    srv_gids));
23077c478bd9Sstevel@tonic-gate 	else
2308a9685eaaSMarcel Telka 		return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2309a9685eaaSMarcel Telka 		    clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2310a9685eaaSMarcel Telka 		    srv_gids));
231189621fe1SMarcel Telka }
231289621fe1SMarcel Telka 
231389621fe1SMarcel Telka extern int _getgroupsbymember(const char *, gid_t[], int, int);
231489621fe1SMarcel Telka 
231589621fe1SMarcel Telka /*
231689621fe1SMarcel Telka  * Get supplemental groups for uid
231789621fe1SMarcel Telka  */
231889621fe1SMarcel Telka static int
getusergroups(uid_t uid,uint_t * ngrps,gid_t ** grps)231989621fe1SMarcel Telka getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
232089621fe1SMarcel Telka {
232189621fe1SMarcel Telka 	struct passwd pwd;
232289621fe1SMarcel Telka 	char *pwbuf = alloca(pw_size);
232389621fe1SMarcel Telka 	gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
232489621fe1SMarcel Telka 	int tmpngrps;
232589621fe1SMarcel Telka 
232689621fe1SMarcel Telka 	if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
232789621fe1SMarcel Telka 		return (-1);
232889621fe1SMarcel Telka 
232989621fe1SMarcel Telka 	tmpgrps[0] = pwd.pw_gid;
233089621fe1SMarcel Telka 
233189621fe1SMarcel Telka 	tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
233289621fe1SMarcel Telka 	if (tmpngrps <= 0) {
233389621fe1SMarcel Telka 		syslog(LOG_WARNING,
233489621fe1SMarcel Telka 		    "getusergroups(): Unable to get groups for user %s",
233589621fe1SMarcel Telka 		    pwd.pw_name);
233689621fe1SMarcel Telka 
233789621fe1SMarcel Telka 		return (-1);
233889621fe1SMarcel Telka 	}
233989621fe1SMarcel Telka 
234089621fe1SMarcel Telka 	*grps = malloc(tmpngrps * sizeof (gid_t));
234189621fe1SMarcel Telka 	if (*grps == NULL) {
234289621fe1SMarcel Telka 		syslog(LOG_ERR,
234389621fe1SMarcel Telka 		    "getusergroups(): Memory allocation failed: %m");
234489621fe1SMarcel Telka 
234589621fe1SMarcel Telka 		return (-1);
234689621fe1SMarcel Telka 	}
234789621fe1SMarcel Telka 
234889621fe1SMarcel Telka 	*ngrps = tmpngrps;
234989621fe1SMarcel Telka 	(void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
235089621fe1SMarcel Telka 
235189621fe1SMarcel Telka 	return (0);
23525cb0d679SMarcel Telka }
23535cb0d679SMarcel Telka 
23545cb0d679SMarcel Telka /*
23555cb0d679SMarcel Telka  * is_a_number(number)
23565cb0d679SMarcel Telka  *
23575cb0d679SMarcel Telka  * is the string a number in one of the forms we want to use?
23585cb0d679SMarcel Telka  */
23595cb0d679SMarcel Telka 
23605cb0d679SMarcel Telka static int
is_a_number(char * number)23615cb0d679SMarcel Telka is_a_number(char *number)
23625cb0d679SMarcel Telka {
23635cb0d679SMarcel Telka 	int ret = 1;
23645cb0d679SMarcel Telka 	int hex = 0;
23655cb0d679SMarcel Telka 
23665cb0d679SMarcel Telka 	if (strncmp(number, "0x", 2) == 0) {
23675cb0d679SMarcel Telka 		number += 2;
23685cb0d679SMarcel Telka 		hex = 1;
23695cb0d679SMarcel Telka 	} else if (*number == '-') {
23705cb0d679SMarcel Telka 		number++; /* skip the minus */
23715cb0d679SMarcel Telka 	}
23725cb0d679SMarcel Telka 	while (ret == 1 && *number != '\0') {
23735cb0d679SMarcel Telka 		if (hex) {
23745cb0d679SMarcel Telka 			ret = isxdigit(*number++);
23755cb0d679SMarcel Telka 		} else {
23765cb0d679SMarcel Telka 			ret = isdigit(*number++);
23775cb0d679SMarcel Telka 		}
23785cb0d679SMarcel Telka 	}
23795cb0d679SMarcel Telka 	return (ret);
23805cb0d679SMarcel Telka }
23815cb0d679SMarcel Telka 
23825cb0d679SMarcel Telka static boolean_t
get_uid(char * value,uid_t * uid)23835cb0d679SMarcel Telka get_uid(char *value, uid_t *uid)
23845cb0d679SMarcel Telka {
23855cb0d679SMarcel Telka 	if (!is_a_number(value)) {
23865cb0d679SMarcel Telka 		struct passwd *pw;
23875cb0d679SMarcel Telka 		/*
23885cb0d679SMarcel Telka 		 * in this case it would have to be a
23895cb0d679SMarcel Telka 		 * user name
23905cb0d679SMarcel Telka 		 */
23915cb0d679SMarcel Telka 		pw = getpwnam(value);
23925cb0d679SMarcel Telka 		if (pw == NULL)
23935cb0d679SMarcel Telka 			return (B_FALSE);
23945cb0d679SMarcel Telka 		*uid = pw->pw_uid;
23955cb0d679SMarcel Telka 		endpwent();
23965cb0d679SMarcel Telka 	} else {
23975cb0d679SMarcel Telka 		uint64_t intval;
23985cb0d679SMarcel Telka 		intval = strtoull(value, NULL, 0);
23995cb0d679SMarcel Telka 		if (intval > UID_MAX && intval != -1)
24005cb0d679SMarcel Telka 			return (B_FALSE);
24015cb0d679SMarcel Telka 		*uid = (uid_t)intval;
24025cb0d679SMarcel Telka 	}
24035cb0d679SMarcel Telka 
24045cb0d679SMarcel Telka 	return (B_TRUE);
24055cb0d679SMarcel Telka }
24065cb0d679SMarcel Telka 
24075cb0d679SMarcel Telka static boolean_t
get_gid(char * value,gid_t * gid)24085cb0d679SMarcel Telka get_gid(char *value, gid_t *gid)
24095cb0d679SMarcel Telka {
24105cb0d679SMarcel Telka 	if (!is_a_number(value)) {
24115cb0d679SMarcel Telka 		struct group *gr;
24125cb0d679SMarcel Telka 		/*
24135cb0d679SMarcel Telka 		 * in this case it would have to be a
24145cb0d679SMarcel Telka 		 * group name
24155cb0d679SMarcel Telka 		 */
24165cb0d679SMarcel Telka 		gr = getgrnam(value);
24175cb0d679SMarcel Telka 		if (gr == NULL)
24185cb0d679SMarcel Telka 			return (B_FALSE);
24195cb0d679SMarcel Telka 		*gid = gr->gr_gid;
24205cb0d679SMarcel Telka 		endgrent();
24215cb0d679SMarcel Telka 	} else {
24225cb0d679SMarcel Telka 		uint64_t intval;
24235cb0d679SMarcel Telka 		intval = strtoull(value, NULL, 0);
24245cb0d679SMarcel Telka 		if (intval > UID_MAX && intval != -1)
24255cb0d679SMarcel Telka 			return (B_FALSE);
24265cb0d679SMarcel Telka 		*gid = (gid_t)intval;
24275cb0d679SMarcel Telka 	}
24285cb0d679SMarcel Telka 
24295cb0d679SMarcel Telka 	return (B_TRUE);
24307c478bd9Sstevel@tonic-gate }
24317c478bd9Sstevel@tonic-gate 
24327c478bd9Sstevel@tonic-gate static int
check_client_old(share_t * sh,struct cln * cln,int flavor,uid_t clnt_uid,gid_t clnt_gid,uint_t clnt_ngids,gid_t * clnt_gids,uid_t * srv_uid,gid_t * srv_gid,uint_t * srv_ngids,gid_t ** srv_gids)2433a9685eaaSMarcel Telka check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
243489621fe1SMarcel Telka     gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
243589621fe1SMarcel Telka     gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
24367c478bd9Sstevel@tonic-gate {
24377c478bd9Sstevel@tonic-gate 	char *opts, *p, *val;
24387c478bd9Sstevel@tonic-gate 	int match;	/* Set when a flavor is matched */
24397c478bd9Sstevel@tonic-gate 	int perm = 0;	/* Set when "ro", "rw" or "root" is matched */
24407c478bd9Sstevel@tonic-gate 	int list = 0;	/* Set when "ro", "rw" is found */
24417c478bd9Sstevel@tonic-gate 	int ro_val = 0;	/* Set if ro option is 'ro=' */
24427c478bd9Sstevel@tonic-gate 	int rw_val = 0;	/* Set if rw option is 'rw=' */
24435cb0d679SMarcel Telka 
24445cb0d679SMarcel Telka 	boolean_t map_deny = B_FALSE;
24457c478bd9Sstevel@tonic-gate 
24467c478bd9Sstevel@tonic-gate 	opts = strdup(sh->sh_opts);
24477c478bd9Sstevel@tonic-gate 	if (opts == NULL) {
24487c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "check_client: no memory");
24497c478bd9Sstevel@tonic-gate 		return (0);
24507c478bd9Sstevel@tonic-gate 	}
24517c478bd9Sstevel@tonic-gate 
245289621fe1SMarcel Telka 	/*
245389621fe1SMarcel Telka 	 * If client provided 16 supplemental groups with AUTH_SYS, lookup
245489621fe1SMarcel Telka 	 * locally for all of them
245589621fe1SMarcel Telka 	 */
245689621fe1SMarcel Telka 	if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
245789621fe1SMarcel Telka 		if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
245889621fe1SMarcel Telka 			perm |= NFSAUTH_GROUPS;
245989621fe1SMarcel Telka 
24607c478bd9Sstevel@tonic-gate 	p = opts;
24617c478bd9Sstevel@tonic-gate 	match = AUTH_UNIX;
24627c478bd9Sstevel@tonic-gate 
24637c478bd9Sstevel@tonic-gate 	while (*p) {
24647c478bd9Sstevel@tonic-gate 		switch (getsubopt(&p, optlist, &val)) {
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 		case OPT_SECURE:
24677c478bd9Sstevel@tonic-gate 			match = AUTH_DES;
246889621fe1SMarcel Telka 
246989621fe1SMarcel Telka 			if (perm & NFSAUTH_GROUPS) {
247089621fe1SMarcel Telka 				free(*srv_gids);
247189621fe1SMarcel Telka 				*srv_ngids = 0;
247289621fe1SMarcel Telka 				*srv_gids = NULL;
247389621fe1SMarcel Telka 				perm &= ~NFSAUTH_GROUPS;
247489621fe1SMarcel Telka 			}
247589621fe1SMarcel Telka 
24767c478bd9Sstevel@tonic-gate 			break;
24777c478bd9Sstevel@tonic-gate 
24787c478bd9Sstevel@tonic-gate 		case OPT_RO:
24797c478bd9Sstevel@tonic-gate 			list++;
24805cb0d679SMarcel Telka 			if (val != NULL)
24815cb0d679SMarcel Telka 				ro_val++;
2482a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
24837c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_RO;
24847c478bd9Sstevel@tonic-gate 			break;
24857c478bd9Sstevel@tonic-gate 
24867c478bd9Sstevel@tonic-gate 		case OPT_RW:
24877c478bd9Sstevel@tonic-gate 			list++;
24885cb0d679SMarcel Telka 			if (val != NULL)
24895cb0d679SMarcel Telka 				rw_val++;
2490a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
24917c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_RW;
24927c478bd9Sstevel@tonic-gate 			break;
24937c478bd9Sstevel@tonic-gate 
24947c478bd9Sstevel@tonic-gate 		case OPT_ROOT:
24957c478bd9Sstevel@tonic-gate 			/*
24967c478bd9Sstevel@tonic-gate 			 * Check if the client is in
24977c478bd9Sstevel@tonic-gate 			 * the root list. Only valid
24987c478bd9Sstevel@tonic-gate 			 * for AUTH_SYS.
24997c478bd9Sstevel@tonic-gate 			 */
25007c478bd9Sstevel@tonic-gate 			if (flavor != AUTH_SYS)
25017c478bd9Sstevel@tonic-gate 				break;
25027c478bd9Sstevel@tonic-gate 
25037c478bd9Sstevel@tonic-gate 			if (val == NULL || *val == '\0')
25047c478bd9Sstevel@tonic-gate 				break;
25057c478bd9Sstevel@tonic-gate 
25065cb0d679SMarcel Telka 			if (clnt_uid != 0)
25075cb0d679SMarcel Telka 				break;
25085cb0d679SMarcel Telka 
2509a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0) {
25107c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_ROOT;
25115cb0d679SMarcel Telka 				perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
25125cb0d679SMarcel Telka 				map_deny = B_FALSE;
251389621fe1SMarcel Telka 
251489621fe1SMarcel Telka 				if (perm & NFSAUTH_GROUPS) {
251589621fe1SMarcel Telka 					free(*srv_gids);
251689621fe1SMarcel Telka 					*srv_ngids = 0;
251789621fe1SMarcel Telka 					*srv_gids = NULL;
251889621fe1SMarcel Telka 					perm &= ~NFSAUTH_GROUPS;
251989621fe1SMarcel Telka 				}
25205cb0d679SMarcel Telka 			}
25217c478bd9Sstevel@tonic-gate 			break;
2522b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2523b89a8333Snatalie li - Sun Microsystems - Irvine United States 		case OPT_NONE:
2524b89a8333Snatalie li - Sun Microsystems - Irvine United States 			/*
252554d34259SMarcel Telka 			 * Check if the client should have no access
2526b89a8333Snatalie li - Sun Microsystems - Irvine United States 			 * to this share at all. This option behaves
2527b89a8333Snatalie li - Sun Microsystems - Irvine United States 			 * more like "root" than either "rw" or "ro".
2528b89a8333Snatalie li - Sun Microsystems - Irvine United States 			 */
2529a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
25305cb0d679SMarcel Telka 				perm |= NFSAUTH_DENIED;
25315cb0d679SMarcel Telka 			break;
25325cb0d679SMarcel Telka 
25335cb0d679SMarcel Telka 		case OPT_UIDMAP: {
25345cb0d679SMarcel Telka 			char *c;
25355cb0d679SMarcel Telka 			char *n;
25365cb0d679SMarcel Telka 
25375cb0d679SMarcel Telka 			/*
25385cb0d679SMarcel Telka 			 * The uidmap is supported for AUTH_SYS only.
25395cb0d679SMarcel Telka 			 */
25405cb0d679SMarcel Telka 			if (flavor != AUTH_SYS)
25415cb0d679SMarcel Telka 				break;
25425cb0d679SMarcel Telka 
25435cb0d679SMarcel Telka 			if (perm & NFSAUTH_UIDMAP || map_deny)
25445cb0d679SMarcel Telka 				break;
25455cb0d679SMarcel Telka 
25465cb0d679SMarcel Telka 			for (c = val; c != NULL; c = n) {
25475cb0d679SMarcel Telka 				char *s;
25485cb0d679SMarcel Telka 				char *al;
25495cb0d679SMarcel Telka 				uid_t srv;
25505cb0d679SMarcel Telka 
25515cb0d679SMarcel Telka 				n = strchr(c, '~');
25525cb0d679SMarcel Telka 				if (n != NULL)
25535cb0d679SMarcel Telka 					*n++ = '\0';
25545cb0d679SMarcel Telka 
25555cb0d679SMarcel Telka 				s = strchr(c, ':');
25565cb0d679SMarcel Telka 				if (s != NULL) {
25575cb0d679SMarcel Telka 					*s++ = '\0';
25585cb0d679SMarcel Telka 					al = strchr(s, ':');
25595cb0d679SMarcel Telka 					if (al != NULL)
25605cb0d679SMarcel Telka 						*al++ = '\0';
25615cb0d679SMarcel Telka 				}
25625cb0d679SMarcel Telka 
25635cb0d679SMarcel Telka 				if (s == NULL || al == NULL)
25645cb0d679SMarcel Telka 					continue;
25655cb0d679SMarcel Telka 
25665cb0d679SMarcel Telka 				if (*c == '\0') {
25675cb0d679SMarcel Telka 					if (clnt_uid != (uid_t)-1)
25685cb0d679SMarcel Telka 						continue;
25695cb0d679SMarcel Telka 				} else if (strcmp(c, "*") != 0) {
25705cb0d679SMarcel Telka 					uid_t clnt;
25715cb0d679SMarcel Telka 
25725cb0d679SMarcel Telka 					if (!get_uid(c, &clnt))
25735cb0d679SMarcel Telka 						continue;
25745cb0d679SMarcel Telka 
25755cb0d679SMarcel Telka 					if (clnt_uid != clnt)
25765cb0d679SMarcel Telka 						continue;
25775cb0d679SMarcel Telka 				}
25785cb0d679SMarcel Telka 
25795cb0d679SMarcel Telka 				if (*s == '\0')
25805cb0d679SMarcel Telka 					srv = UID_NOBODY;
25815cb0d679SMarcel Telka 				else if (!get_uid(s, &srv))
25825cb0d679SMarcel Telka 					continue;
25835cb0d679SMarcel Telka 				else if (srv == (uid_t)-1) {
25845cb0d679SMarcel Telka 					map_deny = B_TRUE;
25855cb0d679SMarcel Telka 					break;
25865cb0d679SMarcel Telka 				}
25875cb0d679SMarcel Telka 
2588a9685eaaSMarcel Telka 				if (in_access_list(cln, al) > 0) {
25895cb0d679SMarcel Telka 					*srv_uid = srv;
25905cb0d679SMarcel Telka 					perm |= NFSAUTH_UIDMAP;
259189621fe1SMarcel Telka 
259289621fe1SMarcel Telka 					if (perm & NFSAUTH_GROUPS) {
259389621fe1SMarcel Telka 						free(*srv_gids);
259489621fe1SMarcel Telka 						*srv_ngids = 0;
259589621fe1SMarcel Telka 						*srv_gids = NULL;
259689621fe1SMarcel Telka 						perm &= ~NFSAUTH_GROUPS;
259789621fe1SMarcel Telka 					}
259889621fe1SMarcel Telka 
25995cb0d679SMarcel Telka 					break;
26005cb0d679SMarcel Telka 				}
26015cb0d679SMarcel Telka 			}
26025cb0d679SMarcel Telka 
26035cb0d679SMarcel Telka 			break;
26045cb0d679SMarcel Telka 		}
26055cb0d679SMarcel Telka 
26065cb0d679SMarcel Telka 		case OPT_GIDMAP: {
26075cb0d679SMarcel Telka 			char *c;
26085cb0d679SMarcel Telka 			char *n;
26095cb0d679SMarcel Telka 
26105cb0d679SMarcel Telka 			/*
26115cb0d679SMarcel Telka 			 * The gidmap is supported for AUTH_SYS only.
26125cb0d679SMarcel Telka 			 */
26135cb0d679SMarcel Telka 			if (flavor != AUTH_SYS)
26145cb0d679SMarcel Telka 				break;
26155cb0d679SMarcel Telka 
26165cb0d679SMarcel Telka 			if (perm & NFSAUTH_GIDMAP || map_deny)
26175cb0d679SMarcel Telka 				break;
26185cb0d679SMarcel Telka 
26195cb0d679SMarcel Telka 			for (c = val; c != NULL; c = n) {
26205cb0d679SMarcel Telka 				char *s;
26215cb0d679SMarcel Telka 				char *al;
26225cb0d679SMarcel Telka 				gid_t srv;
26235cb0d679SMarcel Telka 
26245cb0d679SMarcel Telka 				n = strchr(c, '~');
26255cb0d679SMarcel Telka 				if (n != NULL)
26265cb0d679SMarcel Telka 					*n++ = '\0';
26275cb0d679SMarcel Telka 
26285cb0d679SMarcel Telka 				s = strchr(c, ':');
26295cb0d679SMarcel Telka 				if (s != NULL) {
26305cb0d679SMarcel Telka 					*s++ = '\0';
26315cb0d679SMarcel Telka 					al = strchr(s, ':');
26325cb0d679SMarcel Telka 					if (al != NULL)
26335cb0d679SMarcel Telka 						*al++ = '\0';
26345cb0d679SMarcel Telka 				}
26355cb0d679SMarcel Telka 
26365cb0d679SMarcel Telka 				if (s == NULL || al == NULL)
26375cb0d679SMarcel Telka 					break;
26385cb0d679SMarcel Telka 
26395cb0d679SMarcel Telka 				if (*c == '\0') {
26405cb0d679SMarcel Telka 					if (clnt_gid != (gid_t)-1)
26415cb0d679SMarcel Telka 						continue;
26425cb0d679SMarcel Telka 				} else if (strcmp(c, "*") != 0) {
26435cb0d679SMarcel Telka 					gid_t clnt;
26445cb0d679SMarcel Telka 
26455cb0d679SMarcel Telka 					if (!get_gid(c, &clnt))
26465cb0d679SMarcel Telka 						continue;
26475cb0d679SMarcel Telka 
26485cb0d679SMarcel Telka 					if (clnt_gid != clnt)
26495cb0d679SMarcel Telka 						continue;
26505cb0d679SMarcel Telka 				}
26515cb0d679SMarcel Telka 
26525cb0d679SMarcel Telka 				if (*s == '\0')
26535cb0d679SMarcel Telka 					srv = UID_NOBODY;
26545cb0d679SMarcel Telka 				else if (!get_gid(s, &srv))
26555cb0d679SMarcel Telka 					continue;
26565cb0d679SMarcel Telka 				else if (srv == (gid_t)-1) {
26575cb0d679SMarcel Telka 					map_deny = B_TRUE;
26585cb0d679SMarcel Telka 					break;
26595cb0d679SMarcel Telka 				}
26605cb0d679SMarcel Telka 
2661a9685eaaSMarcel Telka 				if (in_access_list(cln, al) > 0) {
26625cb0d679SMarcel Telka 					*srv_gid = srv;
26635cb0d679SMarcel Telka 					perm |= NFSAUTH_GIDMAP;
266489621fe1SMarcel Telka 
266589621fe1SMarcel Telka 					if (perm & NFSAUTH_GROUPS) {
266689621fe1SMarcel Telka 						free(*srv_gids);
266789621fe1SMarcel Telka 						*srv_ngids = 0;
266889621fe1SMarcel Telka 						*srv_gids = NULL;
266989621fe1SMarcel Telka 						perm &= ~NFSAUTH_GROUPS;
267089621fe1SMarcel Telka 					}
267189621fe1SMarcel Telka 
26725cb0d679SMarcel Telka 					break;
26735cb0d679SMarcel Telka 				}
26745cb0d679SMarcel Telka 			}
26755cb0d679SMarcel Telka 
26765cb0d679SMarcel Telka 			break;
26775cb0d679SMarcel Telka 		}
26785cb0d679SMarcel Telka 
26795cb0d679SMarcel Telka 		default:
2680b89a8333Snatalie li - Sun Microsystems - Irvine United States 			break;
26817c478bd9Sstevel@tonic-gate 		}
26827c478bd9Sstevel@tonic-gate 	}
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate 	free(opts);
26857c478bd9Sstevel@tonic-gate 
26865cb0d679SMarcel Telka 	if (perm & NFSAUTH_ROOT) {
26875cb0d679SMarcel Telka 		*srv_uid = 0;
26885cb0d679SMarcel Telka 		*srv_gid = 0;
26895cb0d679SMarcel Telka 	}
26905cb0d679SMarcel Telka 
26915cb0d679SMarcel Telka 	if (map_deny)
26925cb0d679SMarcel Telka 		perm |= NFSAUTH_DENIED;
26935cb0d679SMarcel Telka 
26945cb0d679SMarcel Telka 	if (!(perm & NFSAUTH_UIDMAP))
26955cb0d679SMarcel Telka 		*srv_uid = clnt_uid;
26965cb0d679SMarcel Telka 	if (!(perm & NFSAUTH_GIDMAP))
26975cb0d679SMarcel Telka 		*srv_gid = clnt_gid;
26985cb0d679SMarcel Telka 
26995cb0d679SMarcel Telka 	if (flavor != match || perm & NFSAUTH_DENIED)
27007c478bd9Sstevel@tonic-gate 		return (NFSAUTH_DENIED);
27017c478bd9Sstevel@tonic-gate 
27027c478bd9Sstevel@tonic-gate 	if (list) {
27037c478bd9Sstevel@tonic-gate 		/*
27047c478bd9Sstevel@tonic-gate 		 * If the client doesn't match an "ro" or "rw"
27057c478bd9Sstevel@tonic-gate 		 * list then set no access.
27067c478bd9Sstevel@tonic-gate 		 */
27077c478bd9Sstevel@tonic-gate 		if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
27087c478bd9Sstevel@tonic-gate 			perm |= NFSAUTH_DENIED;
27097c478bd9Sstevel@tonic-gate 	} else {
27107c478bd9Sstevel@tonic-gate 		/*
27117c478bd9Sstevel@tonic-gate 		 * The client matched a flavor entry that
27127c478bd9Sstevel@tonic-gate 		 * has no explicit "rw" or "ro" determination.
27137c478bd9Sstevel@tonic-gate 		 * Default it to "rw".
27147c478bd9Sstevel@tonic-gate 		 */
27157c478bd9Sstevel@tonic-gate 		perm |= NFSAUTH_RW;
27167c478bd9Sstevel@tonic-gate 	}
27177c478bd9Sstevel@tonic-gate 
27187c478bd9Sstevel@tonic-gate 	/*
27197c478bd9Sstevel@tonic-gate 	 * The client may show up in both ro= and rw=
27207c478bd9Sstevel@tonic-gate 	 * lists.  If so, then turn off the RO access
27217c478bd9Sstevel@tonic-gate 	 * bit leaving RW access.
27227c478bd9Sstevel@tonic-gate 	 */
27237c478bd9Sstevel@tonic-gate 	if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
27247c478bd9Sstevel@tonic-gate 		/*
27257c478bd9Sstevel@tonic-gate 		 * Logically cover all permutations of rw=,ro=.
27267c478bd9Sstevel@tonic-gate 		 * In the case where, rw,ro=<host> we would like
27277c478bd9Sstevel@tonic-gate 		 * to remove RW access for the host.  In all other cases
27287c478bd9Sstevel@tonic-gate 		 * RW wins the precedence battle.
27297c478bd9Sstevel@tonic-gate 		 */
27307c478bd9Sstevel@tonic-gate 		if (!rw_val && ro_val) {
27317c478bd9Sstevel@tonic-gate 			perm &= ~(NFSAUTH_RW);
27327c478bd9Sstevel@tonic-gate 		} else {
27337c478bd9Sstevel@tonic-gate 			perm &= ~(NFSAUTH_RO);
27347c478bd9Sstevel@tonic-gate 		}
27357c478bd9Sstevel@tonic-gate 	}
27367c478bd9Sstevel@tonic-gate 
27377c478bd9Sstevel@tonic-gate 	return (perm);
27387c478bd9Sstevel@tonic-gate }
27397c478bd9Sstevel@tonic-gate 
27407c478bd9Sstevel@tonic-gate /*
27417c478bd9Sstevel@tonic-gate  * Check if the client has access by using a flavor different from
27427c478bd9Sstevel@tonic-gate  * the given "flavor". If "flavor" is not in the flavor list,
27437c478bd9Sstevel@tonic-gate  * return TRUE to indicate that this "flavor" is a wrong sec.
27447c478bd9Sstevel@tonic-gate  */
27457c478bd9Sstevel@tonic-gate static bool_t
is_wrongsec(share_t * sh,struct cln * cln,int flavor)2746a9685eaaSMarcel Telka is_wrongsec(share_t *sh, struct cln *cln, int flavor)
27477c478bd9Sstevel@tonic-gate {
27487c478bd9Sstevel@tonic-gate 	int flavor_list[MAX_FLAVORS];
27497c478bd9Sstevel@tonic-gate 	int flavor_count, i;
27507c478bd9Sstevel@tonic-gate 
27517c478bd9Sstevel@tonic-gate 	/* get the flavor list that the client has access with */
2752a9685eaaSMarcel Telka 	flavor_count = getclientsflavors_new(sh, cln, flavor_list);
27537c478bd9Sstevel@tonic-gate 
27547c478bd9Sstevel@tonic-gate 	if (flavor_count == 0)
27557c478bd9Sstevel@tonic-gate 		return (FALSE);
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 	/*
27587c478bd9Sstevel@tonic-gate 	 * Check if the given "flavor" is in the flavor_list.
27597c478bd9Sstevel@tonic-gate 	 */
27607c478bd9Sstevel@tonic-gate 	for (i = 0; i < flavor_count; i++) {
27617c478bd9Sstevel@tonic-gate 		if (flavor == flavor_list[i])
27627c478bd9Sstevel@tonic-gate 			return (FALSE);
27637c478bd9Sstevel@tonic-gate 	}
27647c478bd9Sstevel@tonic-gate 
27657c478bd9Sstevel@tonic-gate 	/*
27667c478bd9Sstevel@tonic-gate 	 * If "flavor" is not in the flavor_list, return TRUE to indicate
27677c478bd9Sstevel@tonic-gate 	 * that the client should have access by using a security flavor
27687c478bd9Sstevel@tonic-gate 	 * different from this "flavor".
27697c478bd9Sstevel@tonic-gate 	 */
27707c478bd9Sstevel@tonic-gate 	return (TRUE);
27717c478bd9Sstevel@tonic-gate }
27727c478bd9Sstevel@tonic-gate 
27737c478bd9Sstevel@tonic-gate /*
27747c478bd9Sstevel@tonic-gate  * Given an export and the client's hostname, we
27757c478bd9Sstevel@tonic-gate  * check the security options to see whether the
27767c478bd9Sstevel@tonic-gate  * client is allowed to use the given security flavor.
27777c478bd9Sstevel@tonic-gate  *
27787c478bd9Sstevel@tonic-gate  * The strategy is to proceed through the options looking
27797c478bd9Sstevel@tonic-gate  * for a flavor match, then pay attention to the ro, rw,
27807c478bd9Sstevel@tonic-gate  * and root options.
27817c478bd9Sstevel@tonic-gate  *
27827c478bd9Sstevel@tonic-gate  * Note that an entry may list several flavors in a
27837c478bd9Sstevel@tonic-gate  * single entry, e.g.
27847c478bd9Sstevel@tonic-gate  *
27857c478bd9Sstevel@tonic-gate  *   sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
27867c478bd9Sstevel@tonic-gate  *
27877c478bd9Sstevel@tonic-gate  */
27887c478bd9Sstevel@tonic-gate 
27897c478bd9Sstevel@tonic-gate static int
check_client_new(share_t * sh,struct cln * cln,int flavor,uid_t clnt_uid,gid_t clnt_gid,uint_t clnt_ngids,gid_t * clnt_gids,uid_t * srv_uid,gid_t * srv_gid,uint_t * srv_ngids,gid_t ** srv_gids)2790a9685eaaSMarcel Telka check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
279189621fe1SMarcel Telka     gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
279289621fe1SMarcel Telka     gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
27937c478bd9Sstevel@tonic-gate {
27947c478bd9Sstevel@tonic-gate 	char *opts, *p, *val;
27957c478bd9Sstevel@tonic-gate 	char *lasts;
27967c478bd9Sstevel@tonic-gate 	char *f;
27977c478bd9Sstevel@tonic-gate 	int match = 0;	/* Set when a flavor is matched */
27987c478bd9Sstevel@tonic-gate 	int perm = 0;	/* Set when "ro", "rw" or "root" is matched */
27997c478bd9Sstevel@tonic-gate 	int list = 0;	/* Set when "ro", "rw" is found */
28007c478bd9Sstevel@tonic-gate 	int ro_val = 0;	/* Set if ro option is 'ro=' */
28017c478bd9Sstevel@tonic-gate 	int rw_val = 0;	/* Set if rw option is 'rw=' */
28025cb0d679SMarcel Telka 
28035cb0d679SMarcel Telka 	boolean_t map_deny = B_FALSE;
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 	opts = strdup(sh->sh_opts);
28067c478bd9Sstevel@tonic-gate 	if (opts == NULL) {
28077c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "check_client: no memory");
28087c478bd9Sstevel@tonic-gate 		return (0);
28097c478bd9Sstevel@tonic-gate 	}
28107c478bd9Sstevel@tonic-gate 
281189621fe1SMarcel Telka 	/*
281289621fe1SMarcel Telka 	 * If client provided 16 supplemental groups with AUTH_SYS, lookup
281389621fe1SMarcel Telka 	 * locally for all of them
281489621fe1SMarcel Telka 	 */
281589621fe1SMarcel Telka 	if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
281689621fe1SMarcel Telka 		if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
281789621fe1SMarcel Telka 			perm |= NFSAUTH_GROUPS;
281889621fe1SMarcel Telka 
28197c478bd9Sstevel@tonic-gate 	p = opts;
28207c478bd9Sstevel@tonic-gate 
28217c478bd9Sstevel@tonic-gate 	while (*p) {
28227c478bd9Sstevel@tonic-gate 		switch (getsubopt(&p, optlist, &val)) {
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 		case OPT_SEC:
28257c478bd9Sstevel@tonic-gate 			if (match)
28267c478bd9Sstevel@tonic-gate 				goto done;
28277c478bd9Sstevel@tonic-gate 
28287c478bd9Sstevel@tonic-gate 			while ((f = strtok_r(val, ":", &lasts))
2829250a0733Sth 			    != NULL) {
28307c478bd9Sstevel@tonic-gate 				if (flavor == map_flavor(f)) {
28317c478bd9Sstevel@tonic-gate 					match = 1;
28327c478bd9Sstevel@tonic-gate 					break;
28337c478bd9Sstevel@tonic-gate 				}
28347c478bd9Sstevel@tonic-gate 				val = NULL;
28357c478bd9Sstevel@tonic-gate 			}
28367c478bd9Sstevel@tonic-gate 			break;
28377c478bd9Sstevel@tonic-gate 
28387c478bd9Sstevel@tonic-gate 		case OPT_RO:
28397c478bd9Sstevel@tonic-gate 			if (!match)
28407c478bd9Sstevel@tonic-gate 				break;
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 			list++;
28435cb0d679SMarcel Telka 			if (val != NULL)
28445cb0d679SMarcel Telka 				ro_val++;
2845a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
28467c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_RO;
28477c478bd9Sstevel@tonic-gate 			break;
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate 		case OPT_RW:
28507c478bd9Sstevel@tonic-gate 			if (!match)
28517c478bd9Sstevel@tonic-gate 				break;
28527c478bd9Sstevel@tonic-gate 
28537c478bd9Sstevel@tonic-gate 			list++;
28545cb0d679SMarcel Telka 			if (val != NULL)
28555cb0d679SMarcel Telka 				rw_val++;
2856a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
28577c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_RW;
28587c478bd9Sstevel@tonic-gate 			break;
28597c478bd9Sstevel@tonic-gate 
28607c478bd9Sstevel@tonic-gate 		case OPT_ROOT:
28617c478bd9Sstevel@tonic-gate 			/*
28627c478bd9Sstevel@tonic-gate 			 * Check if the client is in
28637c478bd9Sstevel@tonic-gate 			 * the root list. Only valid
28647c478bd9Sstevel@tonic-gate 			 * for AUTH_SYS.
28657c478bd9Sstevel@tonic-gate 			 */
28667c478bd9Sstevel@tonic-gate 			if (flavor != AUTH_SYS)
28677c478bd9Sstevel@tonic-gate 				break;
28687c478bd9Sstevel@tonic-gate 
28697c478bd9Sstevel@tonic-gate 			if (!match)
28707c478bd9Sstevel@tonic-gate 				break;
28717c478bd9Sstevel@tonic-gate 
28727c478bd9Sstevel@tonic-gate 			if (val == NULL || *val == '\0')
28737c478bd9Sstevel@tonic-gate 				break;
28747c478bd9Sstevel@tonic-gate 
28755cb0d679SMarcel Telka 			if (clnt_uid != 0)
28765cb0d679SMarcel Telka 				break;
28775cb0d679SMarcel Telka 
2878a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0) {
28797c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_ROOT;
28805cb0d679SMarcel Telka 				perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
28815cb0d679SMarcel Telka 				map_deny = B_FALSE;
288289621fe1SMarcel Telka 
288389621fe1SMarcel Telka 				if (perm & NFSAUTH_GROUPS) {
288489621fe1SMarcel Telka 					free(*srv_gids);
288589621fe1SMarcel Telka 					*srv_gids = NULL;
288689621fe1SMarcel Telka 					*srv_ngids = 0;
288789621fe1SMarcel Telka 					perm &= ~NFSAUTH_GROUPS;
288889621fe1SMarcel Telka 				}
28895cb0d679SMarcel Telka 			}
28907c478bd9Sstevel@tonic-gate 			break;
2891b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2892b89a8333Snatalie li - Sun Microsystems - Irvine United States 		case OPT_NONE:
2893b89a8333Snatalie li - Sun Microsystems - Irvine United States 			/*
289454d34259SMarcel Telka 			 * Check if the client should have no access
2895b89a8333Snatalie li - Sun Microsystems - Irvine United States 			 * to this share at all. This option behaves
2896b89a8333Snatalie li - Sun Microsystems - Irvine United States 			 * more like "root" than either "rw" or "ro".
2897b89a8333Snatalie li - Sun Microsystems - Irvine United States 			 */
2898a9685eaaSMarcel Telka 			if (in_access_list(cln, val) > 0)
2899b89a8333Snatalie li - Sun Microsystems - Irvine United States 				perm |= NFSAUTH_DENIED;
2900b89a8333Snatalie li - Sun Microsystems - Irvine United States 			break;
29015cb0d679SMarcel Telka 
29025cb0d679SMarcel Telka 		case OPT_UIDMAP: {
29035cb0d679SMarcel Telka 			char *c;
29045cb0d679SMarcel Telka 			char *n;
29055cb0d679SMarcel Telka 
29065cb0d679SMarcel Telka 			/*
29075cb0d679SMarcel Telka 			 * The uidmap is supported for AUTH_SYS only.
29085cb0d679SMarcel Telka 			 */
29095cb0d679SMarcel Telka 			if (flavor != AUTH_SYS)
29105cb0d679SMarcel Telka 				break;
29115cb0d679SMarcel Telka 
29125cb0d679SMarcel Telka 			if (!match || perm & NFSAUTH_UIDMAP || map_deny)
29135cb0d679SMarcel Telka 				break;
29145cb0d679SMarcel Telka 
29155cb0d679SMarcel Telka 			for (c = val; c != NULL; c = n) {
29165cb0d679SMarcel Telka 				char *s;
29175cb0d679SMarcel Telka 				char *al;
29185cb0d679SMarcel Telka 				uid_t srv;
29195cb0d679SMarcel Telka 
29205cb0d679SMarcel Telka 				n = strchr(c, '~');
29215cb0d679SMarcel Telka 				if (n != NULL)
29225cb0d679SMarcel Telka 					*n++ = '\0';
29235cb0d679SMarcel Telka 
29245cb0d679SMarcel Telka 				s = strchr(c, ':');
29255cb0d679SMarcel Telka 				if (s != NULL) {
29265cb0d679SMarcel Telka 					*s++ = '\0';
29275cb0d679SMarcel Telka 					al = strchr(s, ':');
29285cb0d679SMarcel Telka 					if (al != NULL)
29295cb0d679SMarcel Telka 						*al++ = '\0';
29305cb0d679SMarcel Telka 				}
29315cb0d679SMarcel Telka 
29325cb0d679SMarcel Telka 				if (s == NULL || al == NULL)
29335cb0d679SMarcel Telka 					continue;
29345cb0d679SMarcel Telka 
29355cb0d679SMarcel Telka 				if (*c == '\0') {
29365cb0d679SMarcel Telka 					if (clnt_uid != (uid_t)-1)
29375cb0d679SMarcel Telka 						continue;
29385cb0d679SMarcel Telka 				} else if (strcmp(c, "*") != 0) {
29395cb0d679SMarcel Telka 					uid_t clnt;
29405cb0d679SMarcel Telka 
29415cb0d679SMarcel Telka 					if (!get_uid(c, &clnt))
29425cb0d679SMarcel Telka 						continue;
29435cb0d679SMarcel Telka 
29445cb0d679SMarcel Telka 					if (clnt_uid != clnt)
29455cb0d679SMarcel Telka 						continue;
29465cb0d679SMarcel Telka 				}
29475cb0d679SMarcel Telka 
29485cb0d679SMarcel Telka 				if (*s == '\0')
29495cb0d679SMarcel Telka 					srv = UID_NOBODY;
29505cb0d679SMarcel Telka 				else if (!get_uid(s, &srv))
29515cb0d679SMarcel Telka 					continue;
29525cb0d679SMarcel Telka 				else if (srv == (uid_t)-1) {
29535cb0d679SMarcel Telka 					map_deny = B_TRUE;
29545cb0d679SMarcel Telka 					break;
29555cb0d679SMarcel Telka 				}
29565cb0d679SMarcel Telka 
2957a9685eaaSMarcel Telka 				if (in_access_list(cln, al) > 0) {
29585cb0d679SMarcel Telka 					*srv_uid = srv;
29595cb0d679SMarcel Telka 					perm |= NFSAUTH_UIDMAP;
296089621fe1SMarcel Telka 
296189621fe1SMarcel Telka 					if (perm & NFSAUTH_GROUPS) {
296289621fe1SMarcel Telka 						free(*srv_gids);
296389621fe1SMarcel Telka 						*srv_gids = NULL;
296489621fe1SMarcel Telka 						*srv_ngids = 0;
296589621fe1SMarcel Telka 						perm &= ~NFSAUTH_GROUPS;
296689621fe1SMarcel Telka 					}
296789621fe1SMarcel Telka 
29685cb0d679SMarcel Telka 					break;
29695cb0d679SMarcel Telka 				}
29705cb0d679SMarcel Telka 			}
29715cb0d679SMarcel Telka 
29725cb0d679SMarcel Telka 			break;
29735cb0d679SMarcel Telka 		}
29745cb0d679SMarcel Telka 
29755cb0d679SMarcel Telka 		case OPT_GIDMAP: {
29765cb0d679SMarcel Telka 			char *c;
29775cb0d679SMarcel Telka 			char *n;
29785cb0d679SMarcel Telka 
29795cb0d679SMarcel Telka 			/*
29805cb0d679SMarcel Telka 			 * The gidmap is supported for AUTH_SYS only.
29815cb0d679SMarcel Telka 			 */
29825cb0d679SMarcel Telka 			if (flavor != AUTH_SYS)
29835cb0d679SMarcel Telka 				break;
29845cb0d679SMarcel Telka 
29855cb0d679SMarcel Telka 			if (!match || perm & NFSAUTH_GIDMAP || map_deny)
29865cb0d679SMarcel Telka 				break;
29875cb0d679SMarcel Telka 
29885cb0d679SMarcel Telka 			for (c = val; c != NULL; c = n) {
29895cb0d679SMarcel Telka 				char *s;
29905cb0d679SMarcel Telka 				char *al;
29915cb0d679SMarcel Telka 				gid_t srv;
29925cb0d679SMarcel Telka 
29935cb0d679SMarcel Telka 				n = strchr(c, '~');
29945cb0d679SMarcel Telka 				if (n != NULL)
29955cb0d679SMarcel Telka 					*n++ = '\0';
29965cb0d679SMarcel Telka 
29975cb0d679SMarcel Telka 				s = strchr(c, ':');
29985cb0d679SMarcel Telka 				if (s != NULL) {
29995cb0d679SMarcel Telka 					*s++ = '\0';
30005cb0d679SMarcel Telka 					al = strchr(s, ':');
30015cb0d679SMarcel Telka 					if (al != NULL)
30025cb0d679SMarcel Telka 						*al++ = '\0';
30035cb0d679SMarcel Telka 				}
30045cb0d679SMarcel Telka 
30055cb0d679SMarcel Telka 				if (s == NULL || al == NULL)
30065cb0d679SMarcel Telka 					break;
30075cb0d679SMarcel Telka 
30085cb0d679SMarcel Telka 				if (*c == '\0') {
30095cb0d679SMarcel Telka 					if (clnt_gid != (gid_t)-1)
30105cb0d679SMarcel Telka 						continue;
30115cb0d679SMarcel Telka 				} else if (strcmp(c, "*") != 0) {
30125cb0d679SMarcel Telka 					gid_t clnt;
30135cb0d679SMarcel Telka 
30145cb0d679SMarcel Telka 					if (!get_gid(c, &clnt))
30155cb0d679SMarcel Telka 						continue;
30165cb0d679SMarcel Telka 
30175cb0d679SMarcel Telka 					if (clnt_gid != clnt)
30185cb0d679SMarcel Telka 						continue;
30195cb0d679SMarcel Telka 				}
30205cb0d679SMarcel Telka 
30215cb0d679SMarcel Telka 				if (*s == '\0')
30225cb0d679SMarcel Telka 					srv = UID_NOBODY;
30235cb0d679SMarcel Telka 				else if (!get_gid(s, &srv))
30245cb0d679SMarcel Telka 					continue;
30255cb0d679SMarcel Telka 				else if (srv == (gid_t)-1) {
30265cb0d679SMarcel Telka 					map_deny = B_TRUE;
30275cb0d679SMarcel Telka 					break;
30285cb0d679SMarcel Telka 				}
30295cb0d679SMarcel Telka 
3030a9685eaaSMarcel Telka 				if (in_access_list(cln, al) > 0) {
30315cb0d679SMarcel Telka 					*srv_gid = srv;
30325cb0d679SMarcel Telka 					perm |= NFSAUTH_GIDMAP;
303389621fe1SMarcel Telka 
303489621fe1SMarcel Telka 					if (perm & NFSAUTH_GROUPS) {
303589621fe1SMarcel Telka 						free(*srv_gids);
303689621fe1SMarcel Telka 						*srv_gids = NULL;
303789621fe1SMarcel Telka 						*srv_ngids = 0;
303889621fe1SMarcel Telka 						perm &= ~NFSAUTH_GROUPS;
303989621fe1SMarcel Telka 					}
304089621fe1SMarcel Telka 
30415cb0d679SMarcel Telka 					break;
30425cb0d679SMarcel Telka 				}
30435cb0d679SMarcel Telka 			}
30445cb0d679SMarcel Telka 
30455cb0d679SMarcel Telka 			break;
30465cb0d679SMarcel Telka 		}
30475cb0d679SMarcel Telka 
30485cb0d679SMarcel Telka 		default:
30495cb0d679SMarcel Telka 			break;
30507c478bd9Sstevel@tonic-gate 		}
30517c478bd9Sstevel@tonic-gate 	}
30527c478bd9Sstevel@tonic-gate 
30537c478bd9Sstevel@tonic-gate done:
30545cb0d679SMarcel Telka 	if (perm & NFSAUTH_ROOT) {
30555cb0d679SMarcel Telka 		*srv_uid = 0;
30565cb0d679SMarcel Telka 		*srv_gid = 0;
30575cb0d679SMarcel Telka 	}
30585cb0d679SMarcel Telka 
30595cb0d679SMarcel Telka 	if (map_deny)
30605cb0d679SMarcel Telka 		perm |= NFSAUTH_DENIED;
30615cb0d679SMarcel Telka 
30625cb0d679SMarcel Telka 	if (!(perm & NFSAUTH_UIDMAP))
30635cb0d679SMarcel Telka 		*srv_uid = clnt_uid;
30645cb0d679SMarcel Telka 	if (!(perm & NFSAUTH_GIDMAP))
30655cb0d679SMarcel Telka 		*srv_gid = clnt_gid;
30665cb0d679SMarcel Telka 
30677c478bd9Sstevel@tonic-gate 	/*
30687c478bd9Sstevel@tonic-gate 	 * If no match then set the perm accordingly
30697c478bd9Sstevel@tonic-gate 	 */
30705cb0d679SMarcel Telka 	if (!match || perm & NFSAUTH_DENIED) {
30715cb0d679SMarcel Telka 		free(opts);
30727c478bd9Sstevel@tonic-gate 		return (NFSAUTH_DENIED);
30735cb0d679SMarcel Telka 	}
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 	if (list) {
30767c478bd9Sstevel@tonic-gate 		/*
30777c478bd9Sstevel@tonic-gate 		 * If the client doesn't match an "ro" or "rw" list then
30787c478bd9Sstevel@tonic-gate 		 * check if it may have access by using a different flavor.
30797c478bd9Sstevel@tonic-gate 		 * If so, return NFSAUTH_WRONGSEC.
30807c478bd9Sstevel@tonic-gate 		 * If not, return NFSAUTH_DENIED.
30817c478bd9Sstevel@tonic-gate 		 */
30827c478bd9Sstevel@tonic-gate 		if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
3083a9685eaaSMarcel Telka 			if (is_wrongsec(sh, cln, flavor))
30847c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_WRONGSEC;
30857c478bd9Sstevel@tonic-gate 			else
30867c478bd9Sstevel@tonic-gate 				perm |= NFSAUTH_DENIED;
30877c478bd9Sstevel@tonic-gate 		}
30887c478bd9Sstevel@tonic-gate 	} else {
30897c478bd9Sstevel@tonic-gate 		/*
30907c478bd9Sstevel@tonic-gate 		 * The client matched a flavor entry that
30917c478bd9Sstevel@tonic-gate 		 * has no explicit "rw" or "ro" determination.
30927c478bd9Sstevel@tonic-gate 		 * Make sure it defaults to "rw".
30937c478bd9Sstevel@tonic-gate 		 */
30947c478bd9Sstevel@tonic-gate 		perm |= NFSAUTH_RW;
30957c478bd9Sstevel@tonic-gate 	}
30967c478bd9Sstevel@tonic-gate 
30977c478bd9Sstevel@tonic-gate 	/*
30987c478bd9Sstevel@tonic-gate 	 * The client may show up in both ro= and rw=
30997c478bd9Sstevel@tonic-gate 	 * lists.  If so, then turn off the RO access
31007c478bd9Sstevel@tonic-gate 	 * bit leaving RW access.
31017c478bd9Sstevel@tonic-gate 	 */
31027c478bd9Sstevel@tonic-gate 	if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
31037c478bd9Sstevel@tonic-gate 		/*
31047c478bd9Sstevel@tonic-gate 		 * Logically cover all permutations of rw=,ro=.
31057c478bd9Sstevel@tonic-gate 		 * In the case where, rw,ro=<host> we would like
31067c478bd9Sstevel@tonic-gate 		 * to remove RW access for the host.  In all other cases
31077c478bd9Sstevel@tonic-gate 		 * RW wins the precedence battle.
31087c478bd9Sstevel@tonic-gate 		 */
31097c478bd9Sstevel@tonic-gate 		if (!rw_val && ro_val) {
31107c478bd9Sstevel@tonic-gate 			perm &= ~(NFSAUTH_RW);
31117c478bd9Sstevel@tonic-gate 		} else {
31127c478bd9Sstevel@tonic-gate 			perm &= ~(NFSAUTH_RO);
31137c478bd9Sstevel@tonic-gate 		}
31147c478bd9Sstevel@tonic-gate 	}
31157c478bd9Sstevel@tonic-gate 
31167c478bd9Sstevel@tonic-gate 	free(opts);
31177c478bd9Sstevel@tonic-gate 
31187c478bd9Sstevel@tonic-gate 	return (perm);
31197c478bd9Sstevel@tonic-gate }
31207c478bd9Sstevel@tonic-gate 
31217c478bd9Sstevel@tonic-gate void
check_sharetab()31227c478bd9Sstevel@tonic-gate check_sharetab()
31237c478bd9Sstevel@tonic-gate {
31247c478bd9Sstevel@tonic-gate 	FILE *f;
31257c478bd9Sstevel@tonic-gate 	struct stat st;
31267c478bd9Sstevel@tonic-gate 	static timestruc_t last_sharetab_time;
31277c478bd9Sstevel@tonic-gate 	timestruc_t prev_sharetab_time;
31284a508a79SThomas Haynes 	share_t *sh;
31297c478bd9Sstevel@tonic-gate 	struct sh_list *shp, *shp_prev;
31307c478bd9Sstevel@tonic-gate 	int res, c = 0;
31317c478bd9Sstevel@tonic-gate 
31327c478bd9Sstevel@tonic-gate 	/*
31337c478bd9Sstevel@tonic-gate 	 *  read in /etc/dfs/sharetab if it has changed
31347c478bd9Sstevel@tonic-gate 	 */
31357c478bd9Sstevel@tonic-gate 	if (stat(SHARETAB, &st) != 0) {
31367c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
31377c478bd9Sstevel@tonic-gate 		return;
31387c478bd9Sstevel@tonic-gate 	}
31397c478bd9Sstevel@tonic-gate 
31407c478bd9Sstevel@tonic-gate 	if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
31417c478bd9Sstevel@tonic-gate 	    st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
31427c478bd9Sstevel@tonic-gate 		/*
31437c478bd9Sstevel@tonic-gate 		 * No change.
31447c478bd9Sstevel@tonic-gate 		 */
31457c478bd9Sstevel@tonic-gate 		return;
31467c478bd9Sstevel@tonic-gate 	}
31477c478bd9Sstevel@tonic-gate 
31487c478bd9Sstevel@tonic-gate 	/*
31497c478bd9Sstevel@tonic-gate 	 * Remember the mod time, then after getting the
31507c478bd9Sstevel@tonic-gate 	 * write lock check again.  If another thread
31517c478bd9Sstevel@tonic-gate 	 * already did the update, then there's no
31527c478bd9Sstevel@tonic-gate 	 * work to do.
31537c478bd9Sstevel@tonic-gate 	 */
31547c478bd9Sstevel@tonic-gate 	prev_sharetab_time = last_sharetab_time;
31557c478bd9Sstevel@tonic-gate 
31567c478bd9Sstevel@tonic-gate 	(void) rw_wrlock(&sharetab_lock);
31577c478bd9Sstevel@tonic-gate 
31587c478bd9Sstevel@tonic-gate 	if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
31597c478bd9Sstevel@tonic-gate 	    prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
31607c478bd9Sstevel@tonic-gate 		(void) rw_unlock(&sharetab_lock);
31617c478bd9Sstevel@tonic-gate 		return;
31627c478bd9Sstevel@tonic-gate 	}
31637c478bd9Sstevel@tonic-gate 
31647c478bd9Sstevel@tonic-gate 	/*
3165a237e38eSth 	 * Note that since the sharetab is now in memory
3166a237e38eSth 	 * and a snapshot is taken, we no longer have to
3167a237e38eSth 	 * lock the file.
31687c478bd9Sstevel@tonic-gate 	 */
3169a237e38eSth 	f = fopen(SHARETAB, "r");
3170a237e38eSth 	if (f == NULL) {
3171a237e38eSth 		syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
31727c478bd9Sstevel@tonic-gate 		(void) rw_unlock(&sharetab_lock);
31737c478bd9Sstevel@tonic-gate 		return;
31747c478bd9Sstevel@tonic-gate 	}
31757c478bd9Sstevel@tonic-gate 
31767c478bd9Sstevel@tonic-gate 	/*
31777c478bd9Sstevel@tonic-gate 	 * Once we are sure /etc/dfs/sharetab has been
31787c478bd9Sstevel@tonic-gate 	 * modified, flush netgroup cache entries.
31797c478bd9Sstevel@tonic-gate 	 */
31807c478bd9Sstevel@tonic-gate 	netgrp_cache_flush();
31817c478bd9Sstevel@tonic-gate 
31827c478bd9Sstevel@tonic-gate 	sh_free(share_list);			/* free old list */
31837c478bd9Sstevel@tonic-gate 	share_list = NULL;
31847c478bd9Sstevel@tonic-gate 
31857c478bd9Sstevel@tonic-gate 	while ((res = getshare(f, &sh)) > 0) {
31867c478bd9Sstevel@tonic-gate 		c++;
31877c478bd9Sstevel@tonic-gate 		if (strcmp(sh->sh_fstype, "nfs") != 0)
31887c478bd9Sstevel@tonic-gate 			continue;
31897c478bd9Sstevel@tonic-gate 
31907c478bd9Sstevel@tonic-gate 		shp = malloc(sizeof (*shp));
31917c478bd9Sstevel@tonic-gate 		if (shp == NULL)
31927c478bd9Sstevel@tonic-gate 			goto alloc_failed;
31937c478bd9Sstevel@tonic-gate 		if (share_list == NULL)
31947c478bd9Sstevel@tonic-gate 			share_list = shp;
31957c478bd9Sstevel@tonic-gate 		else
31967c478bd9Sstevel@tonic-gate 			/* LINTED not used before set */
31977c478bd9Sstevel@tonic-gate 			shp_prev->shl_next = shp;
31987c478bd9Sstevel@tonic-gate 		shp_prev = shp;
31997c478bd9Sstevel@tonic-gate 		shp->shl_next = NULL;
32007c478bd9Sstevel@tonic-gate 		shp->shl_sh = sharedup(sh);
32017c478bd9Sstevel@tonic-gate 		if (shp->shl_sh == NULL)
32027c478bd9Sstevel@tonic-gate 			goto alloc_failed;
32037c478bd9Sstevel@tonic-gate 	}
32044a508a79SThomas Haynes 
32057c478bd9Sstevel@tonic-gate 	if (res < 0)
32067c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "%s: invalid at line %d\n",
3207250a0733Sth 		    SHARETAB, c + 1);
32087c478bd9Sstevel@tonic-gate 
32097c478bd9Sstevel@tonic-gate 	if (stat(SHARETAB, &st) != 0) {
32107c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
321162a1b812Sth 		(void) fclose(f);
32127c478bd9Sstevel@tonic-gate 		(void) rw_unlock(&sharetab_lock);
32137c478bd9Sstevel@tonic-gate 		return;
32147c478bd9Sstevel@tonic-gate 	}
32154a508a79SThomas Haynes 
32167c478bd9Sstevel@tonic-gate 	last_sharetab_time = st.st_mtim;
32177c478bd9Sstevel@tonic-gate 	(void) fclose(f);
32187c478bd9Sstevel@tonic-gate 	(void) rw_unlock(&sharetab_lock);
32194a508a79SThomas Haynes 
32207c478bd9Sstevel@tonic-gate 	return;
32217c478bd9Sstevel@tonic-gate 
32227c478bd9Sstevel@tonic-gate alloc_failed:
32234a508a79SThomas Haynes 
32247c478bd9Sstevel@tonic-gate 	syslog(LOG_ERR, "check_sharetab: no memory");
32257c478bd9Sstevel@tonic-gate 	sh_free(share_list);
32267c478bd9Sstevel@tonic-gate 	share_list = NULL;
32277c478bd9Sstevel@tonic-gate 	(void) fclose(f);
32287c478bd9Sstevel@tonic-gate 	(void) rw_unlock(&sharetab_lock);
32297c478bd9Sstevel@tonic-gate }
32307c478bd9Sstevel@tonic-gate 
32317c478bd9Sstevel@tonic-gate static void
sh_free(struct sh_list * shp)32327c478bd9Sstevel@tonic-gate sh_free(struct sh_list *shp)
32337c478bd9Sstevel@tonic-gate {
323454d34259SMarcel Telka 	struct sh_list *next;
32357c478bd9Sstevel@tonic-gate 
32367c478bd9Sstevel@tonic-gate 	while (shp) {
32377c478bd9Sstevel@tonic-gate 		sharefree(shp->shl_sh);
32387c478bd9Sstevel@tonic-gate 		next = shp->shl_next;
32397c478bd9Sstevel@tonic-gate 		free(shp);
32407c478bd9Sstevel@tonic-gate 		shp = next;
32417c478bd9Sstevel@tonic-gate 	}
32427c478bd9Sstevel@tonic-gate }
32437c478bd9Sstevel@tonic-gate 
32447c478bd9Sstevel@tonic-gate 
32457c478bd9Sstevel@tonic-gate /*
32467c478bd9Sstevel@tonic-gate  * Remove an entry from mounted list
32477c478bd9Sstevel@tonic-gate  */
32487c478bd9Sstevel@tonic-gate static void
umount(struct svc_req * rqstp)32497c478bd9Sstevel@tonic-gate umount(struct svc_req *rqstp)
32507c478bd9Sstevel@tonic-gate {
32517c478bd9Sstevel@tonic-gate 	char *host, *path, *remove_path;
32527c478bd9Sstevel@tonic-gate 	char rpath[MAXPATHLEN];
32537c478bd9Sstevel@tonic-gate 	SVCXPRT *transp;
3254a9685eaaSMarcel Telka 	struct cln cln;
32557c478bd9Sstevel@tonic-gate 
32567c478bd9Sstevel@tonic-gate 	transp = rqstp->rq_xprt;
32577c478bd9Sstevel@tonic-gate 	path = NULL;
32587c478bd9Sstevel@tonic-gate 	if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
32597c478bd9Sstevel@tonic-gate 		svcerr_decode(transp);
32607c478bd9Sstevel@tonic-gate 		return;
32617c478bd9Sstevel@tonic-gate 	}
3262a9685eaaSMarcel Telka 
3263a9685eaaSMarcel Telka 	cln_init(&cln, transp);
3264a9685eaaSMarcel Telka 
32657c478bd9Sstevel@tonic-gate 	errno = 0;
32667c478bd9Sstevel@tonic-gate 	if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3267a9685eaaSMarcel Telka 		log_cant_reply_cln(&cln);
32687c478bd9Sstevel@tonic-gate 
3269a9685eaaSMarcel Telka 	host = cln_gethost(&cln);
3270a9685eaaSMarcel Telka 	if (host == NULL) {
32717c478bd9Sstevel@tonic-gate 		/*
32727c478bd9Sstevel@tonic-gate 		 * Without the hostname we can't do audit or delete
32737c478bd9Sstevel@tonic-gate 		 * this host from the mount entries.
32747c478bd9Sstevel@tonic-gate 		 */
3275544783caSToomas Soome 		(void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
32767c478bd9Sstevel@tonic-gate 		return;
32777c478bd9Sstevel@tonic-gate 	}
32787c478bd9Sstevel@tonic-gate 
32797c478bd9Sstevel@tonic-gate 	if (verbose)
32807c478bd9Sstevel@tonic-gate 		syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
32817c478bd9Sstevel@tonic-gate 
32827c478bd9Sstevel@tonic-gate 	audit_mountd_umount(host, path);
32837c478bd9Sstevel@tonic-gate 
32847c478bd9Sstevel@tonic-gate 	remove_path = rpath;	/* assume we will use the cannonical path */
32857c478bd9Sstevel@tonic-gate 	if (realpath(path, rpath) == NULL) {
32867c478bd9Sstevel@tonic-gate 		if (verbose)
32877c478bd9Sstevel@tonic-gate 			syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
32887c478bd9Sstevel@tonic-gate 		remove_path = path;	/* use path provided instead */
32897c478bd9Sstevel@tonic-gate 	}
32907c478bd9Sstevel@tonic-gate 
32917c478bd9Sstevel@tonic-gate 	mntlist_delete(host, remove_path);	/* remove from mount list */
32927c478bd9Sstevel@tonic-gate 
3293a9685eaaSMarcel Telka 	cln_fini(&cln);
3294a9685eaaSMarcel Telka 
3295544783caSToomas Soome 	(void) svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
32967c478bd9Sstevel@tonic-gate }
32977c478bd9Sstevel@tonic-gate 
32987c478bd9Sstevel@tonic-gate /*
32997c478bd9Sstevel@tonic-gate  * Remove all entries for one machine from mounted list
33007c478bd9Sstevel@tonic-gate  */
33017c478bd9Sstevel@tonic-gate static void
umountall(struct svc_req * rqstp)33027c478bd9Sstevel@tonic-gate umountall(struct svc_req *rqstp)
33037c478bd9Sstevel@tonic-gate {
33047c478bd9Sstevel@tonic-gate 	SVCXPRT *transp;
33057c478bd9Sstevel@tonic-gate 	char *host;
3306a9685eaaSMarcel Telka 	struct cln cln;
33077c478bd9Sstevel@tonic-gate 
33087c478bd9Sstevel@tonic-gate 	transp = rqstp->rq_xprt;
33097c478bd9Sstevel@tonic-gate 	if (!svc_getargs(transp, xdr_void, NULL)) {
33107c478bd9Sstevel@tonic-gate 		svcerr_decode(transp);
33117c478bd9Sstevel@tonic-gate 		return;
33127c478bd9Sstevel@tonic-gate 	}
33137c478bd9Sstevel@tonic-gate 	/*
33147c478bd9Sstevel@tonic-gate 	 * We assume that this call is asynchronous and made via rpcbind
33157c478bd9Sstevel@tonic-gate 	 * callit routine.  Therefore return control immediately. The error
33167c478bd9Sstevel@tonic-gate 	 * causes rpcbind to remain silent, as opposed to every machine
33177c478bd9Sstevel@tonic-gate 	 * on the net blasting the requester with a response.
33187c478bd9Sstevel@tonic-gate 	 */
33197c478bd9Sstevel@tonic-gate 	svcerr_systemerr(transp);
3320a9685eaaSMarcel Telka 
3321a9685eaaSMarcel Telka 	cln_init(&cln, transp);
3322a9685eaaSMarcel Telka 
3323a9685eaaSMarcel Telka 	host = cln_gethost(&cln);
3324a9685eaaSMarcel Telka 	if (host == NULL) {
33257c478bd9Sstevel@tonic-gate 		/* Can't do anything without the name of the client */
33267c478bd9Sstevel@tonic-gate 		return;
33277c478bd9Sstevel@tonic-gate 	}
33287c478bd9Sstevel@tonic-gate 
33297c478bd9Sstevel@tonic-gate 	/*
33307c478bd9Sstevel@tonic-gate 	 * Remove all hosts entries from mount list
33317c478bd9Sstevel@tonic-gate 	 */
33327c478bd9Sstevel@tonic-gate 	mntlist_delete_all(host);
33337c478bd9Sstevel@tonic-gate 
33347c478bd9Sstevel@tonic-gate 	if (verbose)
33357c478bd9Sstevel@tonic-gate 		syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
33367c478bd9Sstevel@tonic-gate 
3337a9685eaaSMarcel Telka 	cln_fini(&cln);
33387c478bd9Sstevel@tonic-gate }
33397c478bd9Sstevel@tonic-gate 
33407c478bd9Sstevel@tonic-gate void *
exmalloc(size_t size)33417c478bd9Sstevel@tonic-gate exmalloc(size_t size)
33427c478bd9Sstevel@tonic-gate {
33437c478bd9Sstevel@tonic-gate 	void *ret;
33447c478bd9Sstevel@tonic-gate 
33457c478bd9Sstevel@tonic-gate 	if ((ret = malloc(size)) == NULL) {
33467c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Out of memory");
33477c478bd9Sstevel@tonic-gate 		exit(1);
33487c478bd9Sstevel@tonic-gate 	}
33497c478bd9Sstevel@tonic-gate 	return (ret);
33507c478bd9Sstevel@tonic-gate }
33517c478bd9Sstevel@tonic-gate 
335203986916Sjarrett static tsol_tpent_t *
get_client_template(struct sockaddr * sock)335303986916Sjarrett get_client_template(struct sockaddr *sock)
335403986916Sjarrett {
335503986916Sjarrett 	in_addr_t	v4client;
335603986916Sjarrett 	in6_addr_t	v6client;
335703986916Sjarrett 	char		v4_addr[INET_ADDRSTRLEN];
335803986916Sjarrett 	char		v6_addr[INET6_ADDRSTRLEN];
335903986916Sjarrett 	tsol_rhent_t	*rh;
336003986916Sjarrett 	tsol_tpent_t	*tp;
336103986916Sjarrett 
336203986916Sjarrett 	switch (sock->sa_family) {
336303986916Sjarrett 	case AF_INET:
336403986916Sjarrett 		v4client = ((struct sockaddr_in *)(void *)sock)->
336503986916Sjarrett 		    sin_addr.s_addr;
336603986916Sjarrett 		if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
336703986916Sjarrett 		    NULL)
336803986916Sjarrett 			return (NULL);
336903986916Sjarrett 		rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
337003986916Sjarrett 		if (rh == NULL)
337103986916Sjarrett 			return (NULL);
337203986916Sjarrett 		tp = tsol_gettpbyname(rh->rh_template);
337303986916Sjarrett 		tsol_freerhent(rh);
337403986916Sjarrett 		return (tp);
337503986916Sjarrett 		break;
337603986916Sjarrett 	case AF_INET6:
337703986916Sjarrett 		v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
337803986916Sjarrett 		if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
337903986916Sjarrett 		    NULL)
338003986916Sjarrett 			return (NULL);
338103986916Sjarrett 		rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
338203986916Sjarrett 		if (rh == NULL)
338303986916Sjarrett 			return (NULL);
338403986916Sjarrett 		tp = tsol_gettpbyname(rh->rh_template);
338503986916Sjarrett 		tsol_freerhent(rh);
338603986916Sjarrett 		return (tp);
338703986916Sjarrett 		break;
338803986916Sjarrett 	default:
338903986916Sjarrett 		return (NULL);
339003986916Sjarrett 	}
339103986916Sjarrett }
3392