Unleashed v1.4
[unleashed.git] / usr / src / cmd / fs.d / nfs / mountd / mountd.c
blobd43f04e90beb1902d4d9eefc7b5d1ce9b3cc0e0e
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
25 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 * Portions of this source code were derived from Berkeley 4.3 BSD
33 * under license from the Regents of the University of California.
36 #include <stdio.h>
37 #include <stdio_ext.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <sys/param.h>
44 #include <rpc/rpc.h>
45 #include <sys/stat.h>
46 #include <netconfig.h>
47 #include <netdir.h>
48 #include <sys/file.h>
49 #include <sys/time.h>
50 #include <sys/errno.h>
51 #include <rpcsvc/mount.h>
52 #include <sys/pathconf.h>
53 #include <sys/systeminfo.h>
54 #include <sys/utsname.h>
55 #include <sys/wait.h>
56 #include <sys/resource.h>
57 #include <signal.h>
58 #include <locale.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <netdb.h>
65 #include <thread.h>
66 #include <assert.h>
67 #include <priv_utils.h>
68 #include <nfs/auth.h>
69 #include <nfs/nfssys.h>
70 #include <nfs/nfs.h>
71 #include <nfs/nfs_sec.h>
72 #include <rpcsvc/daemon_utils.h>
73 #include <deflt.h>
74 #include "../../fslib.h"
75 #include <sharefs/share.h>
76 #include <sharefs/sharetab.h>
77 #include "../lib/sharetab.h"
78 #include "mountd.h"
79 #include <sys/sdt.h>
80 #include <libscf.h>
81 #include <limits.h>
82 #include <sys/nvpair.h>
83 #include <attr.h>
84 #include "smfcfg.h"
85 #include <pwd.h>
86 #include <grp.h>
87 #include <alloca.h>
89 extern int daemonize_init(void);
90 extern void daemonize_fini(int);
92 extern int _nfssys(int, void *);
94 struct sh_list *share_list;
96 rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
97 static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
99 static mutex_t logging_queue_lock;
100 static cond_t logging_queue_cv;
102 static share_t *find_lofsentry(char *, int *);
103 static int getclientsflavors_old(share_t *, struct cln *, int *);
104 static int getclientsflavors_new(share_t *, struct cln *, int *);
105 static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
106 gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
107 static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
108 gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
109 static void mnt(struct svc_req *, SVCXPRT *);
110 static void mnt_pathconf(struct svc_req *);
111 static int mount(struct svc_req *r);
112 static void sh_free(struct sh_list *);
113 static void umount(struct svc_req *);
114 static void umountall(struct svc_req *);
115 static int newopts(char *);
117 static int debug;
118 static int verbose;
119 static int rejecting;
120 static int mount_vers_min = MOUNTVERS;
121 static int mount_vers_max = MOUNTVERS3;
122 static int mountd_port = 0;
124 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
126 thread_t nfsauth_thread;
127 thread_t cmd_thread;
128 thread_t logging_thread;
130 typedef struct logging_data {
131 char *ld_host;
132 char *ld_path;
133 char *ld_rpath;
134 int ld_status;
135 char *ld_netid;
136 struct netbuf *ld_nb;
137 struct logging_data *ld_next;
138 } logging_data;
140 static logging_data *logging_head = NULL;
141 static logging_data *logging_tail = NULL;
144 * Our copy of some system variables obtained using sysconf(3c)
146 static long ngroups_max; /* _SC_NGROUPS_MAX */
147 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
149 /* ARGSUSED */
150 static void *
151 nfsauth_svc(void *arg)
153 int doorfd = -1;
154 uint_t darg;
155 #ifdef DEBUG
156 int dfd;
157 #endif
159 if ((doorfd = door_create(nfsauth_func, NULL,
160 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
161 syslog(LOG_ERR, "Unable to create door: %m\n");
162 exit(10);
165 #ifdef DEBUG
167 * Create a file system path for the door
169 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
170 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
171 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
172 (void) close(doorfd);
173 exit(11);
177 * Clean up any stale namespace associations
179 (void) fdetach(MOUNTD_DOOR);
182 * Register in namespace to pass to the kernel to door_ki_open
184 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
185 syslog(LOG_ERR, "Unable to fattach door: %m\n");
186 (void) close(dfd);
187 (void) close(doorfd);
188 exit(12);
190 (void) close(dfd);
191 #endif
194 * Must pass the doorfd down to the kernel.
196 darg = doorfd;
197 (void) _nfssys(MOUNTD_ARGS, &darg);
200 * Wait for incoming calls
202 /*CONSTCOND*/
203 for (;;)
204 (void) pause();
206 /*NOTREACHED*/
207 syslog(LOG_ERR, gettext("Door server exited"));
208 return (NULL);
212 * NFS command service thread code for setup and handling of the
213 * nfs_cmd requests for character set conversion and other future
214 * events.
217 static void *
218 cmd_svc(void *arg)
220 int doorfd = -1;
221 uint_t darg;
223 if ((doorfd = door_create(nfscmd_func, NULL,
224 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
225 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
226 exit(10);
230 * Must pass the doorfd down to the kernel.
232 darg = doorfd;
233 (void) _nfssys(NFSCMD_ARGS, &darg);
236 * Wait for incoming calls
238 /*CONSTCOND*/
239 for (;;)
240 (void) pause();
242 /*NOTREACHED*/
243 syslog(LOG_ERR, gettext("Cmd door server exited"));
244 return (NULL);
247 static void
248 free_logging_data(logging_data *lq)
250 if (lq != NULL) {
251 free(lq->ld_host);
252 free(lq->ld_netid);
254 if (lq->ld_nb != NULL) {
255 free(lq->ld_nb->buf);
256 free(lq->ld_nb);
259 free(lq->ld_path);
260 free(lq->ld_rpath);
262 free(lq);
266 static logging_data *
267 remove_head_of_queue(void)
269 logging_data *lq;
272 * Pull it off the queue.
274 lq = logging_head;
275 if (lq) {
276 logging_head = lq->ld_next;
279 * Drained it.
281 if (logging_head == NULL) {
282 logging_tail = NULL;
286 return (lq);
289 static void
290 do_logging_queue(logging_data *lq)
292 int cleared = 0;
293 char *host;
295 while (lq) {
296 struct cln cln;
298 if (lq->ld_host == NULL) {
299 DTRACE_PROBE(mountd, name_by_lazy);
300 cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
301 host = cln_gethost(&cln);
302 } else
303 host = lq->ld_host;
305 /* add entry to mount list */
306 if (lq->ld_rpath)
307 mntlist_new(host, lq->ld_rpath);
309 if (lq->ld_host == NULL)
310 cln_fini(&cln);
312 free_logging_data(lq);
313 cleared++;
315 (void) mutex_lock(&logging_queue_lock);
316 lq = remove_head_of_queue();
317 (void) mutex_unlock(&logging_queue_lock);
320 DTRACE_PROBE1(mountd, logging_cleared, cleared);
323 static void *
324 logging_svc(void *arg)
326 logging_data *lq;
328 for (;;) {
329 (void) mutex_lock(&logging_queue_lock);
330 while (logging_head == NULL) {
331 (void) cond_wait(&logging_queue_cv,
332 &logging_queue_lock);
335 lq = remove_head_of_queue();
336 (void) mutex_unlock(&logging_queue_lock);
338 do_logging_queue(lq);
341 /*NOTREACHED*/
342 syslog(LOG_ERR, gettext("Logging server exited"));
343 return (NULL);
346 static int
347 convert_int(int *val, char *str)
349 long lval;
351 if (str == NULL || !isdigit(*str))
352 return (-1);
354 lval = strtol(str, &str, 10);
355 if (*str != '\0' || lval > INT_MAX)
356 return (-2);
358 *val = (int)lval;
359 return (0);
363 * This function is called for each configured network type to
364 * bind and register our RPC service programs.
366 * On TCP or UDP, we may want to bind MOUNTPROG on a specific port
367 * (when mountd_port is specified) in which case we'll use the
368 * variant of svc_tp_create() that lets us pass a bind address.
370 static void
371 md_svc_tp_create(struct netconfig *nconf)
373 char port_str[8];
374 struct nd_hostserv hs;
375 struct nd_addrlist *al = NULL;
376 SVCXPRT *xprt = NULL;
377 rpcvers_t vers;
379 vers = mount_vers_max;
382 * If mountd_port is set and this is an inet transport,
383 * bind this service on the specified port. The TLI way
384 * to create such a bind address is netdir_getbyname()
385 * with the special "host" HOST_SELF_BIND. This builds
386 * an all-zeros IP address with the specified port.
388 if (mountd_port != 0 &&
389 (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
390 strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
391 int err;
393 snprintf(port_str, sizeof (port_str), "%u",
394 (unsigned short)mountd_port);
396 hs.h_host = HOST_SELF_BIND;
397 hs.h_serv = port_str;
398 err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
399 if (err == 0 && al != NULL) {
400 xprt = svc_tp_create_addr(mnt, MOUNTPROG, vers,
401 nconf, al->n_addrs);
402 netdir_free(al, ND_ADDRLIST);
404 if (xprt == NULL) {
405 syslog(LOG_ERR, "mountd: unable to create "
406 "(MOUNTD,%d) on transport %s (port %d)",
407 vers, nconf->nc_netid, mountd_port);
409 /* fall-back to default bind */
411 if (xprt == NULL) {
413 * Had mountd_port=0, or non-inet transport,
414 * or the bind to a specific port failed.
415 * Do a default bind.
417 xprt = svc_tp_create(mnt, MOUNTPROG, vers, nconf);
419 if (xprt == NULL) {
420 syslog(LOG_ERR, "mountd: unable to create "
421 "(MOUNTD,%d) on transport %s",
422 vers, nconf->nc_netid);
423 return;
427 * Register additional versions on this transport.
429 while (--vers >= mount_vers_min) {
430 if (!svc_reg(xprt, MOUNTPROG, vers, mnt, nconf)) {
431 (void) syslog(LOG_ERR, "mountd: "
432 "failed to register vers %d on %s",
433 vers, nconf->nc_netid);
439 main(int argc, char *argv[])
441 int pid;
442 int c;
443 int rpc_svc_fdunlim = 1;
444 int rpc_svc_mode = RPC_SVC_MT_AUTO;
445 int maxrecsz = RPC_MAXDATASIZE;
446 bool_t exclbind = TRUE;
447 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
448 char defval[4];
449 int defvers, ret, bufsz;
450 struct rlimit rl;
451 int listen_backlog = 0;
452 int max_threads = 0;
453 int tmp;
454 struct netconfig *nconf;
455 NCONF_HANDLE *nc;
457 int pipe_fd = -1;
460 * Mountd requires uid 0 for:
461 * /etc/rmtab updates (we could chown it to daemon)
462 * /etc/dfs/dfstab reading (it wants to lock out share which
463 * doesn't do any locking before first truncate;
464 * NFS share does; should use fcntl locking instead)
465 * Needed privileges:
466 * nfs syscall
467 * file dac search (so it can stat all files)
469 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
470 PRIV_SYS_NFS, PRIV_FILE_DAC_SEARCH,
471 PRIV_NET_PRIVADDR, NULL) == -1) {
472 (void) fprintf(stderr,
473 "%s: must be run with sufficient privileges\n",
474 argv[0]);
475 exit(1);
478 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
479 syslog(LOG_ERR, "getrlimit failed");
480 } else {
481 rl.rlim_cur = rl.rlim_max;
482 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
483 syslog(LOG_ERR, "setrlimit failed");
486 (void) enable_extended_FILE_stdio(-1, -1);
488 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
489 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
490 if (ret != SA_OK) {
491 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
492 "failed, using default value");
495 ret = nfs_smf_get_iprop("mountd_port", &mountd_port,
496 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
497 if (ret != SA_OK) {
498 syslog(LOG_ERR, "Reading of mountd_port from SMF "
499 "failed, using default value");
502 while ((c = getopt(argc, argv, "dvrm:p:")) != EOF) {
503 switch (c) {
504 case 'd':
505 debug++;
506 break;
507 case 'v':
508 verbose++;
509 break;
510 case 'r':
511 rejecting = 1;
512 break;
513 case 'm':
514 if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
515 (void) fprintf(stderr, "%s: invalid "
516 "max_threads option, using defaults\n",
517 argv[0]);
518 break;
520 max_threads = tmp;
521 break;
522 case 'p':
523 if (convert_int(&tmp, optarg) != 0 || tmp < 1 ||
524 tmp > UINT16_MAX) {
525 (void) fprintf(stderr, "%s: invalid port "
526 "number\n", argv[0]);
527 break;
529 mountd_port = tmp;
530 break;
531 default:
532 fprintf(stderr, "usage: mountd [-v] [-r]\n");
533 exit(1);
538 * Read in the NFS version values from config file.
540 bufsz = 4;
541 ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
542 SCF_TYPE_INTEGER, NFSD, &bufsz);
543 if (ret == SA_OK) {
544 errno = 0;
545 defvers = strtol(defval, (char **)NULL, 10);
546 if (errno == 0) {
547 mount_vers_min = defvers;
549 * special because NFSv2 is
550 * supported by mount v1 & v2
552 if (defvers == NFS_VERSION)
553 mount_vers_min = MOUNTVERS;
557 bufsz = 4;
558 ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
559 SCF_TYPE_INTEGER, NFSD, &bufsz);
560 if (ret == SA_OK) {
561 errno = 0;
562 defvers = strtol(defval, (char **)NULL, 10);
563 if (errno == 0) {
564 mount_vers_max = defvers;
568 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
569 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
570 if (ret != SA_OK) {
571 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
572 "failed, using default value");
576 * Sanity check versions,
577 * even though we may get versions > MOUNTVERS3, we still need
578 * to start nfsauth service, so continue on regardless of values.
580 if (mount_vers_max > MOUNTVERS3)
581 mount_vers_max = MOUNTVERS3;
582 if (mount_vers_min > mount_vers_max) {
583 fprintf(stderr, "server_versmin > server_versmax\n");
584 mount_vers_max = mount_vers_min;
586 (void) setlocale(LC_ALL, "");
587 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
588 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
589 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
590 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
592 netgroup_init();
594 #if !defined(TEXT_DOMAIN)
595 #define TEXT_DOMAIN "SYS_TEST"
596 #endif
597 (void) textdomain(TEXT_DOMAIN);
599 /* Don't drop core if the NFS module isn't loaded. */
600 (void) signal(SIGSYS, SIG_IGN);
602 if (!debug)
603 pipe_fd = daemonize_init();
606 * If we coredump it'll be in /core
608 if (chdir("/") < 0)
609 fprintf(stderr, "chdir /: %s\n", strerror(errno));
611 if (!debug)
612 openlog("mountd", LOG_PID, LOG_DAEMON);
615 * establish our lock on the lock file and write our pid to it.
616 * exit if some other process holds the lock, or if there's any
617 * error in writing/locking the file.
619 pid = _enter_daemon_lock(MOUNTD);
620 switch (pid) {
621 case 0:
622 break;
623 case -1:
624 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
625 strerror(errno));
626 exit(2);
627 default:
628 /* daemon was already running */
629 exit(0);
633 * Get required system variables
635 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
636 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
637 exit(1);
639 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
640 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
641 exit(1);
645 * Set number of file descriptors to unlimited
647 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
648 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
652 * Tell RPC that we want automatic thread mode.
653 * A new thread will be spawned for each request.
655 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
656 fprintf(stderr, "unable to set automatic MT mode\n");
657 exit(1);
661 * Enable non-blocking mode and maximum record size checks for
662 * connection oriented transports.
664 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
665 fprintf(stderr, "unable to set RPC max record size\n");
669 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
670 * from being hijacked by a bind to a more specific addr.
672 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
673 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
677 * Set the maximum number of outstanding connection
678 * indications (listen backlog) to the value specified.
680 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
681 &listen_backlog)) {
682 fprintf(stderr, "unable to set listen backlog\n");
683 exit(1);
687 * If max_threads was specified, then set the
688 * maximum number of threads to the value specified.
690 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
691 fprintf(stderr, "unable to set max_threads\n");
692 exit(1);
695 if (mountd_port < 0 || mountd_port > UINT16_MAX) {
696 fprintf(stderr, "unable to use specified port\n");
697 exit(1);
701 * Make sure to unregister any previous versions in case the
702 * user is reconfiguring the server in interesting ways.
704 svc_unreg(MOUNTPROG, MOUNTVERS);
705 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
706 svc_unreg(MOUNTPROG, MOUNTVERS3);
709 * Create the nfsauth thread with same signal disposition
710 * as the main thread. We need to create a separate thread
711 * since mountd() will be both an RPC server (for remote
712 * traffic) _and_ a doors server (for kernel upcalls).
714 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
715 fprintf(stderr,
716 gettext("Failed to create NFSAUTH svc thread\n"));
717 exit(2);
721 * Create the cmd service thread with same signal disposition
722 * as the main thread. We need to create a separate thread
723 * since mountd() will be both an RPC server (for remote
724 * traffic) _and_ a doors server (for kernel upcalls).
726 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
727 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
728 exit(2);
732 * Create an additional thread to service the rmtab
733 * logging for mount requests. Use the same
734 * signal disposition as the main thread. We create
735 * a separate thread to allow the mount request threads to
736 * clear as soon as possible.
738 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
739 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
740 exit(2);
744 * Enumerate network transports and create service listeners
745 * as appropriate for each.
747 if ((nc = setnetconfig()) == NULL) {
748 syslog(LOG_ERR, "setnetconfig failed: %m");
749 return (-1);
751 while ((nconf = getnetconfig(nc)) != NULL) {
753 * Skip things like tpi_raw, invisible...
755 if ((nconf->nc_flag & NC_VISIBLE) == 0)
756 continue;
757 if (nconf->nc_semantics != NC_TPI_CLTS &&
758 nconf->nc_semantics != NC_TPI_COTS &&
759 nconf->nc_semantics != NC_TPI_COTS_ORD)
760 continue;
762 md_svc_tp_create(nconf);
764 (void) endnetconfig(nc);
767 * Start serving
769 rmtab_load();
771 daemonize_fini(pipe_fd);
773 /* Get rid of the most dangerous basic privileges. */
774 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
775 NULL);
777 svc_run();
778 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
779 abort();
781 /* NOTREACHED */
782 return (0);
786 * Server procedure switch routine
788 void
789 mnt(struct svc_req *rqstp, SVCXPRT *transp)
791 switch (rqstp->rq_proc) {
792 case NULLPROC:
793 errno = 0;
794 if (!svc_sendreply(transp, xdr_void, (char *)0))
795 log_cant_reply(transp);
796 return;
798 case MOUNTPROC_MNT:
799 (void) mount(rqstp);
800 return;
802 case MOUNTPROC_DUMP:
803 mntlist_send(transp);
804 return;
806 case MOUNTPROC_UMNT:
807 umount(rqstp);
808 return;
810 case MOUNTPROC_UMNTALL:
811 umountall(rqstp);
812 return;
814 case MOUNTPROC_EXPORT:
815 case MOUNTPROC_EXPORTALL:
816 export(rqstp);
817 return;
819 case MOUNTPROC_PATHCONF:
820 if (rqstp->rq_vers == MOUNTVERS_POSIX)
821 mnt_pathconf(rqstp);
822 else
823 svcerr_noproc(transp);
824 return;
826 default:
827 svcerr_noproc(transp);
828 return;
832 void
833 log_cant_reply_cln(struct cln *cln)
835 int saverrno;
836 char *host;
838 saverrno = errno; /* save error code */
840 host = cln_gethost(cln);
841 if (host == NULL)
842 return;
844 errno = saverrno;
845 if (errno == 0)
846 syslog(LOG_ERR, "couldn't send reply to %s", host);
847 else
848 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
851 void
852 log_cant_reply(SVCXPRT *transp)
854 int saverrno;
855 struct cln cln;
857 saverrno = errno; /* save error code */
858 cln_init(&cln, transp);
859 errno = saverrno;
861 log_cant_reply_cln(&cln);
863 cln_fini(&cln);
867 * Answer pathconf questions for the mount point fs
869 static void
870 mnt_pathconf(struct svc_req *rqstp)
872 SVCXPRT *transp;
873 struct pathcnf p;
874 char *path, rpath[MAXPATHLEN];
875 struct stat st;
877 transp = rqstp->rq_xprt;
878 path = NULL;
879 (void) memset((caddr_t)&p, 0, sizeof (p));
881 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
882 svcerr_decode(transp);
883 return;
885 if (lstat(path, &st) < 0) {
886 _PC_SET(_PC_ERROR, p.pc_mask);
887 goto done;
890 * Get a path without symbolic links.
892 if (realpath(path, rpath) == NULL) {
893 syslog(LOG_DEBUG,
894 "mount request: realpath failed on %s: %m",
895 path);
896 _PC_SET(_PC_ERROR, p.pc_mask);
897 goto done;
899 (void) memset((caddr_t)&p, 0, sizeof (p));
901 * can't ask about devices over NFS
903 _PC_SET(_PC_MAX_CANON, p.pc_mask);
904 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
905 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
906 _PC_SET(_PC_VDISABLE, p.pc_mask);
908 errno = 0;
909 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
910 if (errno)
911 _PC_SET(_PC_LINK_MAX, p.pc_mask);
912 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
913 if (errno)
914 _PC_SET(_PC_NAME_MAX, p.pc_mask);
915 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
916 if (errno)
917 _PC_SET(_PC_PATH_MAX, p.pc_mask);
918 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
919 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
920 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
921 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
923 done:
924 errno = 0;
925 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
926 log_cant_reply(transp);
927 if (path != NULL)
928 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
932 * If the rootmount (export) option is specified, the all mount requests for
933 * subdirectories return EACCES.
935 static int
936 checkrootmount(share_t *sh, char *rpath)
938 char *val;
940 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
941 free(val);
942 if (strcmp(sh->sh_path, rpath) != 0)
943 return (0);
944 else
945 return (1);
946 } else
947 return (1);
950 #define MAX_FLAVORS 128
953 * Return only EACCES if client does not have access
954 * to this directory.
955 * "If the server exports only /a/b, an attempt to
956 * mount a/b/c will fail with ENOENT if the directory
957 * does not exist"... However, if the client
958 * does not have access to /a/b, an attacker can
959 * determine whether the directory exists.
960 * This routine checks either existence of the file or
961 * existence of the file name entry in the mount table.
962 * If the file exists and there is no file name entry,
963 * the error returned should be EACCES.
964 * If the file does not exist, it must be determined
965 * whether the client has access to a parent
966 * directory. If the client has access to a parent
967 * directory, the error returned should be ENOENT,
968 * otherwise EACCES.
970 static int
971 mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
973 char *checkpath, *dp;
974 share_t *sh = NULL;
975 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
976 int flavor_count;
978 checkpath = strdup(path);
979 if (checkpath == NULL) {
980 syslog(LOG_ERR, "mount_enoent: no memory");
981 return (EACCES);
984 /* CONSTCOND */
985 while (1) {
986 if (sh) {
987 sharefree(sh);
988 sh = NULL;
991 if ((sh = findentry(rpath)) == NULL &&
992 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
994 * There is no file name entry.
995 * If the file (with symbolic links resolved) exists,
996 * the error returned should be EACCES.
998 if (realpath_error == 0)
999 break;
1000 } else if (checkrootmount(sh, rpath) == 0) {
1002 * This is a "nosub" only export, in which case,
1003 * mounting subdirectories isn't allowed.
1004 * If the file (with symbolic links resolved) exists,
1005 * the error returned should be EACCES.
1007 if (realpath_error == 0)
1008 break;
1009 } else {
1011 * Check permissions in mount table.
1013 if (newopts(sh->sh_opts))
1014 flavor_count = getclientsflavors_new(sh, cln,
1015 flavor_list);
1016 else
1017 flavor_count = getclientsflavors_old(sh, cln,
1018 flavor_list);
1019 if (flavor_count != 0) {
1021 * Found entry in table and
1022 * client has correct permissions.
1024 reply_error = ENOENT;
1025 break;
1030 * Check all parent directories.
1032 dp = strrchr(checkpath, '/');
1033 if (dp == NULL)
1034 break;
1035 *dp = '\0';
1036 if (strlen(checkpath) == 0)
1037 break;
1039 * Get the real path (no symbolic links in it)
1041 if (realpath(checkpath, rpath) == NULL) {
1042 if (errno != ENOENT)
1043 break;
1044 } else {
1045 realpath_error = 0;
1049 if (sh)
1050 sharefree(sh);
1051 free(checkpath);
1052 return (reply_error);
1056 * We need to inform the caller whether or not we were
1057 * able to add a node to the queue. If we are not, then
1058 * it is up to the caller to go ahead and log the data.
1060 static int
1061 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
1062 char *rpath, int status, int error)
1064 logging_data *lq;
1065 struct netbuf *nb;
1067 lq = (logging_data *)calloc(1, sizeof (logging_data));
1068 if (lq == NULL)
1069 goto cleanup;
1072 * We might not yet have the host...
1074 if (host) {
1075 DTRACE_PROBE1(mountd, log_host, host);
1076 lq->ld_host = strdup(host);
1077 if (lq->ld_host == NULL)
1078 goto cleanup;
1079 } else {
1080 DTRACE_PROBE(mountd, log_no_host);
1082 lq->ld_netid = strdup(transp->xp_netid);
1083 if (lq->ld_netid == NULL)
1084 goto cleanup;
1086 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1087 if (lq->ld_nb == NULL)
1088 goto cleanup;
1090 nb = svc_getrpccaller(transp);
1091 if (nb == NULL) {
1092 DTRACE_PROBE(mountd, e__nb__enqueue);
1093 goto cleanup;
1096 DTRACE_PROBE(mountd, nb_set_enqueue);
1098 lq->ld_nb->maxlen = nb->maxlen;
1099 lq->ld_nb->len = nb->len;
1101 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1102 if (lq->ld_nb->buf == NULL)
1103 goto cleanup;
1105 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1108 lq->ld_path = strdup(path);
1109 if (lq->ld_path == NULL)
1110 goto cleanup;
1112 if (!error) {
1113 lq->ld_rpath = strdup(rpath);
1114 if (lq->ld_rpath == NULL)
1115 goto cleanup;
1118 lq->ld_status = status;
1121 * Add to the tail of the logging queue.
1123 (void) mutex_lock(&logging_queue_lock);
1124 if (logging_tail == NULL) {
1125 logging_tail = logging_head = lq;
1126 } else {
1127 logging_tail->ld_next = lq;
1128 logging_tail = lq;
1130 (void) cond_signal(&logging_queue_cv);
1131 (void) mutex_unlock(&logging_queue_lock);
1133 return (TRUE);
1135 cleanup:
1137 free_logging_data(lq);
1139 return (FALSE);
1143 #define CLN_CLNAMES (1 << 0)
1144 #define CLN_HOST (1 << 1)
1146 static void
1147 cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1148 struct netbuf *nbuf)
1150 if ((cln->transp = transp) != NULL) {
1151 assert(netid == NULL && nbuf == NULL);
1152 cln->netid = transp->xp_netid;
1153 cln->nbuf = svc_getrpccaller(transp);
1154 } else {
1155 cln->netid = netid;
1156 cln->nbuf = nbuf;
1159 cln->nconf = NULL;
1160 cln->clnames = NULL;
1161 cln->host = NULL;
1163 cln->flags = 0;
1166 void
1167 cln_init(struct cln *cln, SVCXPRT *transp)
1169 cln_init_common(cln, transp, NULL, NULL);
1172 void
1173 cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1175 cln_init_common(cln, NULL, netid, nbuf);
1178 void
1179 cln_fini(struct cln *cln)
1181 if (cln->nconf != NULL)
1182 freenetconfigent(cln->nconf);
1184 if (cln->clnames != NULL)
1185 netdir_free(cln->clnames, ND_HOSTSERVLIST);
1187 free(cln->host);
1190 struct netbuf *
1191 cln_getnbuf(struct cln *cln)
1193 return (cln->nbuf);
1196 struct nd_hostservlist *
1197 cln_getclientsnames(struct cln *cln)
1199 if ((cln->flags & CLN_CLNAMES) == 0) {
1201 * nconf is not needed if we do not have nbuf (see
1202 * cln_gethost() too), so we check for nbuf and in a case it is
1203 * NULL we do not try to get nconf.
1205 if (cln->netid != NULL && cln->nbuf != NULL) {
1206 cln->nconf = getnetconfigent(cln->netid);
1207 if (cln->nconf == NULL)
1208 syslog(LOG_ERR, "%s: getnetconfigent failed",
1209 cln->netid);
1212 if (cln->nconf != NULL && cln->nbuf != NULL)
1213 (void) __netdir_getbyaddr_nosrv(cln->nconf,
1214 &cln->clnames, cln->nbuf);
1216 cln->flags |= CLN_CLNAMES;
1219 return (cln->clnames);
1223 * Return B_TRUE if the host is already available at no cost
1225 boolean_t
1226 cln_havehost(struct cln *cln)
1228 return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1231 char *
1232 cln_gethost(struct cln *cln)
1234 if (cln_getclientsnames(cln) != NULL)
1235 return (cln->clnames->h_hostservs[0].h_host);
1237 if ((cln->flags & CLN_HOST) == 0) {
1238 if (cln->nconf == NULL || cln->nbuf == NULL) {
1239 cln->host = strdup("(anon)");
1240 } else {
1241 char host[MAXIPADDRLEN];
1243 if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1244 struct sockaddr_in *sa;
1246 /* LINTED pointer alignment */
1247 sa = (struct sockaddr_in *)(cln->nbuf->buf);
1248 (void) inet_ntoa_r(sa->sin_addr, host);
1250 cln->host = strdup(host);
1251 } else if (strcmp(cln->nconf->nc_protofmly,
1252 NC_INET6) == 0) {
1253 struct sockaddr_in6 *sa;
1255 /* LINTED pointer alignment */
1256 sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1257 (void) inet_ntop(AF_INET6,
1258 sa->sin6_addr.s6_addr,
1259 host, INET6_ADDRSTRLEN);
1261 cln->host = strdup(host);
1262 } else {
1263 syslog(LOG_ERR, gettext("Client's address is "
1264 "neither IPv4 nor IPv6"));
1266 cln->host = strdup("(anon)");
1270 cln->flags |= CLN_HOST;
1273 return (cln->host);
1277 * Check mount requests, add to mounted list if ok
1279 static int
1280 mount(struct svc_req *rqstp)
1282 SVCXPRT *transp;
1283 int version, vers;
1284 struct fhstatus fhs;
1285 struct mountres3 mountres3;
1286 char fh[FHSIZE3];
1287 int len = FHSIZE3;
1288 char *path, rpath[MAXPATHLEN];
1289 share_t *sh = NULL;
1290 struct cln cln;
1291 char *host = NULL;
1292 int error = 0, lofs_tried = 0, enqueued;
1293 int flavor_list[MAX_FLAVORS];
1294 int flavor_count;
1295 ucred_t *uc = NULL;
1297 int status;
1299 transp = rqstp->rq_xprt;
1300 version = rqstp->rq_vers;
1301 path = NULL;
1303 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1304 svcerr_decode(transp);
1305 return (EACCES);
1308 cln_init(&cln, transp);
1311 * Put off getting the name for the client until we
1312 * need it. This is a performance gain. If we are logging,
1313 * then we don't care about performance and might as well
1314 * get the host name now in case we need to spit out an
1315 * error message.
1317 if (verbose) {
1318 DTRACE_PROBE(mountd, name_by_verbose);
1319 if ((host = cln_gethost(&cln)) == NULL) {
1321 * We failed to get a name for the client, even
1322 * 'anon', probably because we ran out of memory.
1323 * In this situation it doesn't make sense to
1324 * allow the mount to succeed.
1326 error = EACCES;
1327 goto reply;
1332 * If the version being used is less than the minimum version,
1333 * the filehandle translation should not be provided to the
1334 * client.
1336 if (rejecting || version < mount_vers_min) {
1337 if (verbose)
1338 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1339 host, path);
1340 error = EACCES;
1341 goto reply;
1345 * Get the real path (no symbolic links in it)
1347 if (realpath(path, rpath) == NULL) {
1348 error = errno;
1349 if (verbose)
1350 syslog(LOG_ERR,
1351 "mount request: realpath: %s: %m", path);
1352 if (error == ENOENT)
1353 error = mount_enoent_error(&cln, path, rpath,
1354 flavor_list);
1355 goto reply;
1358 if ((sh = findentry(rpath)) == NULL &&
1359 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1360 error = EACCES;
1361 goto reply;
1365 * Check if this is a "nosub" only export, in which case, mounting
1366 * subdirectories isn't allowed. Bug 1184573.
1368 if (checkrootmount(sh, rpath) == 0) {
1369 error = EACCES;
1370 goto reply;
1373 if (newopts(sh->sh_opts))
1374 flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
1375 else
1376 flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
1378 if (flavor_count == 0) {
1379 error = EACCES;
1380 goto reply;
1384 * Now get the filehandle.
1386 * NFS V2 clients get a 32 byte filehandle.
1387 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1388 * the embedded FIDs.
1390 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1392 /* LINTED pointer alignment */
1393 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1394 if (errno == EINVAL &&
1395 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1396 errno = 0;
1397 continue;
1399 error = errno == EINVAL ? EACCES : errno;
1400 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1401 path);
1402 break;
1405 if (version == MOUNTVERS3) {
1406 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1407 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1408 } else {
1409 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1412 reply:
1413 if (uc != NULL)
1414 ucred_free(uc);
1416 switch (version) {
1417 case MOUNTVERS:
1418 case MOUNTVERS_POSIX:
1419 if (error == EINVAL)
1420 fhs.fhs_status = NFSERR_ACCES;
1421 else if (error == EREMOTE)
1422 fhs.fhs_status = NFSERR_REMOTE;
1423 else
1424 fhs.fhs_status = error;
1426 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1427 log_cant_reply_cln(&cln);
1429 status = fhs.fhs_status;
1430 break;
1432 case MOUNTVERS3:
1433 if (!error) {
1434 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1435 flavor_list;
1436 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1437 flavor_count;
1439 } else if (error == ENAMETOOLONG)
1440 error = MNT3ERR_NAMETOOLONG;
1442 mountres3.fhs_status = error;
1443 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1444 log_cant_reply_cln(&cln);
1446 status = mountres3.fhs_status;
1447 break;
1450 if (cln_havehost(&cln))
1451 host = cln_gethost(&cln);
1453 if (verbose)
1454 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1455 (host == NULL) ? "unknown host" : host,
1456 error ? "denied" : "mounted", path);
1459 * If we can not create a queue entry, go ahead and do it
1460 * in the context of this thread.
1462 enqueued = enqueue_logging_data(host, transp, path, rpath,
1463 status, error);
1464 if (enqueued == FALSE) {
1465 if (host == NULL) {
1466 DTRACE_PROBE(mountd, name_by_in_thread);
1467 host = cln_gethost(&cln);
1470 DTRACE_PROBE(mountd, logged_in_thread);
1471 if (!error)
1472 mntlist_new(host, rpath); /* add entry to mount list */
1475 if (path != NULL)
1476 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1478 if (sh)
1479 sharefree(sh);
1481 cln_fini(&cln);
1483 return (error);
1487 * Determine whether two paths are within the same file system.
1488 * Returns nonzero (true) if paths are the same, zero (false) if
1489 * they are different. If an error occurs, return false.
1491 * Use the actual FSID if it's available (via getattrat()); otherwise,
1492 * fall back on st_dev.
1494 * With ZFS snapshots, st_dev differs from the regular file system
1495 * versus the snapshot. But the fsid is the same throughout. Thus
1496 * the fsid is a better test.
1498 static int
1499 same_file_system(const char *path1, const char *path2)
1501 uint64_t fsid1, fsid2;
1502 struct stat st1, st2;
1503 nvlist_t *nvl1 = NULL;
1504 nvlist_t *nvl2 = NULL;
1506 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1507 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1508 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1509 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1510 nvlist_free(nvl1);
1511 nvlist_free(nvl2);
1513 * We have found fsid's for both paths.
1516 if (fsid1 == fsid2)
1517 return (B_TRUE);
1519 return (B_FALSE);
1522 nvlist_free(nvl1);
1523 nvlist_free(nvl2);
1526 * We were unable to find fsid's for at least one of the paths.
1527 * fall back on st_dev.
1530 if (stat(path1, &st1) < 0) {
1531 syslog(LOG_NOTICE, "%s: %m", path1);
1532 return (B_FALSE);
1534 if (stat(path2, &st2) < 0) {
1535 syslog(LOG_NOTICE, "%s: %m", path2);
1536 return (B_FALSE);
1539 if (st1.st_dev == st2.st_dev)
1540 return (B_TRUE);
1542 return (B_FALSE);
1545 share_t *
1546 findentry(char *path)
1548 share_t *sh = NULL;
1549 struct sh_list *shp;
1550 char *p1, *p2;
1552 check_sharetab();
1554 (void) rw_rdlock(&sharetab_lock);
1556 for (shp = share_list; shp; shp = shp->shl_next) {
1557 sh = shp->shl_sh;
1558 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1559 if (*p1 == '\0')
1560 goto done; /* exact match */
1563 * Now compare the pathnames for three cases:
1565 * Parent: /export/foo (no trailing slash on parent)
1566 * Child: /export/foo/bar
1568 * Parent: /export/foo/ (trailing slash on parent)
1569 * Child: /export/foo/bar
1571 * Parent: /export/foo/ (no trailing slash on child)
1572 * Child: /export/foo
1574 if ((*p1 == '\0' && *p2 == '/') ||
1575 (*p1 == '\0' && *(p1-1) == '/') ||
1576 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1578 * We have a subdirectory. Test whether the
1579 * subdirectory is in the same file system.
1581 if (same_file_system(path, sh->sh_path))
1582 goto done;
1585 done:
1586 sh = shp ? sharedup(sh) : NULL;
1588 (void) rw_unlock(&sharetab_lock);
1590 return (sh);
1594 static int
1595 is_substring(char **mntp, char **path)
1597 char *p1 = *mntp, *p2 = *path;
1599 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1600 return (1);
1601 else if (*p1 == '\0' && *p2 == '/')
1602 return (1);
1603 else if (*p1 == '\0' && *(p1-1) == '/') {
1604 *path = --p2; /* we need the slash in p2 */
1605 return (1);
1606 } else if (*p2 == '\0') {
1607 while (*p1 == '/')
1608 p1++;
1609 if (*p1 == '\0') /* exact match */
1610 return (1);
1612 return (0);
1616 * find_lofsentry() searches for the real path which this requested LOFS path
1617 * (rpath) shadows. If found, it will return the sharetab entry of
1618 * the real path that corresponds to the LOFS path.
1619 * We first search mnttab to see if the requested path is an automounted
1620 * path. If it is an automounted path, it will trigger the mount by stat()ing
1621 * the requested path. Note that it is important to check that this path is
1622 * actually an automounted path, otherwise we would stat() a path which may
1623 * turn out to be NFS and block indefinitely on a dead server. The automounter
1624 * times-out if the server is dead, so there's no risk of hanging this
1625 * thread waiting for stat().
1626 * After the mount has been triggered (if necessary), we look for a
1627 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1628 * is a substring of the rpath. If found, we construct a new path by
1629 * concatenating the mnt_special and the remaining of rpath, call findentry()
1630 * to make sure the 'real path' is shared.
1632 static share_t *
1633 find_lofsentry(char *rpath, int *done_flag)
1635 struct stat r_stbuf;
1636 mntlist_t *ml, *mntl, *mntpnt = NULL;
1637 share_t *retcode = NULL;
1638 char tmp_path[MAXPATHLEN];
1639 int mntpnt_len = 0, tmp;
1640 char *p1, *p2;
1642 if ((*done_flag)++)
1643 return (retcode);
1646 * While fsgetmntlist() uses lockf() to
1647 * lock the mnttab before reading it in,
1648 * the lock ignores threads in the same process.
1649 * Read in the mnttab with the protection of a mutex.
1651 (void) mutex_lock(&mnttab_lock);
1652 mntl = fsgetmntlist();
1653 (void) mutex_unlock(&mnttab_lock);
1656 * Obtain the mountpoint for the requested path.
1658 for (ml = mntl; ml; ml = ml->mntl_next) {
1659 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1660 *p1 == *p2 && *p1; p1++, p2++)
1662 if (is_substring(&p1, &p2) &&
1663 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1664 mntpnt = ml;
1665 mntpnt_len = tmp;
1670 * If the path needs to be autoFS mounted, trigger the mount by
1671 * stat()ing it. This is determined by checking whether the
1672 * mountpoint we just found is of type autofs.
1674 if (mntpnt != NULL &&
1675 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1677 * The requested path is a substring of an autoFS filesystem.
1678 * Trigger the mount.
1680 if (stat(rpath, &r_stbuf) < 0) {
1681 if (verbose)
1682 syslog(LOG_NOTICE, "%s: %m", rpath);
1683 goto done;
1685 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1687 * The requested path is a directory, stat(2) it
1688 * again with a trailing '.' to force the autoFS
1689 * module to trigger the mount of indirect
1690 * automount entries, such as /net/jurassic/.
1692 if (strlen(rpath) + 2 > MAXPATHLEN) {
1693 if (verbose) {
1694 syslog(LOG_NOTICE,
1695 "%s/.: exceeds MAXPATHLEN %d",
1696 rpath, MAXPATHLEN);
1698 goto done;
1700 (void) strcpy(tmp_path, rpath);
1701 (void) strcat(tmp_path, "/.");
1703 if (stat(tmp_path, &r_stbuf) < 0) {
1704 if (verbose)
1705 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1706 goto done;
1711 * The mount has been triggered, re-read mnttab to pick up
1712 * the changes made by autoFS.
1714 fsfreemntlist(mntl);
1715 (void) mutex_lock(&mnttab_lock);
1716 mntl = fsgetmntlist();
1717 (void) mutex_unlock(&mnttab_lock);
1721 * The autoFS mountpoint has been triggered if necessary,
1722 * now search mnttab again to determine if the requested path
1723 * is an LOFS mount of a shared path.
1725 mntpnt_len = 0;
1726 for (ml = mntl; ml; ml = ml->mntl_next) {
1727 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1728 continue;
1730 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1731 *p1 == *p2 && *p1; p1++, p2++)
1734 if (is_substring(&p1, &p2) &&
1735 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1736 mntpnt_len = tmp;
1738 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1739 MAXPATHLEN) {
1740 if (verbose) {
1741 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1742 ml->mntl_mnt->mnt_special, p2,
1743 MAXPATHLEN);
1745 if (retcode)
1746 sharefree(retcode);
1747 retcode = NULL;
1748 goto done;
1751 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1752 (void) strcat(tmp_path, p2);
1753 if (retcode)
1754 sharefree(retcode);
1755 retcode = findentry(tmp_path);
1759 if (retcode) {
1760 assert(strlen(tmp_path) > 0);
1761 (void) strcpy(rpath, tmp_path);
1764 done:
1765 fsfreemntlist(mntl);
1766 return (retcode);
1770 * Determine whether an access list grants rights to a particular host.
1771 * We match on aliases of the hostname as well as on the canonical name.
1772 * Names in the access list may be either hosts or netgroups; they're
1773 * not distinguished syntactically. We check for hosts first because
1774 * it's cheaper, then try netgroups.
1776 * Return values:
1777 * 1 - access is granted
1778 * 0 - access is denied
1779 * -1 - an error occured
1782 in_access_list(struct cln *cln,
1783 char *access_list) /* N.B. we clobber this "input" parameter */
1785 char addr[INET_ADDRSTRLEN];
1786 char buff[256];
1787 int nentries = 0;
1788 char *cstr = access_list;
1789 char *gr = access_list;
1790 int i;
1791 int response;
1792 int ret;
1793 struct netbuf *pnb;
1794 struct nd_hostservlist *clnames = NULL;
1796 /* If no access list - then it's unrestricted */
1797 if (access_list == NULL || *access_list == '\0')
1798 return (1);
1800 if ((pnb = cln_getnbuf(cln)) == NULL)
1801 return (-1);
1803 for (;;) {
1804 if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1805 if (*cstr == ':') {
1806 *cstr = '\0';
1807 } else {
1808 assert(*cstr == '[');
1809 cstr = strchr(cstr + 1, ']');
1810 if (cstr == NULL)
1811 return (-1);
1812 cstr++;
1813 continue;
1818 * If the list name has a '-' prepended then a match of
1819 * the following name implies failure instead of success.
1821 if (*gr == '-') {
1822 response = 0;
1823 gr++;
1824 } else {
1825 response = 1;
1829 * First check if we have '@' entry, as it doesn't
1830 * require client hostname.
1832 if (*gr == '@') {
1833 gr++;
1835 /* Netname support */
1836 if (!isdigit(*gr) && *gr != '[') {
1837 struct netent n, *np;
1839 if ((np = getnetbyname_r(gr, &n, buff,
1840 sizeof (buff))) != NULL &&
1841 np->n_net != 0) {
1842 while ((np->n_net & 0xFF000000u) == 0)
1843 np->n_net <<= 8;
1844 np->n_net = htonl(np->n_net);
1845 if (inet_ntop(AF_INET, &np->n_net, addr,
1846 INET_ADDRSTRLEN) == NULL)
1847 break;
1848 ret = inet_matchaddr(pnb->buf, addr);
1849 if (ret == -1) {
1850 if (errno == EINVAL) {
1851 syslog(LOG_WARNING,
1852 "invalid access "
1853 "list entry: %s",
1854 addr);
1856 return (-1);
1857 } else if (ret == 1) {
1858 return (response);
1861 } else {
1862 ret = inet_matchaddr(pnb->buf, gr);
1863 if (ret == -1) {
1864 if (errno == EINVAL) {
1865 syslog(LOG_WARNING,
1866 "invalid access list "
1867 "entry: %s", gr);
1869 return (-1);
1870 } else if (ret == 1) {
1871 return (response);
1875 goto next;
1879 * No other checks can be performed if client address
1880 * can't be resolved.
1882 if ((clnames = cln_getclientsnames(cln)) == NULL)
1883 goto next;
1885 /* Otherwise loop through all client hostname aliases */
1886 for (i = 0; i < clnames->h_cnt; i++) {
1887 char *host = clnames->h_hostservs[i].h_host;
1890 * If the list name begins with a dot then
1891 * do a domain name suffix comparison.
1892 * A single dot matches any name with no
1893 * suffix.
1895 if (*gr == '.') {
1896 if (*(gr + 1) == '\0') { /* single dot */
1897 if (strchr(host, '.') == NULL)
1898 return (response);
1899 } else {
1900 int off = strlen(host) - strlen(gr);
1901 if (off > 0 &&
1902 strcasecmp(host + off, gr) == 0) {
1903 return (response);
1906 } else {
1907 /* Just do a hostname match */
1908 if (strcasecmp(gr, host) == 0)
1909 return (response);
1913 nentries++;
1915 next:
1916 if (cstr == NULL)
1917 break;
1919 gr = ++cstr;
1922 if (clnames == NULL)
1923 return (0);
1925 return (netgroup_check(clnames, access_list, nentries));
1929 static char *optlist[] = {
1930 #define OPT_RO 0
1931 SHOPT_RO,
1932 #define OPT_RW 1
1933 SHOPT_RW,
1934 #define OPT_ROOT 2
1935 SHOPT_ROOT,
1936 #define OPT_SECURE 3
1937 SHOPT_SECURE,
1938 #define OPT_ANON 4
1939 SHOPT_ANON,
1940 #define OPT_WINDOW 5
1941 SHOPT_WINDOW,
1942 #define OPT_NOSUID 6
1943 SHOPT_NOSUID,
1944 #define OPT_ACLOK 7
1945 SHOPT_ACLOK,
1946 #define OPT_SEC 8
1947 SHOPT_SEC,
1948 #define OPT_NONE 9
1949 SHOPT_NONE,
1950 #define OPT_UIDMAP 10
1951 SHOPT_UIDMAP,
1952 #define OPT_GIDMAP 11
1953 SHOPT_GIDMAP,
1954 NULL
1957 static int
1958 map_flavor(char *str)
1960 seconfig_t sec;
1962 if (nfs_getseconfig_byname(str, &sec))
1963 return (-1);
1965 return (sec.sc_nfsnum);
1969 * If the option string contains a "sec="
1970 * option, then use new option syntax.
1972 static int
1973 newopts(char *opts)
1975 char *head, *p, *val;
1977 if (!opts || *opts == '\0')
1978 return (0);
1980 head = strdup(opts);
1981 if (head == NULL) {
1982 syslog(LOG_ERR, "opts: no memory");
1983 return (0);
1986 p = head;
1987 while (*p) {
1988 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
1989 free(head);
1990 return (1);
1994 free(head);
1995 return (0);
1999 * Given an export and the clients hostname(s)
2000 * determine the security flavors that this
2001 * client is permitted to use.
2003 * This routine is called only for "old" syntax, i.e.
2004 * only one security flavor is allowed. So we need
2005 * to determine two things: the particular flavor,
2006 * and whether the client is allowed to use this
2007 * flavor, i.e. is in the access list.
2009 * Note that if there is no access list, then the
2010 * default is that access is granted.
2012 static int
2013 getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
2015 char *opts, *p, *val;
2016 boolean_t ok = B_FALSE;
2017 int defaultaccess = 1;
2018 boolean_t reject = B_FALSE;
2020 opts = strdup(sh->sh_opts);
2021 if (opts == NULL) {
2022 syslog(LOG_ERR, "getclientsflavors: no memory");
2023 return (0);
2026 flavors[0] = AUTH_SYS;
2027 p = opts;
2029 while (*p) {
2031 switch (getsubopt(&p, optlist, &val)) {
2032 case OPT_SECURE:
2033 flavors[0] = AUTH_DES;
2034 break;
2036 case OPT_RO:
2037 case OPT_RW:
2038 defaultaccess = 0;
2039 if (in_access_list(cln, val) > 0)
2040 ok = B_TRUE;
2041 break;
2043 case OPT_NONE:
2044 defaultaccess = 0;
2045 if (in_access_list(cln, val) > 0)
2046 reject = B_TRUE;
2050 free(opts);
2052 /* none takes precedence over everything else */
2053 if (reject)
2054 ok = B_FALSE;
2056 return (defaultaccess || ok);
2060 * Given an export and the clients hostname(s)
2061 * determine the security flavors that this
2062 * client is permitted to use.
2064 * This is somewhat more complicated than the "old"
2065 * routine because the options may contain multiple
2066 * security flavors (sec=) each with its own access
2067 * lists. So a client could be granted access based
2068 * on a number of security flavors. Note that the
2069 * type of access might not always be the same, the
2070 * client may get readonly access with one flavor
2071 * and readwrite with another, however the client
2072 * is not told this detail, it gets only the list
2073 * of flavors, and only if the client is using
2074 * version 3 of the mount protocol.
2076 static int
2077 getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
2079 char *opts, *p, *val;
2080 char *lasts;
2081 char *f;
2082 boolean_t defaultaccess = B_TRUE; /* default access is rw */
2083 boolean_t access_ok = B_FALSE;
2084 int count, c;
2085 boolean_t reject = B_FALSE;
2087 opts = strdup(sh->sh_opts);
2088 if (opts == NULL) {
2089 syslog(LOG_ERR, "getclientsflavors: no memory");
2090 return (0);
2093 p = opts;
2094 count = c = 0;
2096 while (*p) {
2097 switch (getsubopt(&p, optlist, &val)) {
2098 case OPT_SEC:
2099 if (reject)
2100 access_ok = B_FALSE;
2103 * Before a new sec=xxx option, check if we need
2104 * to move the c index back to the previous count.
2106 if (!defaultaccess && !access_ok) {
2107 c = count;
2110 /* get all the sec=f1[:f2] flavors */
2111 while ((f = strtok_r(val, ":", &lasts)) != NULL) {
2112 flavors[c++] = map_flavor(f);
2113 val = NULL;
2116 /* for a new sec=xxx option, default is rw access */
2117 defaultaccess = B_TRUE;
2118 access_ok = B_FALSE;
2119 reject = B_FALSE;
2120 break;
2122 case OPT_RO:
2123 case OPT_RW:
2124 defaultaccess = B_FALSE;
2125 if (in_access_list(cln, val) > 0)
2126 access_ok = B_TRUE;
2127 break;
2129 case OPT_NONE:
2130 defaultaccess = B_FALSE;
2131 if (in_access_list(cln, val) > 0)
2132 reject = B_TRUE; /* none overides rw/ro */
2133 break;
2137 if (reject)
2138 access_ok = B_FALSE;
2140 if (!defaultaccess && !access_ok)
2141 c = count;
2143 free(opts);
2145 return (c);
2149 * This is a tricky piece of code that parses the
2150 * share options looking for a match on the auth
2151 * flavor that the client is using. If it finds
2152 * a match, then the client is given ro, rw, or
2153 * no access depending whether it is in the access
2154 * list. There is a special case for "secure"
2155 * flavor. Other flavors are values of the new "sec=" option.
2158 check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2159 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2160 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2162 if (newopts(sh->sh_opts))
2163 return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2164 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2165 srv_gids));
2166 else
2167 return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2168 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2169 srv_gids));
2172 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2175 * Get supplemental groups for uid
2177 static int
2178 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2180 struct passwd pwd;
2181 char *pwbuf = alloca(pw_size);
2182 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2183 int tmpngrps;
2184 struct passwd *result;
2186 getpwuid_r(uid, &pwd, pwbuf, pw_size, &result);
2187 if (!result)
2188 return (-1);
2190 tmpgrps[0] = pwd.pw_gid;
2192 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2193 if (tmpngrps <= 0) {
2194 syslog(LOG_WARNING,
2195 "getusergroups(): Unable to get groups for user %s",
2196 pwd.pw_name);
2198 return (-1);
2201 *grps = malloc(tmpngrps * sizeof (gid_t));
2202 if (*grps == NULL) {
2203 syslog(LOG_ERR,
2204 "getusergroups(): Memory allocation failed: %m");
2206 return (-1);
2209 *ngrps = tmpngrps;
2210 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2212 return (0);
2216 * is_a_number(number)
2218 * is the string a number in one of the forms we want to use?
2221 static int
2222 is_a_number(char *number)
2224 int ret = 1;
2225 int hex = 0;
2227 if (strncmp(number, "0x", 2) == 0) {
2228 number += 2;
2229 hex = 1;
2230 } else if (*number == '-') {
2231 number++; /* skip the minus */
2233 while (ret == 1 && *number != '\0') {
2234 if (hex) {
2235 ret = isxdigit(*number++);
2236 } else {
2237 ret = isdigit(*number++);
2240 return (ret);
2243 static boolean_t
2244 get_uid(char *value, uid_t *uid)
2246 if (!is_a_number(value)) {
2247 struct passwd *pw;
2249 * in this case it would have to be a
2250 * user name
2252 pw = getpwnam(value);
2253 if (pw == NULL)
2254 return (B_FALSE);
2255 *uid = pw->pw_uid;
2256 endpwent();
2257 } else {
2258 uint64_t intval;
2259 intval = strtoull(value, NULL, 0);
2260 if (intval > UID_MAX && intval != -1)
2261 return (B_FALSE);
2262 *uid = (uid_t)intval;
2265 return (B_TRUE);
2268 static boolean_t
2269 get_gid(char *value, gid_t *gid)
2271 if (!is_a_number(value)) {
2272 struct group *gr;
2274 * in this case it would have to be a
2275 * group name
2277 gr = getgrnam(value);
2278 if (gr == NULL)
2279 return (B_FALSE);
2280 *gid = gr->gr_gid;
2281 endgrent();
2282 } else {
2283 uint64_t intval;
2284 intval = strtoull(value, NULL, 0);
2285 if (intval > UID_MAX && intval != -1)
2286 return (B_FALSE);
2287 *gid = (gid_t)intval;
2290 return (B_TRUE);
2293 static int
2294 check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2295 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2296 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2298 char *opts, *p, *val;
2299 int match; /* Set when a flavor is matched */
2300 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2301 int list = 0; /* Set when "ro", "rw" is found */
2302 int ro_val = 0; /* Set if ro option is 'ro=' */
2303 int rw_val = 0; /* Set if rw option is 'rw=' */
2305 boolean_t map_deny = B_FALSE;
2307 opts = strdup(sh->sh_opts);
2308 if (opts == NULL) {
2309 syslog(LOG_ERR, "check_client: no memory");
2310 return (0);
2314 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2315 * locally for all of them
2317 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2318 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2319 perm |= NFSAUTH_GROUPS;
2321 p = opts;
2322 match = AUTH_UNIX;
2324 while (*p) {
2325 switch (getsubopt(&p, optlist, &val)) {
2327 case OPT_SECURE:
2328 match = AUTH_DES;
2330 if (perm & NFSAUTH_GROUPS) {
2331 free(*srv_gids);
2332 *srv_ngids = 0;
2333 *srv_gids = NULL;
2334 perm &= ~NFSAUTH_GROUPS;
2337 break;
2339 case OPT_RO:
2340 list++;
2341 if (val != NULL)
2342 ro_val++;
2343 if (in_access_list(cln, val) > 0)
2344 perm |= NFSAUTH_RO;
2345 break;
2347 case OPT_RW:
2348 list++;
2349 if (val != NULL)
2350 rw_val++;
2351 if (in_access_list(cln, val) > 0)
2352 perm |= NFSAUTH_RW;
2353 break;
2355 case OPT_ROOT:
2357 * Check if the client is in
2358 * the root list. Only valid
2359 * for AUTH_SYS.
2361 if (flavor != AUTH_SYS)
2362 break;
2364 if (val == NULL || *val == '\0')
2365 break;
2367 if (clnt_uid != 0)
2368 break;
2370 if (in_access_list(cln, val) > 0) {
2371 perm |= NFSAUTH_ROOT;
2372 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2373 map_deny = B_FALSE;
2375 if (perm & NFSAUTH_GROUPS) {
2376 free(*srv_gids);
2377 *srv_ngids = 0;
2378 *srv_gids = NULL;
2379 perm &= ~NFSAUTH_GROUPS;
2382 break;
2384 case OPT_NONE:
2386 * Check if the client should have no access
2387 * to this share at all. This option behaves
2388 * more like "root" than either "rw" or "ro".
2390 if (in_access_list(cln, val) > 0)
2391 perm |= NFSAUTH_DENIED;
2392 break;
2394 case OPT_UIDMAP: {
2395 char *c;
2396 char *n;
2399 * The uidmap is supported for AUTH_SYS only.
2401 if (flavor != AUTH_SYS)
2402 break;
2404 if (perm & NFSAUTH_UIDMAP || map_deny)
2405 break;
2407 for (c = val; c != NULL; c = n) {
2408 char *s;
2409 char *al;
2410 uid_t srv;
2412 n = strchr(c, '~');
2413 if (n != NULL)
2414 *n++ = '\0';
2416 s = strchr(c, ':');
2417 if (s != NULL) {
2418 *s++ = '\0';
2419 al = strchr(s, ':');
2420 if (al != NULL)
2421 *al++ = '\0';
2424 if (s == NULL || al == NULL)
2425 continue;
2427 if (*c == '\0') {
2428 if (clnt_uid != (uid_t)-1)
2429 continue;
2430 } else if (strcmp(c, "*") != 0) {
2431 uid_t clnt;
2433 if (!get_uid(c, &clnt))
2434 continue;
2436 if (clnt_uid != clnt)
2437 continue;
2440 if (*s == '\0')
2441 srv = UID_NOBODY;
2442 else if (!get_uid(s, &srv))
2443 continue;
2444 else if (srv == (uid_t)-1) {
2445 map_deny = B_TRUE;
2446 break;
2449 if (in_access_list(cln, al) > 0) {
2450 *srv_uid = srv;
2451 perm |= NFSAUTH_UIDMAP;
2453 if (perm & NFSAUTH_GROUPS) {
2454 free(*srv_gids);
2455 *srv_ngids = 0;
2456 *srv_gids = NULL;
2457 perm &= ~NFSAUTH_GROUPS;
2460 break;
2464 break;
2467 case OPT_GIDMAP: {
2468 char *c;
2469 char *n;
2472 * The gidmap is supported for AUTH_SYS only.
2474 if (flavor != AUTH_SYS)
2475 break;
2477 if (perm & NFSAUTH_GIDMAP || map_deny)
2478 break;
2480 for (c = val; c != NULL; c = n) {
2481 char *s;
2482 char *al;
2483 gid_t srv;
2485 n = strchr(c, '~');
2486 if (n != NULL)
2487 *n++ = '\0';
2489 s = strchr(c, ':');
2490 if (s != NULL) {
2491 *s++ = '\0';
2492 al = strchr(s, ':');
2493 if (al != NULL)
2494 *al++ = '\0';
2497 if (s == NULL || al == NULL)
2498 break;
2500 if (*c == '\0') {
2501 if (clnt_gid != (gid_t)-1)
2502 continue;
2503 } else if (strcmp(c, "*") != 0) {
2504 gid_t clnt;
2506 if (!get_gid(c, &clnt))
2507 continue;
2509 if (clnt_gid != clnt)
2510 continue;
2513 if (*s == '\0')
2514 srv = UID_NOBODY;
2515 else if (!get_gid(s, &srv))
2516 continue;
2517 else if (srv == (gid_t)-1) {
2518 map_deny = B_TRUE;
2519 break;
2522 if (in_access_list(cln, al) > 0) {
2523 *srv_gid = srv;
2524 perm |= NFSAUTH_GIDMAP;
2526 if (perm & NFSAUTH_GROUPS) {
2527 free(*srv_gids);
2528 *srv_ngids = 0;
2529 *srv_gids = NULL;
2530 perm &= ~NFSAUTH_GROUPS;
2533 break;
2537 break;
2540 default:
2541 break;
2545 free(opts);
2547 if (perm & NFSAUTH_ROOT) {
2548 *srv_uid = 0;
2549 *srv_gid = 0;
2552 if (map_deny)
2553 perm |= NFSAUTH_DENIED;
2555 if (!(perm & NFSAUTH_UIDMAP))
2556 *srv_uid = clnt_uid;
2557 if (!(perm & NFSAUTH_GIDMAP))
2558 *srv_gid = clnt_gid;
2560 if (flavor != match || perm & NFSAUTH_DENIED)
2561 return (NFSAUTH_DENIED);
2563 if (list) {
2565 * If the client doesn't match an "ro" or "rw"
2566 * list then set no access.
2568 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2569 perm |= NFSAUTH_DENIED;
2570 } else {
2572 * The client matched a flavor entry that
2573 * has no explicit "rw" or "ro" determination.
2574 * Default it to "rw".
2576 perm |= NFSAUTH_RW;
2580 * The client may show up in both ro= and rw=
2581 * lists. If so, then turn off the RO access
2582 * bit leaving RW access.
2584 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2586 * Logically cover all permutations of rw=,ro=.
2587 * In the case where, rw,ro=<host> we would like
2588 * to remove RW access for the host. In all other cases
2589 * RW wins the precedence battle.
2591 if (!rw_val && ro_val) {
2592 perm &= ~(NFSAUTH_RW);
2593 } else {
2594 perm &= ~(NFSAUTH_RO);
2598 return (perm);
2602 * Check if the client has access by using a flavor different from
2603 * the given "flavor". If "flavor" is not in the flavor list,
2604 * return TRUE to indicate that this "flavor" is a wrong sec.
2606 static bool_t
2607 is_wrongsec(share_t *sh, struct cln *cln, int flavor)
2609 int flavor_list[MAX_FLAVORS];
2610 int flavor_count, i;
2612 /* get the flavor list that the client has access with */
2613 flavor_count = getclientsflavors_new(sh, cln, flavor_list);
2615 if (flavor_count == 0)
2616 return (FALSE);
2619 * Check if the given "flavor" is in the flavor_list.
2621 for (i = 0; i < flavor_count; i++) {
2622 if (flavor == flavor_list[i])
2623 return (FALSE);
2627 * If "flavor" is not in the flavor_list, return TRUE to indicate
2628 * that the client should have access by using a security flavor
2629 * different from this "flavor".
2631 return (TRUE);
2635 * Given an export and the client's hostname, we
2636 * check the security options to see whether the
2637 * client is allowed to use the given security flavor.
2639 * The strategy is to proceed through the options looking
2640 * for a flavor match, then pay attention to the ro, rw,
2641 * and root options.
2643 * Note that an entry may list several flavors in a
2644 * single entry, e.g.
2646 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2650 static int
2651 check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2652 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2653 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2655 char *opts, *p, *val;
2656 char *lasts;
2657 char *f;
2658 int match = 0; /* Set when a flavor is matched */
2659 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2660 int list = 0; /* Set when "ro", "rw" is found */
2661 int ro_val = 0; /* Set if ro option is 'ro=' */
2662 int rw_val = 0; /* Set if rw option is 'rw=' */
2664 boolean_t map_deny = B_FALSE;
2666 opts = strdup(sh->sh_opts);
2667 if (opts == NULL) {
2668 syslog(LOG_ERR, "check_client: no memory");
2669 return (0);
2673 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2674 * locally for all of them
2676 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2677 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2678 perm |= NFSAUTH_GROUPS;
2680 p = opts;
2682 while (*p) {
2683 switch (getsubopt(&p, optlist, &val)) {
2685 case OPT_SEC:
2686 if (match)
2687 goto done;
2689 while ((f = strtok_r(val, ":", &lasts))
2690 != NULL) {
2691 if (flavor == map_flavor(f)) {
2692 match = 1;
2693 break;
2695 val = NULL;
2697 break;
2699 case OPT_RO:
2700 if (!match)
2701 break;
2703 list++;
2704 if (val != NULL)
2705 ro_val++;
2706 if (in_access_list(cln, val) > 0)
2707 perm |= NFSAUTH_RO;
2708 break;
2710 case OPT_RW:
2711 if (!match)
2712 break;
2714 list++;
2715 if (val != NULL)
2716 rw_val++;
2717 if (in_access_list(cln, val) > 0)
2718 perm |= NFSAUTH_RW;
2719 break;
2721 case OPT_ROOT:
2723 * Check if the client is in
2724 * the root list. Only valid
2725 * for AUTH_SYS.
2727 if (flavor != AUTH_SYS)
2728 break;
2730 if (!match)
2731 break;
2733 if (val == NULL || *val == '\0')
2734 break;
2736 if (clnt_uid != 0)
2737 break;
2739 if (in_access_list(cln, val) > 0) {
2740 perm |= NFSAUTH_ROOT;
2741 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2742 map_deny = B_FALSE;
2744 if (perm & NFSAUTH_GROUPS) {
2745 free(*srv_gids);
2746 *srv_gids = NULL;
2747 *srv_ngids = 0;
2748 perm &= ~NFSAUTH_GROUPS;
2751 break;
2753 case OPT_NONE:
2755 * Check if the client should have no access
2756 * to this share at all. This option behaves
2757 * more like "root" than either "rw" or "ro".
2759 if (in_access_list(cln, val) > 0)
2760 perm |= NFSAUTH_DENIED;
2761 break;
2763 case OPT_UIDMAP: {
2764 char *c;
2765 char *n;
2768 * The uidmap is supported for AUTH_SYS only.
2770 if (flavor != AUTH_SYS)
2771 break;
2773 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2774 break;
2776 for (c = val; c != NULL; c = n) {
2777 char *s;
2778 char *al;
2779 uid_t srv;
2781 n = strchr(c, '~');
2782 if (n != NULL)
2783 *n++ = '\0';
2785 s = strchr(c, ':');
2786 if (s != NULL) {
2787 *s++ = '\0';
2788 al = strchr(s, ':');
2789 if (al != NULL)
2790 *al++ = '\0';
2793 if (s == NULL || al == NULL)
2794 continue;
2796 if (*c == '\0') {
2797 if (clnt_uid != (uid_t)-1)
2798 continue;
2799 } else if (strcmp(c, "*") != 0) {
2800 uid_t clnt;
2802 if (!get_uid(c, &clnt))
2803 continue;
2805 if (clnt_uid != clnt)
2806 continue;
2809 if (*s == '\0')
2810 srv = UID_NOBODY;
2811 else if (!get_uid(s, &srv))
2812 continue;
2813 else if (srv == (uid_t)-1) {
2814 map_deny = B_TRUE;
2815 break;
2818 if (in_access_list(cln, al) > 0) {
2819 *srv_uid = srv;
2820 perm |= NFSAUTH_UIDMAP;
2822 if (perm & NFSAUTH_GROUPS) {
2823 free(*srv_gids);
2824 *srv_gids = NULL;
2825 *srv_ngids = 0;
2826 perm &= ~NFSAUTH_GROUPS;
2829 break;
2833 break;
2836 case OPT_GIDMAP: {
2837 char *c;
2838 char *n;
2841 * The gidmap is supported for AUTH_SYS only.
2843 if (flavor != AUTH_SYS)
2844 break;
2846 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2847 break;
2849 for (c = val; c != NULL; c = n) {
2850 char *s;
2851 char *al;
2852 gid_t srv;
2854 n = strchr(c, '~');
2855 if (n != NULL)
2856 *n++ = '\0';
2858 s = strchr(c, ':');
2859 if (s != NULL) {
2860 *s++ = '\0';
2861 al = strchr(s, ':');
2862 if (al != NULL)
2863 *al++ = '\0';
2866 if (s == NULL || al == NULL)
2867 break;
2869 if (*c == '\0') {
2870 if (clnt_gid != (gid_t)-1)
2871 continue;
2872 } else if (strcmp(c, "*") != 0) {
2873 gid_t clnt;
2875 if (!get_gid(c, &clnt))
2876 continue;
2878 if (clnt_gid != clnt)
2879 continue;
2882 if (*s == '\0')
2883 srv = UID_NOBODY;
2884 else if (!get_gid(s, &srv))
2885 continue;
2886 else if (srv == (gid_t)-1) {
2887 map_deny = B_TRUE;
2888 break;
2891 if (in_access_list(cln, al) > 0) {
2892 *srv_gid = srv;
2893 perm |= NFSAUTH_GIDMAP;
2895 if (perm & NFSAUTH_GROUPS) {
2896 free(*srv_gids);
2897 *srv_gids = NULL;
2898 *srv_ngids = 0;
2899 perm &= ~NFSAUTH_GROUPS;
2902 break;
2906 break;
2909 default:
2910 break;
2914 done:
2915 if (perm & NFSAUTH_ROOT) {
2916 *srv_uid = 0;
2917 *srv_gid = 0;
2920 if (map_deny)
2921 perm |= NFSAUTH_DENIED;
2923 if (!(perm & NFSAUTH_UIDMAP))
2924 *srv_uid = clnt_uid;
2925 if (!(perm & NFSAUTH_GIDMAP))
2926 *srv_gid = clnt_gid;
2929 * If no match then set the perm accordingly
2931 if (!match || perm & NFSAUTH_DENIED) {
2932 free(opts);
2933 return (NFSAUTH_DENIED);
2936 if (list) {
2938 * If the client doesn't match an "ro" or "rw" list then
2939 * check if it may have access by using a different flavor.
2940 * If so, return NFSAUTH_WRONGSEC.
2941 * If not, return NFSAUTH_DENIED.
2943 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2944 if (is_wrongsec(sh, cln, flavor))
2945 perm |= NFSAUTH_WRONGSEC;
2946 else
2947 perm |= NFSAUTH_DENIED;
2949 } else {
2951 * The client matched a flavor entry that
2952 * has no explicit "rw" or "ro" determination.
2953 * Make sure it defaults to "rw".
2955 perm |= NFSAUTH_RW;
2959 * The client may show up in both ro= and rw=
2960 * lists. If so, then turn off the RO access
2961 * bit leaving RW access.
2963 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2965 * Logically cover all permutations of rw=,ro=.
2966 * In the case where, rw,ro=<host> we would like
2967 * to remove RW access for the host. In all other cases
2968 * RW wins the precedence battle.
2970 if (!rw_val && ro_val) {
2971 perm &= ~(NFSAUTH_RW);
2972 } else {
2973 perm &= ~(NFSAUTH_RO);
2977 free(opts);
2979 return (perm);
2982 void
2983 check_sharetab()
2985 FILE *f;
2986 struct stat st;
2987 static timestruc_t last_sharetab_time;
2988 timestruc_t prev_sharetab_time;
2989 share_t *sh;
2990 struct sh_list *shp, *shp_prev;
2991 int res, c = 0;
2994 * read in /etc/dfs/sharetab if it has changed
2996 if (stat(SHARETAB, &st) != 0) {
2997 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2998 return;
3001 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
3002 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
3004 * No change.
3006 return;
3010 * Remember the mod time, then after getting the
3011 * write lock check again. If another thread
3012 * already did the update, then there's no
3013 * work to do.
3015 prev_sharetab_time = last_sharetab_time;
3017 (void) rw_wrlock(&sharetab_lock);
3019 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3020 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3021 (void) rw_unlock(&sharetab_lock);
3022 return;
3026 * Note that since the sharetab is now in memory
3027 * and a snapshot is taken, we no longer have to
3028 * lock the file.
3030 f = fopen(SHARETAB, "r");
3031 if (f == NULL) {
3032 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3033 (void) rw_unlock(&sharetab_lock);
3034 return;
3038 * Once we are sure /etc/dfs/sharetab has been
3039 * modified, flush netgroup cache entries.
3041 netgrp_cache_flush();
3043 sh_free(share_list); /* free old list */
3044 share_list = NULL;
3046 while ((res = getshare(f, &sh)) > 0) {
3047 c++;
3048 if (strcmp(sh->sh_fstype, "nfs") != 0)
3049 continue;
3051 shp = malloc(sizeof (*shp));
3052 if (shp == NULL)
3053 goto alloc_failed;
3054 if (share_list == NULL)
3055 share_list = shp;
3056 else
3057 /* LINTED not used before set */
3058 shp_prev->shl_next = shp;
3059 shp_prev = shp;
3060 shp->shl_next = NULL;
3061 shp->shl_sh = sharedup(sh);
3062 if (shp->shl_sh == NULL)
3063 goto alloc_failed;
3066 if (res < 0)
3067 syslog(LOG_ERR, "%s: invalid at line %d\n",
3068 SHARETAB, c + 1);
3070 if (stat(SHARETAB, &st) != 0) {
3071 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3072 (void) fclose(f);
3073 (void) rw_unlock(&sharetab_lock);
3074 return;
3077 last_sharetab_time = st.st_mtim;
3078 (void) fclose(f);
3079 (void) rw_unlock(&sharetab_lock);
3081 return;
3083 alloc_failed:
3085 syslog(LOG_ERR, "check_sharetab: no memory");
3086 sh_free(share_list);
3087 share_list = NULL;
3088 (void) fclose(f);
3089 (void) rw_unlock(&sharetab_lock);
3092 static void
3093 sh_free(struct sh_list *shp)
3095 struct sh_list *next;
3097 while (shp) {
3098 sharefree(shp->shl_sh);
3099 next = shp->shl_next;
3100 free(shp);
3101 shp = next;
3107 * Remove an entry from mounted list
3109 static void
3110 umount(struct svc_req *rqstp)
3112 char *host, *path, *remove_path;
3113 char rpath[MAXPATHLEN];
3114 SVCXPRT *transp;
3115 struct cln cln;
3117 transp = rqstp->rq_xprt;
3118 path = NULL;
3119 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3120 svcerr_decode(transp);
3121 return;
3124 cln_init(&cln, transp);
3126 errno = 0;
3127 if (!svc_sendreply(transp, xdr_void, NULL))
3128 log_cant_reply_cln(&cln);
3130 host = cln_gethost(&cln);
3131 if (host == NULL) {
3133 * Without the hostname we can't delete this host from the
3134 * mount entries.
3136 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3137 return;
3140 if (verbose)
3141 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3143 remove_path = rpath; /* assume we will use the cannonical path */
3144 if (realpath(path, rpath) == NULL) {
3145 if (verbose)
3146 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3147 remove_path = path; /* use path provided instead */
3150 mntlist_delete(host, remove_path); /* remove from mount list */
3152 cln_fini(&cln);
3154 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3158 * Remove all entries for one machine from mounted list
3160 static void
3161 umountall(struct svc_req *rqstp)
3163 SVCXPRT *transp;
3164 char *host;
3165 struct cln cln;
3167 transp = rqstp->rq_xprt;
3168 if (!svc_getargs(transp, xdr_void, NULL)) {
3169 svcerr_decode(transp);
3170 return;
3173 * We assume that this call is asynchronous and made via rpcbind
3174 * callit routine. Therefore return control immediately. The error
3175 * causes rpcbind to remain silent, as opposed to every machine
3176 * on the net blasting the requester with a response.
3178 svcerr_systemerr(transp);
3180 cln_init(&cln, transp);
3182 host = cln_gethost(&cln);
3183 if (host == NULL) {
3184 /* Can't do anything without the name of the client */
3185 return;
3189 * Remove all hosts entries from mount list
3191 mntlist_delete_all(host);
3193 if (verbose)
3194 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3196 cln_fini(&cln);
3199 void *
3200 exmalloc(size_t size)
3202 void *ret;
3204 if ((ret = malloc(size)) == NULL) {
3205 syslog(LOG_ERR, "Out of memory");
3206 exit(1);
3208 return (ret);