dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fs.d / nfs / mountd / mountd.c
blobc0e66ca0a426cda4e628499e3ece42ddf5ba1056
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 2015 Nexenta Systems, Inc. All rights reserved.
24 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
35 #include <stdio.h>
36 #include <stdio_ext.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <sys/param.h>
43 #include <rpc/rpc.h>
44 #include <sys/stat.h>
45 #include <netconfig.h>
46 #include <netdir.h>
47 #include <sys/file.h>
48 #include <sys/time.h>
49 #include <sys/errno.h>
50 #include <rpcsvc/mount.h>
51 #include <sys/pathconf.h>
52 #include <sys/systeminfo.h>
53 #include <sys/utsname.h>
54 #include <sys/wait.h>
55 #include <sys/resource.h>
56 #include <signal.h>
57 #include <locale.h>
58 #include <unistd.h>
59 #include <errno.h>
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #include <netdb.h>
64 #include <thread.h>
65 #include <assert.h>
66 #include <priv_utils.h>
67 #include <nfs/auth.h>
68 #include <nfs/nfssys.h>
69 #include <nfs/nfs.h>
70 #include <nfs/nfs_sec.h>
71 #include <rpcsvc/daemon_utils.h>
72 #include <deflt.h>
73 #include "../../fslib.h"
74 #include <sharefs/share.h>
75 #include <sharefs/sharetab.h>
76 #include "../lib/sharetab.h"
77 #include "mountd.h"
78 #include <sys/sdt.h>
79 #include <libscf.h>
80 #include <limits.h>
81 #include <sys/nvpair.h>
82 #include <attr.h>
83 #include "smfcfg.h"
84 #include <pwd.h>
85 #include <grp.h>
86 #include <alloca.h>
88 extern int daemonize_init(void);
89 extern void daemonize_fini(int);
91 extern int _nfssys(int, void *);
93 struct sh_list *share_list;
95 rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
96 static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
98 static mutex_t logging_queue_lock;
99 static cond_t logging_queue_cv;
101 static share_t *find_lofsentry(char *, int *);
102 static int getclientsflavors_old(share_t *, struct cln *, int *);
103 static int getclientsflavors_new(share_t *, struct cln *, int *);
104 static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
105 gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
106 static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
107 gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
108 static void mnt(struct svc_req *, SVCXPRT *);
109 static void mnt_pathconf(struct svc_req *);
110 static int mount(struct svc_req *r);
111 static void sh_free(struct sh_list *);
112 static void umount(struct svc_req *);
113 static void umountall(struct svc_req *);
114 static int newopts(char *);
116 static int verbose;
117 static int rejecting;
118 static int mount_vers_min = MOUNTVERS;
119 static int mount_vers_max = MOUNTVERS3;
121 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
123 thread_t nfsauth_thread;
124 thread_t cmd_thread;
125 thread_t logging_thread;
127 typedef struct logging_data {
128 char *ld_host;
129 char *ld_path;
130 char *ld_rpath;
131 int ld_status;
132 char *ld_netid;
133 struct netbuf *ld_nb;
134 struct logging_data *ld_next;
135 } logging_data;
137 static logging_data *logging_head = NULL;
138 static logging_data *logging_tail = NULL;
141 * Our copy of some system variables obtained using sysconf(3c)
143 static long ngroups_max; /* _SC_NGROUPS_MAX */
144 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
146 /* ARGSUSED */
147 static void *
148 nfsauth_svc(void *arg)
150 int doorfd = -1;
151 uint_t darg;
152 #ifdef DEBUG
153 int dfd;
154 #endif
156 if ((doorfd = door_create(nfsauth_func, NULL,
157 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
158 syslog(LOG_ERR, "Unable to create door: %m\n");
159 exit(10);
162 #ifdef DEBUG
164 * Create a file system path for the door
166 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
167 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
168 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
169 (void) close(doorfd);
170 exit(11);
174 * Clean up any stale namespace associations
176 (void) fdetach(MOUNTD_DOOR);
179 * Register in namespace to pass to the kernel to door_ki_open
181 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
182 syslog(LOG_ERR, "Unable to fattach door: %m\n");
183 (void) close(dfd);
184 (void) close(doorfd);
185 exit(12);
187 (void) close(dfd);
188 #endif
191 * Must pass the doorfd down to the kernel.
193 darg = doorfd;
194 (void) _nfssys(MOUNTD_ARGS, &darg);
197 * Wait for incoming calls
199 /*CONSTCOND*/
200 for (;;)
201 (void) pause();
203 /*NOTREACHED*/
204 syslog(LOG_ERR, gettext("Door server exited"));
205 return (NULL);
209 * NFS command service thread code for setup and handling of the
210 * nfs_cmd requests for character set conversion and other future
211 * events.
214 static void *
215 cmd_svc(void *arg)
217 int doorfd = -1;
218 uint_t darg;
220 if ((doorfd = door_create(nfscmd_func, NULL,
221 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
222 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
223 exit(10);
227 * Must pass the doorfd down to the kernel.
229 darg = doorfd;
230 (void) _nfssys(NFSCMD_ARGS, &darg);
233 * Wait for incoming calls
235 /*CONSTCOND*/
236 for (;;)
237 (void) pause();
239 /*NOTREACHED*/
240 syslog(LOG_ERR, gettext("Cmd door server exited"));
241 return (NULL);
244 static void
245 free_logging_data(logging_data *lq)
247 if (lq != NULL) {
248 free(lq->ld_host);
249 free(lq->ld_netid);
251 if (lq->ld_nb != NULL) {
252 free(lq->ld_nb->buf);
253 free(lq->ld_nb);
256 free(lq->ld_path);
257 free(lq->ld_rpath);
259 free(lq);
263 static logging_data *
264 remove_head_of_queue(void)
266 logging_data *lq;
269 * Pull it off the queue.
271 lq = logging_head;
272 if (lq) {
273 logging_head = lq->ld_next;
276 * Drained it.
278 if (logging_head == NULL) {
279 logging_tail = NULL;
283 return (lq);
286 static void
287 do_logging_queue(logging_data *lq)
289 int cleared = 0;
290 char *host;
292 while (lq) {
293 struct cln cln;
295 if (lq->ld_host == NULL) {
296 DTRACE_PROBE(mountd, name_by_lazy);
297 cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
298 host = cln_gethost(&cln);
299 } else
300 host = lq->ld_host;
302 audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
304 /* add entry to mount list */
305 if (lq->ld_rpath)
306 mntlist_new(host, lq->ld_rpath);
308 if (lq->ld_host == NULL)
309 cln_fini(&cln);
311 free_logging_data(lq);
312 cleared++;
314 (void) mutex_lock(&logging_queue_lock);
315 lq = remove_head_of_queue();
316 (void) mutex_unlock(&logging_queue_lock);
319 DTRACE_PROBE1(mountd, logging_cleared, cleared);
322 static void *
323 logging_svc(void *arg)
325 logging_data *lq;
327 for (;;) {
328 (void) mutex_lock(&logging_queue_lock);
329 while (logging_head == NULL) {
330 (void) cond_wait(&logging_queue_cv,
331 &logging_queue_lock);
334 lq = remove_head_of_queue();
335 (void) mutex_unlock(&logging_queue_lock);
337 do_logging_queue(lq);
340 /*NOTREACHED*/
341 syslog(LOG_ERR, gettext("Logging server exited"));
342 return (NULL);
345 static int
346 convert_int(int *val, char *str)
348 long lval;
350 if (str == NULL || !isdigit(*str))
351 return (-1);
353 lval = strtol(str, &str, 10);
354 if (*str != '\0' || lval > INT_MAX)
355 return (-2);
357 *val = (int)lval;
358 return (0);
362 main(int argc, char *argv[])
364 int pid;
365 int c;
366 int rpc_svc_fdunlim = 1;
367 int rpc_svc_mode = RPC_SVC_MT_AUTO;
368 int maxrecsz = RPC_MAXDATASIZE;
369 bool_t exclbind = TRUE;
370 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
371 char defval[4];
372 int defvers, ret, bufsz;
373 struct rlimit rl;
374 int listen_backlog = 0;
375 int max_threads = 0;
376 int tmp;
378 int pipe_fd = -1;
381 * Mountd requires uid 0 for:
382 * /etc/rmtab updates (we could chown it to daemon)
383 * /etc/dfs/dfstab reading (it wants to lock out share which
384 * doesn't do any locking before first truncate;
385 * NFS share does; should use fcntl locking instead)
386 * Needed privileges:
387 * auditing
388 * nfs syscall
389 * file dac search (so it can stat all files)
391 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
392 PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH, NULL) == -1) {
393 (void) fprintf(stderr,
394 "%s: must be run with sufficient privileges\n",
395 argv[0]);
396 exit(1);
399 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
400 syslog(LOG_ERR, "getrlimit failed");
401 } else {
402 rl.rlim_cur = rl.rlim_max;
403 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
404 syslog(LOG_ERR, "setrlimit failed");
407 (void) enable_extended_FILE_stdio(-1, -1);
409 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
410 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
411 if (ret != SA_OK) {
412 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
413 "failed, using default value");
416 while ((c = getopt(argc, argv, "vrm:")) != EOF) {
417 switch (c) {
418 case 'v':
419 verbose++;
420 break;
421 case 'r':
422 rejecting = 1;
423 break;
424 case 'm':
425 if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
426 (void) fprintf(stderr, "%s: invalid "
427 "max_threads option, using defaults\n",
428 argv[0]);
429 break;
431 max_threads = tmp;
432 break;
433 default:
434 fprintf(stderr, "usage: mountd [-v] [-r]\n");
435 exit(1);
440 * Read in the NFS version values from config file.
442 bufsz = 4;
443 ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
444 SCF_TYPE_INTEGER, NFSD, &bufsz);
445 if (ret == SA_OK) {
446 errno = 0;
447 defvers = strtol(defval, (char **)NULL, 10);
448 if (errno == 0) {
449 mount_vers_min = defvers;
451 * special because NFSv2 is
452 * supported by mount v1 & v2
454 if (defvers == NFS_VERSION)
455 mount_vers_min = MOUNTVERS;
459 bufsz = 4;
460 ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
461 SCF_TYPE_INTEGER, NFSD, &bufsz);
462 if (ret == SA_OK) {
463 errno = 0;
464 defvers = strtol(defval, (char **)NULL, 10);
465 if (errno == 0) {
466 mount_vers_max = defvers;
470 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
471 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
472 if (ret != SA_OK) {
473 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
474 "failed, using default value");
478 * Sanity check versions,
479 * even though we may get versions > MOUNTVERS3, we still need
480 * to start nfsauth service, so continue on regardless of values.
482 if (mount_vers_min > mount_vers_max) {
483 fprintf(stderr, "server_versmin > server_versmax\n");
484 mount_vers_max = mount_vers_min;
486 (void) setlocale(LC_ALL, "");
487 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
488 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
489 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
490 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
492 netgroup_init();
494 #if !defined(TEXT_DOMAIN)
495 #define TEXT_DOMAIN "SYS_TEST"
496 #endif
497 (void) textdomain(TEXT_DOMAIN);
499 /* Don't drop core if the NFS module isn't loaded. */
500 (void) signal(SIGSYS, SIG_IGN);
502 pipe_fd = daemonize_init();
505 * If we coredump it'll be in /core
507 if (chdir("/") < 0)
508 fprintf(stderr, "chdir /: %s\n", strerror(errno));
510 openlog("mountd", LOG_PID, LOG_DAEMON);
513 * establish our lock on the lock file and write our pid to it.
514 * exit if some other process holds the lock, or if there's any
515 * error in writing/locking the file.
517 pid = _enter_daemon_lock(MOUNTD);
518 switch (pid) {
519 case 0:
520 break;
521 case -1:
522 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
523 strerror(errno));
524 exit(2);
525 default:
526 /* daemon was already running */
527 exit(0);
530 audit_mountd_setup(); /* BSM */
533 * Get required system variables
535 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
536 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
537 exit(1);
539 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
540 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
541 exit(1);
545 * Set number of file descriptors to unlimited
547 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
548 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
552 * Tell RPC that we want automatic thread mode.
553 * A new thread will be spawned for each request.
555 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
556 fprintf(stderr, "unable to set automatic MT mode\n");
557 exit(1);
561 * Enable non-blocking mode and maximum record size checks for
562 * connection oriented transports.
564 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
565 fprintf(stderr, "unable to set RPC max record size\n");
569 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
570 * from being hijacked by a bind to a more specific addr.
572 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
573 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
577 * Set the maximum number of outstanding connection
578 * indications (listen backlog) to the value specified.
580 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
581 &listen_backlog)) {
582 fprintf(stderr, "unable to set listen backlog\n");
583 exit(1);
587 * If max_threads was specified, then set the
588 * maximum number of threads to the value specified.
590 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
591 fprintf(stderr, "unable to set max_threads\n");
592 exit(1);
596 * Make sure to unregister any previous versions in case the
597 * user is reconfiguring the server in interesting ways.
599 svc_unreg(MOUNTPROG, MOUNTVERS);
600 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
601 svc_unreg(MOUNTPROG, MOUNTVERS3);
604 * Create the nfsauth thread with same signal disposition
605 * as the main thread. We need to create a separate thread
606 * since mountd() will be both an RPC server (for remote
607 * traffic) _and_ a doors server (for kernel upcalls).
609 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
610 fprintf(stderr,
611 gettext("Failed to create NFSAUTH svc thread\n"));
612 exit(2);
616 * Create the cmd service thread with same signal disposition
617 * as the main thread. We need to create a separate thread
618 * since mountd() will be both an RPC server (for remote
619 * traffic) _and_ a doors server (for kernel upcalls).
621 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
622 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
623 exit(2);
627 * Create an additional thread to service the rmtab and
628 * audit_mountd_mount logging for mount requests. Use the same
629 * signal disposition as the main thread. We create
630 * a separate thread to allow the mount request threads to
631 * clear as soon as possible.
633 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
634 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
635 exit(2);
639 * Create datagram and connection oriented services
641 if (mount_vers_max >= MOUNTVERS) {
642 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
643 fprintf(stderr,
644 "couldn't register datagram_v MOUNTVERS\n");
645 exit(1);
647 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
648 fprintf(stderr,
649 "couldn't register circuit_v MOUNTVERS\n");
650 exit(1);
654 if (mount_vers_max >= MOUNTVERS_POSIX) {
655 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
656 "datagram_v") == 0) {
657 fprintf(stderr,
658 "couldn't register datagram_v MOUNTVERS_POSIX\n");
659 exit(1);
661 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
662 "circuit_v") == 0) {
663 fprintf(stderr,
664 "couldn't register circuit_v MOUNTVERS_POSIX\n");
665 exit(1);
669 if (mount_vers_max >= MOUNTVERS3) {
670 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
671 fprintf(stderr,
672 "couldn't register datagram_v MOUNTVERS3\n");
673 exit(1);
675 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
676 fprintf(stderr,
677 "couldn't register circuit_v MOUNTVERS3\n");
678 exit(1);
683 * Start serving
685 rmtab_load();
687 daemonize_fini(pipe_fd);
689 /* Get rid of the most dangerous basic privileges. */
690 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
691 NULL);
693 svc_run();
694 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
695 abort();
697 /* NOTREACHED */
698 return (0);
702 * Server procedure switch routine
704 void
705 mnt(struct svc_req *rqstp, SVCXPRT *transp)
707 switch (rqstp->rq_proc) {
708 case NULLPROC:
709 errno = 0;
710 if (!svc_sendreply(transp, xdr_void, (char *)0))
711 log_cant_reply(transp);
712 return;
714 case MOUNTPROC_MNT:
715 (void) mount(rqstp);
716 return;
718 case MOUNTPROC_DUMP:
719 mntlist_send(transp);
720 return;
722 case MOUNTPROC_UMNT:
723 umount(rqstp);
724 return;
726 case MOUNTPROC_UMNTALL:
727 umountall(rqstp);
728 return;
730 case MOUNTPROC_EXPORT:
731 case MOUNTPROC_EXPORTALL:
732 export(rqstp);
733 return;
735 case MOUNTPROC_PATHCONF:
736 if (rqstp->rq_vers == MOUNTVERS_POSIX)
737 mnt_pathconf(rqstp);
738 else
739 svcerr_noproc(transp);
740 return;
742 default:
743 svcerr_noproc(transp);
744 return;
748 void
749 log_cant_reply_cln(struct cln *cln)
751 int saverrno;
752 char *host;
754 saverrno = errno; /* save error code */
756 host = cln_gethost(cln);
757 if (host == NULL)
758 return;
760 errno = saverrno;
761 if (errno == 0)
762 syslog(LOG_ERR, "couldn't send reply to %s", host);
763 else
764 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
767 void
768 log_cant_reply(SVCXPRT *transp)
770 int saverrno;
771 struct cln cln;
773 saverrno = errno; /* save error code */
774 cln_init(&cln, transp);
775 errno = saverrno;
777 log_cant_reply_cln(&cln);
779 cln_fini(&cln);
783 * Answer pathconf questions for the mount point fs
785 static void
786 mnt_pathconf(struct svc_req *rqstp)
788 SVCXPRT *transp;
789 struct pathcnf p;
790 char *path, rpath[MAXPATHLEN];
791 struct stat st;
793 transp = rqstp->rq_xprt;
794 path = NULL;
795 (void) memset((caddr_t)&p, 0, sizeof (p));
797 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
798 svcerr_decode(transp);
799 return;
801 if (lstat(path, &st) < 0) {
802 _PC_SET(_PC_ERROR, p.pc_mask);
803 goto done;
806 * Get a path without symbolic links.
808 if (realpath(path, rpath) == NULL) {
809 syslog(LOG_DEBUG,
810 "mount request: realpath failed on %s: %m",
811 path);
812 _PC_SET(_PC_ERROR, p.pc_mask);
813 goto done;
815 (void) memset((caddr_t)&p, 0, sizeof (p));
817 * can't ask about devices over NFS
819 _PC_SET(_PC_MAX_CANON, p.pc_mask);
820 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
821 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
822 _PC_SET(_PC_VDISABLE, p.pc_mask);
824 errno = 0;
825 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
826 if (errno)
827 _PC_SET(_PC_LINK_MAX, p.pc_mask);
828 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
829 if (errno)
830 _PC_SET(_PC_NAME_MAX, p.pc_mask);
831 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
832 if (errno)
833 _PC_SET(_PC_PATH_MAX, p.pc_mask);
834 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
835 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
836 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
837 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
839 done:
840 errno = 0;
841 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
842 log_cant_reply(transp);
843 if (path != NULL)
844 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
848 * If the rootmount (export) option is specified, the all mount requests for
849 * subdirectories return EACCES.
851 static int
852 checkrootmount(share_t *sh, char *rpath)
854 char *val;
856 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
857 free(val);
858 if (strcmp(sh->sh_path, rpath) != 0)
859 return (0);
860 else
861 return (1);
862 } else
863 return (1);
866 #define MAX_FLAVORS 128
869 * Return only EACCES if client does not have access
870 * to this directory.
871 * "If the server exports only /a/b, an attempt to
872 * mount a/b/c will fail with ENOENT if the directory
873 * does not exist"... However, if the client
874 * does not have access to /a/b, an attacker can
875 * determine whether the directory exists.
876 * This routine checks either existence of the file or
877 * existence of the file name entry in the mount table.
878 * If the file exists and there is no file name entry,
879 * the error returned should be EACCES.
880 * If the file does not exist, it must be determined
881 * whether the client has access to a parent
882 * directory. If the client has access to a parent
883 * directory, the error returned should be ENOENT,
884 * otherwise EACCES.
886 static int
887 mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
889 char *checkpath, *dp;
890 share_t *sh = NULL;
891 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
892 int flavor_count;
894 checkpath = strdup(path);
895 if (checkpath == NULL) {
896 syslog(LOG_ERR, "mount_enoent: no memory");
897 return (EACCES);
900 /* CONSTCOND */
901 while (1) {
902 if (sh) {
903 sharefree(sh);
904 sh = NULL;
907 if ((sh = findentry(rpath)) == NULL &&
908 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
910 * There is no file name entry.
911 * If the file (with symbolic links resolved) exists,
912 * the error returned should be EACCES.
914 if (realpath_error == 0)
915 break;
916 } else if (checkrootmount(sh, rpath) == 0) {
918 * This is a "nosub" only export, in which case,
919 * mounting subdirectories isn't allowed.
920 * If the file (with symbolic links resolved) exists,
921 * the error returned should be EACCES.
923 if (realpath_error == 0)
924 break;
925 } else {
927 * Check permissions in mount table.
929 if (newopts(sh->sh_opts))
930 flavor_count = getclientsflavors_new(sh, cln,
931 flavor_list);
932 else
933 flavor_count = getclientsflavors_old(sh, cln,
934 flavor_list);
935 if (flavor_count != 0) {
937 * Found entry in table and
938 * client has correct permissions.
940 reply_error = ENOENT;
941 break;
946 * Check all parent directories.
948 dp = strrchr(checkpath, '/');
949 if (dp == NULL)
950 break;
951 *dp = '\0';
952 if (strlen(checkpath) == 0)
953 break;
955 * Get the real path (no symbolic links in it)
957 if (realpath(checkpath, rpath) == NULL) {
958 if (errno != ENOENT)
959 break;
960 } else {
961 realpath_error = 0;
965 if (sh)
966 sharefree(sh);
967 free(checkpath);
968 return (reply_error);
972 * We need to inform the caller whether or not we were
973 * able to add a node to the queue. If we are not, then
974 * it is up to the caller to go ahead and log the data.
976 static int
977 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
978 char *rpath, int status, int error)
980 logging_data *lq;
981 struct netbuf *nb;
983 lq = (logging_data *)calloc(1, sizeof (logging_data));
984 if (lq == NULL)
985 goto cleanup;
988 * We might not yet have the host...
990 if (host) {
991 DTRACE_PROBE1(mountd, log_host, host);
992 lq->ld_host = strdup(host);
993 if (lq->ld_host == NULL)
994 goto cleanup;
995 } else {
996 DTRACE_PROBE(mountd, log_no_host);
998 lq->ld_netid = strdup(transp->xp_netid);
999 if (lq->ld_netid == NULL)
1000 goto cleanup;
1002 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1003 if (lq->ld_nb == NULL)
1004 goto cleanup;
1006 nb = svc_getrpccaller(transp);
1007 if (nb == NULL) {
1008 DTRACE_PROBE(mountd, e__nb__enqueue);
1009 goto cleanup;
1012 DTRACE_PROBE(mountd, nb_set_enqueue);
1014 lq->ld_nb->maxlen = nb->maxlen;
1015 lq->ld_nb->len = nb->len;
1017 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1018 if (lq->ld_nb->buf == NULL)
1019 goto cleanup;
1021 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1024 lq->ld_path = strdup(path);
1025 if (lq->ld_path == NULL)
1026 goto cleanup;
1028 if (!error) {
1029 lq->ld_rpath = strdup(rpath);
1030 if (lq->ld_rpath == NULL)
1031 goto cleanup;
1034 lq->ld_status = status;
1037 * Add to the tail of the logging queue.
1039 (void) mutex_lock(&logging_queue_lock);
1040 if (logging_tail == NULL) {
1041 logging_tail = logging_head = lq;
1042 } else {
1043 logging_tail->ld_next = lq;
1044 logging_tail = lq;
1046 (void) cond_signal(&logging_queue_cv);
1047 (void) mutex_unlock(&logging_queue_lock);
1049 return (TRUE);
1051 cleanup:
1053 free_logging_data(lq);
1055 return (FALSE);
1059 #define CLN_CLNAMES (1 << 0)
1060 #define CLN_HOST (1 << 1)
1062 static void
1063 cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1064 struct netbuf *nbuf)
1066 if ((cln->transp = transp) != NULL) {
1067 assert(netid == NULL && nbuf == NULL);
1068 cln->netid = transp->xp_netid;
1069 cln->nbuf = svc_getrpccaller(transp);
1070 } else {
1071 cln->netid = netid;
1072 cln->nbuf = nbuf;
1075 cln->nconf = NULL;
1076 cln->clnames = NULL;
1077 cln->host = NULL;
1079 cln->flags = 0;
1082 void
1083 cln_init(struct cln *cln, SVCXPRT *transp)
1085 cln_init_common(cln, transp, NULL, NULL);
1088 void
1089 cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1091 cln_init_common(cln, NULL, netid, nbuf);
1094 void
1095 cln_fini(struct cln *cln)
1097 if (cln->nconf != NULL)
1098 freenetconfigent(cln->nconf);
1100 if (cln->clnames != NULL)
1101 netdir_free(cln->clnames, ND_HOSTSERVLIST);
1103 free(cln->host);
1106 struct netbuf *
1107 cln_getnbuf(struct cln *cln)
1109 return (cln->nbuf);
1112 struct nd_hostservlist *
1113 cln_getclientsnames(struct cln *cln)
1115 if ((cln->flags & CLN_CLNAMES) == 0) {
1117 * nconf is not needed if we do not have nbuf (see
1118 * cln_gethost() too), so we check for nbuf and in a case it is
1119 * NULL we do not try to get nconf.
1121 if (cln->netid != NULL && cln->nbuf != NULL) {
1122 cln->nconf = getnetconfigent(cln->netid);
1123 if (cln->nconf == NULL)
1124 syslog(LOG_ERR, "%s: getnetconfigent failed",
1125 cln->netid);
1128 if (cln->nconf != NULL && cln->nbuf != NULL)
1129 (void) __netdir_getbyaddr_nosrv(cln->nconf,
1130 &cln->clnames, cln->nbuf);
1132 cln->flags |= CLN_CLNAMES;
1135 return (cln->clnames);
1139 * Return B_TRUE if the host is already available at no cost
1141 boolean_t
1142 cln_havehost(struct cln *cln)
1144 return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1147 char *
1148 cln_gethost(struct cln *cln)
1150 if (cln_getclientsnames(cln) != NULL)
1151 return (cln->clnames->h_hostservs[0].h_host);
1153 if ((cln->flags & CLN_HOST) == 0) {
1154 if (cln->nconf == NULL || cln->nbuf == NULL) {
1155 cln->host = strdup("(anon)");
1156 } else {
1157 char host[MAXIPADDRLEN];
1159 if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1160 struct sockaddr_in *sa;
1162 /* LINTED pointer alignment */
1163 sa = (struct sockaddr_in *)(cln->nbuf->buf);
1164 (void) inet_ntoa_r(sa->sin_addr, host);
1166 cln->host = strdup(host);
1167 } else if (strcmp(cln->nconf->nc_protofmly,
1168 NC_INET6) == 0) {
1169 struct sockaddr_in6 *sa;
1171 /* LINTED pointer alignment */
1172 sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1173 (void) inet_ntop(AF_INET6,
1174 sa->sin6_addr.s6_addr,
1175 host, INET6_ADDRSTRLEN);
1177 cln->host = strdup(host);
1178 } else {
1179 syslog(LOG_ERR, gettext("Client's address is "
1180 "neither IPv4 nor IPv6"));
1182 cln->host = strdup("(anon)");
1186 cln->flags |= CLN_HOST;
1189 return (cln->host);
1193 * Check mount requests, add to mounted list if ok
1195 static int
1196 mount(struct svc_req *rqstp)
1198 SVCXPRT *transp;
1199 int version, vers;
1200 struct fhstatus fhs;
1201 struct mountres3 mountres3;
1202 char fh[FHSIZE3];
1203 int len = FHSIZE3;
1204 char *path, rpath[MAXPATHLEN];
1205 share_t *sh = NULL;
1206 struct cln cln;
1207 char *host = NULL;
1208 int error = 0, lofs_tried = 0, enqueued;
1209 int flavor_list[MAX_FLAVORS];
1210 int flavor_count;
1211 ucred_t *uc = NULL;
1213 int audit_status;
1215 transp = rqstp->rq_xprt;
1216 version = rqstp->rq_vers;
1217 path = NULL;
1219 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1220 svcerr_decode(transp);
1221 return (EACCES);
1224 cln_init(&cln, transp);
1227 * Put off getting the name for the client until we
1228 * need it. This is a performance gain. If we are logging,
1229 * then we don't care about performance and might as well
1230 * get the host name now in case we need to spit out an
1231 * error message.
1233 if (verbose) {
1234 DTRACE_PROBE(mountd, name_by_verbose);
1235 if ((host = cln_gethost(&cln)) == NULL) {
1237 * We failed to get a name for the client, even
1238 * 'anon', probably because we ran out of memory.
1239 * In this situation it doesn't make sense to
1240 * allow the mount to succeed.
1242 error = EACCES;
1243 goto reply;
1248 * If the version being used is less than the minimum version,
1249 * the filehandle translation should not be provided to the
1250 * client.
1252 if (rejecting || version < mount_vers_min) {
1253 if (verbose)
1254 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1255 host, path);
1256 error = EACCES;
1257 goto reply;
1261 * Get the real path (no symbolic links in it)
1263 if (realpath(path, rpath) == NULL) {
1264 error = errno;
1265 if (verbose)
1266 syslog(LOG_ERR,
1267 "mount request: realpath: %s: %m", path);
1268 if (error == ENOENT)
1269 error = mount_enoent_error(&cln, path, rpath,
1270 flavor_list);
1271 goto reply;
1274 if ((sh = findentry(rpath)) == NULL &&
1275 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1276 error = EACCES;
1277 goto reply;
1281 * Check if this is a "nosub" only export, in which case, mounting
1282 * subdirectories isn't allowed. Bug 1184573.
1284 if (checkrootmount(sh, rpath) == 0) {
1285 error = EACCES;
1286 goto reply;
1289 if (newopts(sh->sh_opts))
1290 flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
1291 else
1292 flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
1294 if (flavor_count == 0) {
1295 error = EACCES;
1296 goto reply;
1300 * Now get the filehandle.
1302 * NFS V2 clients get a 32 byte filehandle.
1303 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1304 * the embedded FIDs.
1306 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1308 /* LINTED pointer alignment */
1309 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1310 if (errno == EINVAL &&
1311 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1312 errno = 0;
1313 continue;
1315 error = errno == EINVAL ? EACCES : errno;
1316 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1317 path);
1318 break;
1321 if (version == MOUNTVERS3) {
1322 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1323 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1324 } else {
1325 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1328 reply:
1329 if (uc != NULL)
1330 ucred_free(uc);
1332 switch (version) {
1333 case MOUNTVERS:
1334 case MOUNTVERS_POSIX:
1335 if (error == EINVAL)
1336 fhs.fhs_status = NFSERR_ACCES;
1337 else if (error == EREMOTE)
1338 fhs.fhs_status = NFSERR_REMOTE;
1339 else
1340 fhs.fhs_status = error;
1342 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1343 log_cant_reply_cln(&cln);
1345 audit_status = fhs.fhs_status;
1346 break;
1348 case MOUNTVERS3:
1349 if (!error) {
1350 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1351 flavor_list;
1352 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1353 flavor_count;
1355 } else if (error == ENAMETOOLONG)
1356 error = MNT3ERR_NAMETOOLONG;
1358 mountres3.fhs_status = error;
1359 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1360 log_cant_reply_cln(&cln);
1362 audit_status = mountres3.fhs_status;
1363 break;
1366 if (cln_havehost(&cln))
1367 host = cln_gethost(&cln);
1369 if (verbose)
1370 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1371 (host == NULL) ? "unknown host" : host,
1372 error ? "denied" : "mounted", path);
1375 * If we can not create a queue entry, go ahead and do it
1376 * in the context of this thread.
1378 enqueued = enqueue_logging_data(host, transp, path, rpath,
1379 audit_status, error);
1380 if (enqueued == FALSE) {
1381 if (host == NULL) {
1382 DTRACE_PROBE(mountd, name_by_in_thread);
1383 host = cln_gethost(&cln);
1386 DTRACE_PROBE(mountd, logged_in_thread);
1387 audit_mountd_mount(host, path, audit_status); /* BSM */
1388 if (!error)
1389 mntlist_new(host, rpath); /* add entry to mount list */
1392 if (path != NULL)
1393 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1395 if (sh)
1396 sharefree(sh);
1398 cln_fini(&cln);
1400 return (error);
1404 * Determine whether two paths are within the same file system.
1405 * Returns nonzero (true) if paths are the same, zero (false) if
1406 * they are different. If an error occurs, return false.
1408 * Use the actual FSID if it's available (via getattrat()); otherwise,
1409 * fall back on st_dev.
1411 * With ZFS snapshots, st_dev differs from the regular file system
1412 * versus the snapshot. But the fsid is the same throughout. Thus
1413 * the fsid is a better test.
1415 static int
1416 same_file_system(const char *path1, const char *path2)
1418 uint64_t fsid1, fsid2;
1419 struct stat64 st1, st2;
1420 nvlist_t *nvl1 = NULL;
1421 nvlist_t *nvl2 = NULL;
1423 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1424 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1425 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1426 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1427 nvlist_free(nvl1);
1428 nvlist_free(nvl2);
1430 * We have found fsid's for both paths.
1433 if (fsid1 == fsid2)
1434 return (B_TRUE);
1436 return (B_FALSE);
1439 nvlist_free(nvl1);
1440 nvlist_free(nvl2);
1443 * We were unable to find fsid's for at least one of the paths.
1444 * fall back on st_dev.
1447 if (stat64(path1, &st1) < 0) {
1448 syslog(LOG_NOTICE, "%s: %m", path1);
1449 return (B_FALSE);
1451 if (stat64(path2, &st2) < 0) {
1452 syslog(LOG_NOTICE, "%s: %m", path2);
1453 return (B_FALSE);
1456 if (st1.st_dev == st2.st_dev)
1457 return (B_TRUE);
1459 return (B_FALSE);
1462 share_t *
1463 findentry(char *path)
1465 share_t *sh = NULL;
1466 struct sh_list *shp;
1467 char *p1, *p2;
1469 check_sharetab();
1471 (void) rw_rdlock(&sharetab_lock);
1473 for (shp = share_list; shp; shp = shp->shl_next) {
1474 sh = shp->shl_sh;
1475 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1476 if (*p1 == '\0')
1477 goto done; /* exact match */
1480 * Now compare the pathnames for three cases:
1482 * Parent: /export/foo (no trailing slash on parent)
1483 * Child: /export/foo/bar
1485 * Parent: /export/foo/ (trailing slash on parent)
1486 * Child: /export/foo/bar
1488 * Parent: /export/foo/ (no trailing slash on child)
1489 * Child: /export/foo
1491 if ((*p1 == '\0' && *p2 == '/') ||
1492 (*p1 == '\0' && *(p1-1) == '/') ||
1493 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1495 * We have a subdirectory. Test whether the
1496 * subdirectory is in the same file system.
1498 if (same_file_system(path, sh->sh_path))
1499 goto done;
1502 done:
1503 sh = shp ? sharedup(sh) : NULL;
1505 (void) rw_unlock(&sharetab_lock);
1507 return (sh);
1511 static int
1512 is_substring(char **mntp, char **path)
1514 char *p1 = *mntp, *p2 = *path;
1516 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1517 return (1);
1518 else if (*p1 == '\0' && *p2 == '/')
1519 return (1);
1520 else if (*p1 == '\0' && *(p1-1) == '/') {
1521 *path = --p2; /* we need the slash in p2 */
1522 return (1);
1523 } else if (*p2 == '\0') {
1524 while (*p1 == '/')
1525 p1++;
1526 if (*p1 == '\0') /* exact match */
1527 return (1);
1529 return (0);
1533 * find_lofsentry() searches for the real path which this requested LOFS path
1534 * (rpath) shadows. If found, it will return the sharetab entry of
1535 * the real path that corresponds to the LOFS path.
1536 * We first search mnttab to see if the requested path is an automounted
1537 * path. If it is an automounted path, it will trigger the mount by stat()ing
1538 * the requested path. Note that it is important to check that this path is
1539 * actually an automounted path, otherwise we would stat() a path which may
1540 * turn out to be NFS and block indefinitely on a dead server. The automounter
1541 * times-out if the server is dead, so there's no risk of hanging this
1542 * thread waiting for stat().
1543 * After the mount has been triggered (if necessary), we look for a
1544 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1545 * is a substring of the rpath. If found, we construct a new path by
1546 * concatenating the mnt_special and the remaining of rpath, call findentry()
1547 * to make sure the 'real path' is shared.
1549 static share_t *
1550 find_lofsentry(char *rpath, int *done_flag)
1552 struct stat r_stbuf;
1553 mntlist_t *ml, *mntl, *mntpnt = NULL;
1554 share_t *retcode = NULL;
1555 char tmp_path[MAXPATHLEN];
1556 int mntpnt_len = 0, tmp;
1557 char *p1, *p2;
1559 if ((*done_flag)++)
1560 return (retcode);
1563 * While fsgetmntlist() uses lockf() to
1564 * lock the mnttab before reading it in,
1565 * the lock ignores threads in the same process.
1566 * Read in the mnttab with the protection of a mutex.
1568 (void) mutex_lock(&mnttab_lock);
1569 mntl = fsgetmntlist();
1570 (void) mutex_unlock(&mnttab_lock);
1573 * Obtain the mountpoint for the requested path.
1575 for (ml = mntl; ml; ml = ml->mntl_next) {
1576 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1577 *p1 == *p2 && *p1; p1++, p2++)
1579 if (is_substring(&p1, &p2) &&
1580 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1581 mntpnt = ml;
1582 mntpnt_len = tmp;
1587 * If the path needs to be autoFS mounted, trigger the mount by
1588 * stat()ing it. This is determined by checking whether the
1589 * mountpoint we just found is of type autofs.
1591 if (mntpnt != NULL &&
1592 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1594 * The requested path is a substring of an autoFS filesystem.
1595 * Trigger the mount.
1597 if (stat(rpath, &r_stbuf) < 0) {
1598 if (verbose)
1599 syslog(LOG_NOTICE, "%s: %m", rpath);
1600 goto done;
1602 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1604 * The requested path is a directory, stat(2) it
1605 * again with a trailing '.' to force the autoFS
1606 * module to trigger the mount of indirect
1607 * automount entries, such as /net/jurassic/.
1609 if (strlen(rpath) + 2 > MAXPATHLEN) {
1610 if (verbose) {
1611 syslog(LOG_NOTICE,
1612 "%s/.: exceeds MAXPATHLEN %d",
1613 rpath, MAXPATHLEN);
1615 goto done;
1617 (void) strcpy(tmp_path, rpath);
1618 (void) strcat(tmp_path, "/.");
1620 if (stat(tmp_path, &r_stbuf) < 0) {
1621 if (verbose)
1622 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1623 goto done;
1628 * The mount has been triggered, re-read mnttab to pick up
1629 * the changes made by autoFS.
1631 fsfreemntlist(mntl);
1632 (void) mutex_lock(&mnttab_lock);
1633 mntl = fsgetmntlist();
1634 (void) mutex_unlock(&mnttab_lock);
1638 * The autoFS mountpoint has been triggered if necessary,
1639 * now search mnttab again to determine if the requested path
1640 * is an LOFS mount of a shared path.
1642 mntpnt_len = 0;
1643 for (ml = mntl; ml; ml = ml->mntl_next) {
1644 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1645 continue;
1647 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1648 *p1 == *p2 && *p1; p1++, p2++)
1651 if (is_substring(&p1, &p2) &&
1652 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1653 mntpnt_len = tmp;
1655 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1656 MAXPATHLEN) {
1657 if (verbose) {
1658 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1659 ml->mntl_mnt->mnt_special, p2,
1660 MAXPATHLEN);
1662 if (retcode)
1663 sharefree(retcode);
1664 retcode = NULL;
1665 goto done;
1668 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1669 (void) strcat(tmp_path, p2);
1670 if (retcode)
1671 sharefree(retcode);
1672 retcode = findentry(tmp_path);
1676 if (retcode) {
1677 assert(strlen(tmp_path) > 0);
1678 (void) strcpy(rpath, tmp_path);
1681 done:
1682 fsfreemntlist(mntl);
1683 return (retcode);
1687 * Determine whether an access list grants rights to a particular host.
1688 * We match on aliases of the hostname as well as on the canonical name.
1689 * Names in the access list may be either hosts or netgroups; they're
1690 * not distinguished syntactically. We check for hosts first because
1691 * it's cheaper, then try netgroups.
1693 * Return values:
1694 * 1 - access is granted
1695 * 0 - access is denied
1696 * -1 - an error occured
1699 in_access_list(struct cln *cln,
1700 char *access_list) /* N.B. we clobber this "input" parameter */
1702 char addr[INET_ADDRSTRLEN];
1703 char buff[256];
1704 int nentries = 0;
1705 char *cstr = access_list;
1706 char *gr = access_list;
1707 int i;
1708 int response;
1709 int ret;
1710 struct netbuf *pnb;
1711 struct nd_hostservlist *clnames = NULL;
1713 /* If no access list - then it's unrestricted */
1714 if (access_list == NULL || *access_list == '\0')
1715 return (1);
1717 if ((pnb = cln_getnbuf(cln)) == NULL)
1718 return (-1);
1720 for (;;) {
1721 if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1722 if (*cstr == ':') {
1723 *cstr = '\0';
1724 } else {
1725 assert(*cstr == '[');
1726 cstr = strchr(cstr + 1, ']');
1727 if (cstr == NULL)
1728 return (-1);
1729 cstr++;
1730 continue;
1735 * If the list name has a '-' prepended then a match of
1736 * the following name implies failure instead of success.
1738 if (*gr == '-') {
1739 response = 0;
1740 gr++;
1741 } else {
1742 response = 1;
1746 * First check if we have '@' entry, as it doesn't
1747 * require client hostname.
1749 if (*gr == '@') {
1750 gr++;
1752 /* Netname support */
1753 if (!isdigit(*gr) && *gr != '[') {
1754 struct netent n, *np;
1756 if ((np = getnetbyname_r(gr, &n, buff,
1757 sizeof (buff))) != NULL &&
1758 np->n_net != 0) {
1759 while ((np->n_net & 0xFF000000u) == 0)
1760 np->n_net <<= 8;
1761 np->n_net = htonl(np->n_net);
1762 if (inet_ntop(AF_INET, &np->n_net, addr,
1763 INET_ADDRSTRLEN) == NULL)
1764 break;
1765 ret = inet_matchaddr(pnb->buf, addr);
1766 if (ret == -1) {
1767 if (errno == EINVAL) {
1768 syslog(LOG_WARNING,
1769 "invalid access "
1770 "list entry: %s",
1771 addr);
1773 return (-1);
1774 } else if (ret == 1) {
1775 return (response);
1778 } else {
1779 ret = inet_matchaddr(pnb->buf, gr);
1780 if (ret == -1) {
1781 if (errno == EINVAL) {
1782 syslog(LOG_WARNING,
1783 "invalid access list "
1784 "entry: %s", gr);
1786 return (-1);
1787 } else if (ret == 1) {
1788 return (response);
1792 goto next;
1796 * No other checks can be performed if client address
1797 * can't be resolved.
1799 if ((clnames = cln_getclientsnames(cln)) == NULL)
1800 goto next;
1802 /* Otherwise loop through all client hostname aliases */
1803 for (i = 0; i < clnames->h_cnt; i++) {
1804 char *host = clnames->h_hostservs[i].h_host;
1807 * If the list name begins with a dot then
1808 * do a domain name suffix comparison.
1809 * A single dot matches any name with no
1810 * suffix.
1812 if (*gr == '.') {
1813 if (*(gr + 1) == '\0') { /* single dot */
1814 if (strchr(host, '.') == NULL)
1815 return (response);
1816 } else {
1817 int off = strlen(host) - strlen(gr);
1818 if (off > 0 &&
1819 strcasecmp(host + off, gr) == 0) {
1820 return (response);
1823 } else {
1824 /* Just do a hostname match */
1825 if (strcasecmp(gr, host) == 0)
1826 return (response);
1830 nentries++;
1832 next:
1833 if (cstr == NULL)
1834 break;
1836 gr = ++cstr;
1839 if (clnames == NULL)
1840 return (0);
1842 return (netgroup_check(clnames, access_list, nentries));
1846 static char *optlist[] = {
1847 #define OPT_RO 0
1848 SHOPT_RO,
1849 #define OPT_RW 1
1850 SHOPT_RW,
1851 #define OPT_ROOT 2
1852 SHOPT_ROOT,
1853 #define OPT_SECURE 3
1854 SHOPT_SECURE,
1855 #define OPT_ANON 4
1856 SHOPT_ANON,
1857 #define OPT_WINDOW 5
1858 SHOPT_WINDOW,
1859 #define OPT_NOSUID 6
1860 SHOPT_NOSUID,
1861 #define OPT_ACLOK 7
1862 SHOPT_ACLOK,
1863 #define OPT_SEC 8
1864 SHOPT_SEC,
1865 #define OPT_NONE 9
1866 SHOPT_NONE,
1867 #define OPT_UIDMAP 10
1868 SHOPT_UIDMAP,
1869 #define OPT_GIDMAP 11
1870 SHOPT_GIDMAP,
1871 NULL
1874 static int
1875 map_flavor(char *str)
1877 seconfig_t sec;
1879 if (nfs_getseconfig_byname(str, &sec))
1880 return (-1);
1882 return (sec.sc_nfsnum);
1886 * If the option string contains a "sec="
1887 * option, then use new option syntax.
1889 static int
1890 newopts(char *opts)
1892 char *head, *p, *val;
1894 if (!opts || *opts == '\0')
1895 return (0);
1897 head = strdup(opts);
1898 if (head == NULL) {
1899 syslog(LOG_ERR, "opts: no memory");
1900 return (0);
1903 p = head;
1904 while (*p) {
1905 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
1906 free(head);
1907 return (1);
1911 free(head);
1912 return (0);
1916 * Given an export and the clients hostname(s)
1917 * determine the security flavors that this
1918 * client is permitted to use.
1920 * This routine is called only for "old" syntax, i.e.
1921 * only one security flavor is allowed. So we need
1922 * to determine two things: the particular flavor,
1923 * and whether the client is allowed to use this
1924 * flavor, i.e. is in the access list.
1926 * Note that if there is no access list, then the
1927 * default is that access is granted.
1929 static int
1930 getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
1932 char *opts, *p, *val;
1933 boolean_t ok = B_FALSE;
1934 int defaultaccess = 1;
1935 boolean_t reject = B_FALSE;
1937 opts = strdup(sh->sh_opts);
1938 if (opts == NULL) {
1939 syslog(LOG_ERR, "getclientsflavors: no memory");
1940 return (0);
1943 flavors[0] = AUTH_SYS;
1944 p = opts;
1946 while (*p) {
1948 switch (getsubopt(&p, optlist, &val)) {
1949 case OPT_SECURE:
1950 flavors[0] = AUTH_DES;
1951 break;
1953 case OPT_RO:
1954 case OPT_RW:
1955 defaultaccess = 0;
1956 if (in_access_list(cln, val) > 0)
1957 ok = B_TRUE;
1958 break;
1960 case OPT_NONE:
1961 defaultaccess = 0;
1962 if (in_access_list(cln, val) > 0)
1963 reject = B_TRUE;
1967 free(opts);
1969 /* none takes precedence over everything else */
1970 if (reject)
1971 ok = B_FALSE;
1973 return (defaultaccess || ok);
1977 * Given an export and the clients hostname(s)
1978 * determine the security flavors that this
1979 * client is permitted to use.
1981 * This is somewhat more complicated than the "old"
1982 * routine because the options may contain multiple
1983 * security flavors (sec=) each with its own access
1984 * lists. So a client could be granted access based
1985 * on a number of security flavors. Note that the
1986 * type of access might not always be the same, the
1987 * client may get readonly access with one flavor
1988 * and readwrite with another, however the client
1989 * is not told this detail, it gets only the list
1990 * of flavors, and only if the client is using
1991 * version 3 of the mount protocol.
1993 static int
1994 getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
1996 char *opts, *p, *val;
1997 char *lasts;
1998 char *f;
1999 boolean_t defaultaccess = B_TRUE; /* default access is rw */
2000 boolean_t access_ok = B_FALSE;
2001 int count, c;
2002 boolean_t reject = B_FALSE;
2004 opts = strdup(sh->sh_opts);
2005 if (opts == NULL) {
2006 syslog(LOG_ERR, "getclientsflavors: no memory");
2007 return (0);
2010 p = opts;
2011 count = c = 0;
2013 while (*p) {
2014 switch (getsubopt(&p, optlist, &val)) {
2015 case OPT_SEC:
2016 if (reject)
2017 access_ok = B_FALSE;
2020 * Before a new sec=xxx option, check if we need
2021 * to move the c index back to the previous count.
2023 if (!defaultaccess && !access_ok) {
2024 c = count;
2027 /* get all the sec=f1[:f2] flavors */
2028 while ((f = strtok_r(val, ":", &lasts)) != NULL) {
2029 flavors[c++] = map_flavor(f);
2030 val = NULL;
2033 /* for a new sec=xxx option, default is rw access */
2034 defaultaccess = B_TRUE;
2035 access_ok = B_FALSE;
2036 reject = B_FALSE;
2037 break;
2039 case OPT_RO:
2040 case OPT_RW:
2041 defaultaccess = B_FALSE;
2042 if (in_access_list(cln, val) > 0)
2043 access_ok = B_TRUE;
2044 break;
2046 case OPT_NONE:
2047 defaultaccess = B_FALSE;
2048 if (in_access_list(cln, val) > 0)
2049 reject = B_TRUE; /* none overides rw/ro */
2050 break;
2054 if (reject)
2055 access_ok = B_FALSE;
2057 if (!defaultaccess && !access_ok)
2058 c = count;
2060 free(opts);
2062 return (c);
2066 * This is a tricky piece of code that parses the
2067 * share options looking for a match on the auth
2068 * flavor that the client is using. If it finds
2069 * a match, then the client is given ro, rw, or
2070 * no access depending whether it is in the access
2071 * list. There is a special case for "secure"
2072 * flavor. Other flavors are values of the new "sec=" option.
2075 check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2076 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2077 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2079 if (newopts(sh->sh_opts))
2080 return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2081 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2082 srv_gids));
2083 else
2084 return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2085 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2086 srv_gids));
2089 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2092 * Get supplemental groups for uid
2094 static int
2095 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2097 struct passwd pwd;
2098 char *pwbuf = alloca(pw_size);
2099 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2100 int tmpngrps;
2101 struct passwd *result;
2103 getpwuid_r(uid, &pwd, pwbuf, pw_size, &result);
2104 if (!result)
2105 return (-1);
2107 tmpgrps[0] = pwd.pw_gid;
2109 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2110 if (tmpngrps <= 0) {
2111 syslog(LOG_WARNING,
2112 "getusergroups(): Unable to get groups for user %s",
2113 pwd.pw_name);
2115 return (-1);
2118 *grps = malloc(tmpngrps * sizeof (gid_t));
2119 if (*grps == NULL) {
2120 syslog(LOG_ERR,
2121 "getusergroups(): Memory allocation failed: %m");
2123 return (-1);
2126 *ngrps = tmpngrps;
2127 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2129 return (0);
2133 * is_a_number(number)
2135 * is the string a number in one of the forms we want to use?
2138 static int
2139 is_a_number(char *number)
2141 int ret = 1;
2142 int hex = 0;
2144 if (strncmp(number, "0x", 2) == 0) {
2145 number += 2;
2146 hex = 1;
2147 } else if (*number == '-') {
2148 number++; /* skip the minus */
2150 while (ret == 1 && *number != '\0') {
2151 if (hex) {
2152 ret = isxdigit(*number++);
2153 } else {
2154 ret = isdigit(*number++);
2157 return (ret);
2160 static boolean_t
2161 get_uid(char *value, uid_t *uid)
2163 if (!is_a_number(value)) {
2164 struct passwd *pw;
2166 * in this case it would have to be a
2167 * user name
2169 pw = getpwnam(value);
2170 if (pw == NULL)
2171 return (B_FALSE);
2172 *uid = pw->pw_uid;
2173 endpwent();
2174 } else {
2175 uint64_t intval;
2176 intval = strtoull(value, NULL, 0);
2177 if (intval > UID_MAX && intval != -1)
2178 return (B_FALSE);
2179 *uid = (uid_t)intval;
2182 return (B_TRUE);
2185 static boolean_t
2186 get_gid(char *value, gid_t *gid)
2188 if (!is_a_number(value)) {
2189 struct group *gr;
2191 * in this case it would have to be a
2192 * group name
2194 gr = getgrnam(value);
2195 if (gr == NULL)
2196 return (B_FALSE);
2197 *gid = gr->gr_gid;
2198 endgrent();
2199 } else {
2200 uint64_t intval;
2201 intval = strtoull(value, NULL, 0);
2202 if (intval > UID_MAX && intval != -1)
2203 return (B_FALSE);
2204 *gid = (gid_t)intval;
2207 return (B_TRUE);
2210 static int
2211 check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2212 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2213 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2215 char *opts, *p, *val;
2216 int match; /* Set when a flavor is matched */
2217 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2218 int list = 0; /* Set when "ro", "rw" is found */
2219 int ro_val = 0; /* Set if ro option is 'ro=' */
2220 int rw_val = 0; /* Set if rw option is 'rw=' */
2222 boolean_t map_deny = B_FALSE;
2224 opts = strdup(sh->sh_opts);
2225 if (opts == NULL) {
2226 syslog(LOG_ERR, "check_client: no memory");
2227 return (0);
2231 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2232 * locally for all of them
2234 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2235 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2236 perm |= NFSAUTH_GROUPS;
2238 p = opts;
2239 match = AUTH_UNIX;
2241 while (*p) {
2242 switch (getsubopt(&p, optlist, &val)) {
2244 case OPT_SECURE:
2245 match = AUTH_DES;
2247 if (perm & NFSAUTH_GROUPS) {
2248 free(*srv_gids);
2249 *srv_ngids = 0;
2250 *srv_gids = NULL;
2251 perm &= ~NFSAUTH_GROUPS;
2254 break;
2256 case OPT_RO:
2257 list++;
2258 if (val != NULL)
2259 ro_val++;
2260 if (in_access_list(cln, val) > 0)
2261 perm |= NFSAUTH_RO;
2262 break;
2264 case OPT_RW:
2265 list++;
2266 if (val != NULL)
2267 rw_val++;
2268 if (in_access_list(cln, val) > 0)
2269 perm |= NFSAUTH_RW;
2270 break;
2272 case OPT_ROOT:
2274 * Check if the client is in
2275 * the root list. Only valid
2276 * for AUTH_SYS.
2278 if (flavor != AUTH_SYS)
2279 break;
2281 if (val == NULL || *val == '\0')
2282 break;
2284 if (clnt_uid != 0)
2285 break;
2287 if (in_access_list(cln, val) > 0) {
2288 perm |= NFSAUTH_ROOT;
2289 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2290 map_deny = B_FALSE;
2292 if (perm & NFSAUTH_GROUPS) {
2293 free(*srv_gids);
2294 *srv_ngids = 0;
2295 *srv_gids = NULL;
2296 perm &= ~NFSAUTH_GROUPS;
2299 break;
2301 case OPT_NONE:
2303 * Check if the client should have no access
2304 * to this share at all. This option behaves
2305 * more like "root" than either "rw" or "ro".
2307 if (in_access_list(cln, val) > 0)
2308 perm |= NFSAUTH_DENIED;
2309 break;
2311 case OPT_UIDMAP: {
2312 char *c;
2313 char *n;
2316 * The uidmap is supported for AUTH_SYS only.
2318 if (flavor != AUTH_SYS)
2319 break;
2321 if (perm & NFSAUTH_UIDMAP || map_deny)
2322 break;
2324 for (c = val; c != NULL; c = n) {
2325 char *s;
2326 char *al;
2327 uid_t srv;
2329 n = strchr(c, '~');
2330 if (n != NULL)
2331 *n++ = '\0';
2333 s = strchr(c, ':');
2334 if (s != NULL) {
2335 *s++ = '\0';
2336 al = strchr(s, ':');
2337 if (al != NULL)
2338 *al++ = '\0';
2341 if (s == NULL || al == NULL)
2342 continue;
2344 if (*c == '\0') {
2345 if (clnt_uid != (uid_t)-1)
2346 continue;
2347 } else if (strcmp(c, "*") != 0) {
2348 uid_t clnt;
2350 if (!get_uid(c, &clnt))
2351 continue;
2353 if (clnt_uid != clnt)
2354 continue;
2357 if (*s == '\0')
2358 srv = UID_NOBODY;
2359 else if (!get_uid(s, &srv))
2360 continue;
2361 else if (srv == (uid_t)-1) {
2362 map_deny = B_TRUE;
2363 break;
2366 if (in_access_list(cln, al) > 0) {
2367 *srv_uid = srv;
2368 perm |= NFSAUTH_UIDMAP;
2370 if (perm & NFSAUTH_GROUPS) {
2371 free(*srv_gids);
2372 *srv_ngids = 0;
2373 *srv_gids = NULL;
2374 perm &= ~NFSAUTH_GROUPS;
2377 break;
2381 break;
2384 case OPT_GIDMAP: {
2385 char *c;
2386 char *n;
2389 * The gidmap is supported for AUTH_SYS only.
2391 if (flavor != AUTH_SYS)
2392 break;
2394 if (perm & NFSAUTH_GIDMAP || map_deny)
2395 break;
2397 for (c = val; c != NULL; c = n) {
2398 char *s;
2399 char *al;
2400 gid_t srv;
2402 n = strchr(c, '~');
2403 if (n != NULL)
2404 *n++ = '\0';
2406 s = strchr(c, ':');
2407 if (s != NULL) {
2408 *s++ = '\0';
2409 al = strchr(s, ':');
2410 if (al != NULL)
2411 *al++ = '\0';
2414 if (s == NULL || al == NULL)
2415 break;
2417 if (*c == '\0') {
2418 if (clnt_gid != (gid_t)-1)
2419 continue;
2420 } else if (strcmp(c, "*") != 0) {
2421 gid_t clnt;
2423 if (!get_gid(c, &clnt))
2424 continue;
2426 if (clnt_gid != clnt)
2427 continue;
2430 if (*s == '\0')
2431 srv = UID_NOBODY;
2432 else if (!get_gid(s, &srv))
2433 continue;
2434 else if (srv == (gid_t)-1) {
2435 map_deny = B_TRUE;
2436 break;
2439 if (in_access_list(cln, al) > 0) {
2440 *srv_gid = srv;
2441 perm |= NFSAUTH_GIDMAP;
2443 if (perm & NFSAUTH_GROUPS) {
2444 free(*srv_gids);
2445 *srv_ngids = 0;
2446 *srv_gids = NULL;
2447 perm &= ~NFSAUTH_GROUPS;
2450 break;
2454 break;
2457 default:
2458 break;
2462 free(opts);
2464 if (perm & NFSAUTH_ROOT) {
2465 *srv_uid = 0;
2466 *srv_gid = 0;
2469 if (map_deny)
2470 perm |= NFSAUTH_DENIED;
2472 if (!(perm & NFSAUTH_UIDMAP))
2473 *srv_uid = clnt_uid;
2474 if (!(perm & NFSAUTH_GIDMAP))
2475 *srv_gid = clnt_gid;
2477 if (flavor != match || perm & NFSAUTH_DENIED)
2478 return (NFSAUTH_DENIED);
2480 if (list) {
2482 * If the client doesn't match an "ro" or "rw"
2483 * list then set no access.
2485 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2486 perm |= NFSAUTH_DENIED;
2487 } else {
2489 * The client matched a flavor entry that
2490 * has no explicit "rw" or "ro" determination.
2491 * Default it to "rw".
2493 perm |= NFSAUTH_RW;
2497 * The client may show up in both ro= and rw=
2498 * lists. If so, then turn off the RO access
2499 * bit leaving RW access.
2501 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2503 * Logically cover all permutations of rw=,ro=.
2504 * In the case where, rw,ro=<host> we would like
2505 * to remove RW access for the host. In all other cases
2506 * RW wins the precedence battle.
2508 if (!rw_val && ro_val) {
2509 perm &= ~(NFSAUTH_RW);
2510 } else {
2511 perm &= ~(NFSAUTH_RO);
2515 return (perm);
2519 * Check if the client has access by using a flavor different from
2520 * the given "flavor". If "flavor" is not in the flavor list,
2521 * return TRUE to indicate that this "flavor" is a wrong sec.
2523 static bool_t
2524 is_wrongsec(share_t *sh, struct cln *cln, int flavor)
2526 int flavor_list[MAX_FLAVORS];
2527 int flavor_count, i;
2529 /* get the flavor list that the client has access with */
2530 flavor_count = getclientsflavors_new(sh, cln, flavor_list);
2532 if (flavor_count == 0)
2533 return (FALSE);
2536 * Check if the given "flavor" is in the flavor_list.
2538 for (i = 0; i < flavor_count; i++) {
2539 if (flavor == flavor_list[i])
2540 return (FALSE);
2544 * If "flavor" is not in the flavor_list, return TRUE to indicate
2545 * that the client should have access by using a security flavor
2546 * different from this "flavor".
2548 return (TRUE);
2552 * Given an export and the client's hostname, we
2553 * check the security options to see whether the
2554 * client is allowed to use the given security flavor.
2556 * The strategy is to proceed through the options looking
2557 * for a flavor match, then pay attention to the ro, rw,
2558 * and root options.
2560 * Note that an entry may list several flavors in a
2561 * single entry, e.g.
2563 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2567 static int
2568 check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2569 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2570 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2572 char *opts, *p, *val;
2573 char *lasts;
2574 char *f;
2575 int match = 0; /* Set when a flavor is matched */
2576 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2577 int list = 0; /* Set when "ro", "rw" is found */
2578 int ro_val = 0; /* Set if ro option is 'ro=' */
2579 int rw_val = 0; /* Set if rw option is 'rw=' */
2581 boolean_t map_deny = B_FALSE;
2583 opts = strdup(sh->sh_opts);
2584 if (opts == NULL) {
2585 syslog(LOG_ERR, "check_client: no memory");
2586 return (0);
2590 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2591 * locally for all of them
2593 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2594 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2595 perm |= NFSAUTH_GROUPS;
2597 p = opts;
2599 while (*p) {
2600 switch (getsubopt(&p, optlist, &val)) {
2602 case OPT_SEC:
2603 if (match)
2604 goto done;
2606 while ((f = strtok_r(val, ":", &lasts))
2607 != NULL) {
2608 if (flavor == map_flavor(f)) {
2609 match = 1;
2610 break;
2612 val = NULL;
2614 break;
2616 case OPT_RO:
2617 if (!match)
2618 break;
2620 list++;
2621 if (val != NULL)
2622 ro_val++;
2623 if (in_access_list(cln, val) > 0)
2624 perm |= NFSAUTH_RO;
2625 break;
2627 case OPT_RW:
2628 if (!match)
2629 break;
2631 list++;
2632 if (val != NULL)
2633 rw_val++;
2634 if (in_access_list(cln, val) > 0)
2635 perm |= NFSAUTH_RW;
2636 break;
2638 case OPT_ROOT:
2640 * Check if the client is in
2641 * the root list. Only valid
2642 * for AUTH_SYS.
2644 if (flavor != AUTH_SYS)
2645 break;
2647 if (!match)
2648 break;
2650 if (val == NULL || *val == '\0')
2651 break;
2653 if (clnt_uid != 0)
2654 break;
2656 if (in_access_list(cln, val) > 0) {
2657 perm |= NFSAUTH_ROOT;
2658 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2659 map_deny = B_FALSE;
2661 if (perm & NFSAUTH_GROUPS) {
2662 free(*srv_gids);
2663 *srv_gids = NULL;
2664 *srv_ngids = 0;
2665 perm &= ~NFSAUTH_GROUPS;
2668 break;
2670 case OPT_NONE:
2672 * Check if the client should have no access
2673 * to this share at all. This option behaves
2674 * more like "root" than either "rw" or "ro".
2676 if (in_access_list(cln, val) > 0)
2677 perm |= NFSAUTH_DENIED;
2678 break;
2680 case OPT_UIDMAP: {
2681 char *c;
2682 char *n;
2685 * The uidmap is supported for AUTH_SYS only.
2687 if (flavor != AUTH_SYS)
2688 break;
2690 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2691 break;
2693 for (c = val; c != NULL; c = n) {
2694 char *s;
2695 char *al;
2696 uid_t srv;
2698 n = strchr(c, '~');
2699 if (n != NULL)
2700 *n++ = '\0';
2702 s = strchr(c, ':');
2703 if (s != NULL) {
2704 *s++ = '\0';
2705 al = strchr(s, ':');
2706 if (al != NULL)
2707 *al++ = '\0';
2710 if (s == NULL || al == NULL)
2711 continue;
2713 if (*c == '\0') {
2714 if (clnt_uid != (uid_t)-1)
2715 continue;
2716 } else if (strcmp(c, "*") != 0) {
2717 uid_t clnt;
2719 if (!get_uid(c, &clnt))
2720 continue;
2722 if (clnt_uid != clnt)
2723 continue;
2726 if (*s == '\0')
2727 srv = UID_NOBODY;
2728 else if (!get_uid(s, &srv))
2729 continue;
2730 else if (srv == (uid_t)-1) {
2731 map_deny = B_TRUE;
2732 break;
2735 if (in_access_list(cln, al) > 0) {
2736 *srv_uid = srv;
2737 perm |= NFSAUTH_UIDMAP;
2739 if (perm & NFSAUTH_GROUPS) {
2740 free(*srv_gids);
2741 *srv_gids = NULL;
2742 *srv_ngids = 0;
2743 perm &= ~NFSAUTH_GROUPS;
2746 break;
2750 break;
2753 case OPT_GIDMAP: {
2754 char *c;
2755 char *n;
2758 * The gidmap is supported for AUTH_SYS only.
2760 if (flavor != AUTH_SYS)
2761 break;
2763 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2764 break;
2766 for (c = val; c != NULL; c = n) {
2767 char *s;
2768 char *al;
2769 gid_t srv;
2771 n = strchr(c, '~');
2772 if (n != NULL)
2773 *n++ = '\0';
2775 s = strchr(c, ':');
2776 if (s != NULL) {
2777 *s++ = '\0';
2778 al = strchr(s, ':');
2779 if (al != NULL)
2780 *al++ = '\0';
2783 if (s == NULL || al == NULL)
2784 break;
2786 if (*c == '\0') {
2787 if (clnt_gid != (gid_t)-1)
2788 continue;
2789 } else if (strcmp(c, "*") != 0) {
2790 gid_t clnt;
2792 if (!get_gid(c, &clnt))
2793 continue;
2795 if (clnt_gid != clnt)
2796 continue;
2799 if (*s == '\0')
2800 srv = UID_NOBODY;
2801 else if (!get_gid(s, &srv))
2802 continue;
2803 else if (srv == (gid_t)-1) {
2804 map_deny = B_TRUE;
2805 break;
2808 if (in_access_list(cln, al) > 0) {
2809 *srv_gid = srv;
2810 perm |= NFSAUTH_GIDMAP;
2812 if (perm & NFSAUTH_GROUPS) {
2813 free(*srv_gids);
2814 *srv_gids = NULL;
2815 *srv_ngids = 0;
2816 perm &= ~NFSAUTH_GROUPS;
2819 break;
2823 break;
2826 default:
2827 break;
2831 done:
2832 if (perm & NFSAUTH_ROOT) {
2833 *srv_uid = 0;
2834 *srv_gid = 0;
2837 if (map_deny)
2838 perm |= NFSAUTH_DENIED;
2840 if (!(perm & NFSAUTH_UIDMAP))
2841 *srv_uid = clnt_uid;
2842 if (!(perm & NFSAUTH_GIDMAP))
2843 *srv_gid = clnt_gid;
2846 * If no match then set the perm accordingly
2848 if (!match || perm & NFSAUTH_DENIED) {
2849 free(opts);
2850 return (NFSAUTH_DENIED);
2853 if (list) {
2855 * If the client doesn't match an "ro" or "rw" list then
2856 * check if it may have access by using a different flavor.
2857 * If so, return NFSAUTH_WRONGSEC.
2858 * If not, return NFSAUTH_DENIED.
2860 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2861 if (is_wrongsec(sh, cln, flavor))
2862 perm |= NFSAUTH_WRONGSEC;
2863 else
2864 perm |= NFSAUTH_DENIED;
2866 } else {
2868 * The client matched a flavor entry that
2869 * has no explicit "rw" or "ro" determination.
2870 * Make sure it defaults to "rw".
2872 perm |= NFSAUTH_RW;
2876 * The client may show up in both ro= and rw=
2877 * lists. If so, then turn off the RO access
2878 * bit leaving RW access.
2880 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2882 * Logically cover all permutations of rw=,ro=.
2883 * In the case where, rw,ro=<host> we would like
2884 * to remove RW access for the host. In all other cases
2885 * RW wins the precedence battle.
2887 if (!rw_val && ro_val) {
2888 perm &= ~(NFSAUTH_RW);
2889 } else {
2890 perm &= ~(NFSAUTH_RO);
2894 free(opts);
2896 return (perm);
2899 void
2900 check_sharetab()
2902 FILE *f;
2903 struct stat st;
2904 static timestruc_t last_sharetab_time;
2905 timestruc_t prev_sharetab_time;
2906 share_t *sh;
2907 struct sh_list *shp, *shp_prev;
2908 int res, c = 0;
2911 * read in /etc/dfs/sharetab if it has changed
2913 if (stat(SHARETAB, &st) != 0) {
2914 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2915 return;
2918 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
2919 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
2921 * No change.
2923 return;
2927 * Remember the mod time, then after getting the
2928 * write lock check again. If another thread
2929 * already did the update, then there's no
2930 * work to do.
2932 prev_sharetab_time = last_sharetab_time;
2934 (void) rw_wrlock(&sharetab_lock);
2936 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
2937 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
2938 (void) rw_unlock(&sharetab_lock);
2939 return;
2943 * Note that since the sharetab is now in memory
2944 * and a snapshot is taken, we no longer have to
2945 * lock the file.
2947 f = fopen(SHARETAB, "r");
2948 if (f == NULL) {
2949 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
2950 (void) rw_unlock(&sharetab_lock);
2951 return;
2955 * Once we are sure /etc/dfs/sharetab has been
2956 * modified, flush netgroup cache entries.
2958 netgrp_cache_flush();
2960 sh_free(share_list); /* free old list */
2961 share_list = NULL;
2963 while ((res = getshare(f, &sh)) > 0) {
2964 c++;
2965 if (strcmp(sh->sh_fstype, "nfs") != 0)
2966 continue;
2968 shp = malloc(sizeof (*shp));
2969 if (shp == NULL)
2970 goto alloc_failed;
2971 if (share_list == NULL)
2972 share_list = shp;
2973 else
2974 /* LINTED not used before set */
2975 shp_prev->shl_next = shp;
2976 shp_prev = shp;
2977 shp->shl_next = NULL;
2978 shp->shl_sh = sharedup(sh);
2979 if (shp->shl_sh == NULL)
2980 goto alloc_failed;
2983 if (res < 0)
2984 syslog(LOG_ERR, "%s: invalid at line %d\n",
2985 SHARETAB, c + 1);
2987 if (stat(SHARETAB, &st) != 0) {
2988 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
2989 (void) fclose(f);
2990 (void) rw_unlock(&sharetab_lock);
2991 return;
2994 last_sharetab_time = st.st_mtim;
2995 (void) fclose(f);
2996 (void) rw_unlock(&sharetab_lock);
2998 return;
3000 alloc_failed:
3002 syslog(LOG_ERR, "check_sharetab: no memory");
3003 sh_free(share_list);
3004 share_list = NULL;
3005 (void) fclose(f);
3006 (void) rw_unlock(&sharetab_lock);
3009 static void
3010 sh_free(struct sh_list *shp)
3012 struct sh_list *next;
3014 while (shp) {
3015 sharefree(shp->shl_sh);
3016 next = shp->shl_next;
3017 free(shp);
3018 shp = next;
3024 * Remove an entry from mounted list
3026 static void
3027 umount(struct svc_req *rqstp)
3029 char *host, *path, *remove_path;
3030 char rpath[MAXPATHLEN];
3031 SVCXPRT *transp;
3032 struct cln cln;
3034 transp = rqstp->rq_xprt;
3035 path = NULL;
3036 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3037 svcerr_decode(transp);
3038 return;
3041 cln_init(&cln, transp);
3043 errno = 0;
3044 if (!svc_sendreply(transp, xdr_void, NULL))
3045 log_cant_reply_cln(&cln);
3047 host = cln_gethost(&cln);
3048 if (host == NULL) {
3050 * Without the hostname we can't do audit or delete
3051 * this host from the mount entries.
3053 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3054 return;
3057 if (verbose)
3058 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3060 audit_mountd_umount(host, path);
3062 remove_path = rpath; /* assume we will use the cannonical path */
3063 if (realpath(path, rpath) == NULL) {
3064 if (verbose)
3065 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3066 remove_path = path; /* use path provided instead */
3069 mntlist_delete(host, remove_path); /* remove from mount list */
3071 cln_fini(&cln);
3073 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3077 * Remove all entries for one machine from mounted list
3079 static void
3080 umountall(struct svc_req *rqstp)
3082 SVCXPRT *transp;
3083 char *host;
3084 struct cln cln;
3086 transp = rqstp->rq_xprt;
3087 if (!svc_getargs(transp, xdr_void, NULL)) {
3088 svcerr_decode(transp);
3089 return;
3092 * We assume that this call is asynchronous and made via rpcbind
3093 * callit routine. Therefore return control immediately. The error
3094 * causes rpcbind to remain silent, as opposed to every machine
3095 * on the net blasting the requester with a response.
3097 svcerr_systemerr(transp);
3099 cln_init(&cln, transp);
3101 host = cln_gethost(&cln);
3102 if (host == NULL) {
3103 /* Can't do anything without the name of the client */
3104 return;
3108 * Remove all hosts entries from mount list
3110 mntlist_delete_all(host);
3112 if (verbose)
3113 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3115 cln_fini(&cln);
3118 void *
3119 exmalloc(size_t size)
3121 void *ret;
3123 if ((ret = malloc(size)) == NULL) {
3124 syslog(LOG_ERR, "Out of memory");
3125 exit(1);
3127 return (ret);