5868 Make checks in inet_matchaddr() more robust
[unleashed.git] / usr / src / cmd / fs.d / nfs / mountd / mountd.c
blobf0a6735a68db435956a68d457b3f8b685fadb29d
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 <tsol/label.h>
79 #include <sys/tsol/label_macro.h>
80 #include <libtsnet.h>
81 #include <sys/sdt.h>
82 #include <libscf.h>
83 #include <limits.h>
84 #include <sys/nvpair.h>
85 #include <attr.h>
86 #include "smfcfg.h"
87 #include <pwd.h>
88 #include <grp.h>
89 #include <alloca.h>
91 extern int daemonize_init(void);
92 extern void daemonize_fini(int);
94 extern int _nfssys(int, void *);
96 struct sh_list *share_list;
98 rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
99 static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
101 static mutex_t logging_queue_lock;
102 static cond_t logging_queue_cv;
104 static share_t *find_lofsentry(char *, int *);
105 static int getclientsflavors_old(share_t *, struct cln *, int *);
106 static int getclientsflavors_new(share_t *, struct cln *, int *);
107 static int check_client_old(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
108 gid_t *, uid_t *, gid_t *, uint_t *, gid_t **);
109 static int check_client_new(share_t *, struct cln *, int, uid_t, gid_t, uint_t,
110 gid_t *, uid_t *, gid_t *i, uint_t *, gid_t **);
111 static void mnt(struct svc_req *, SVCXPRT *);
112 static void mnt_pathconf(struct svc_req *);
113 static int mount(struct svc_req *r);
114 static void sh_free(struct sh_list *);
115 static void umount(struct svc_req *);
116 static void umountall(struct svc_req *);
117 static int newopts(char *);
118 static tsol_tpent_t *get_client_template(struct sockaddr *);
120 static int verbose;
121 static int rejecting;
122 static int mount_vers_min = MOUNTVERS;
123 static int mount_vers_max = MOUNTVERS3;
125 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
127 thread_t nfsauth_thread;
128 thread_t cmd_thread;
129 thread_t logging_thread;
131 typedef struct logging_data {
132 char *ld_host;
133 char *ld_path;
134 char *ld_rpath;
135 int ld_status;
136 char *ld_netid;
137 struct netbuf *ld_nb;
138 struct logging_data *ld_next;
139 } logging_data;
141 static logging_data *logging_head = NULL;
142 static logging_data *logging_tail = NULL;
145 * Our copy of some system variables obtained using sysconf(3c)
147 static long ngroups_max; /* _SC_NGROUPS_MAX */
148 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
150 /* ARGSUSED */
151 static void *
152 nfsauth_svc(void *arg)
154 int doorfd = -1;
155 uint_t darg;
156 #ifdef DEBUG
157 int dfd;
158 #endif
160 if ((doorfd = door_create(nfsauth_func, NULL,
161 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
162 syslog(LOG_ERR, "Unable to create door: %m\n");
163 exit(10);
166 #ifdef DEBUG
168 * Create a file system path for the door
170 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
171 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
172 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
173 (void) close(doorfd);
174 exit(11);
178 * Clean up any stale namespace associations
180 (void) fdetach(MOUNTD_DOOR);
183 * Register in namespace to pass to the kernel to door_ki_open
185 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
186 syslog(LOG_ERR, "Unable to fattach door: %m\n");
187 (void) close(dfd);
188 (void) close(doorfd);
189 exit(12);
191 (void) close(dfd);
192 #endif
195 * Must pass the doorfd down to the kernel.
197 darg = doorfd;
198 (void) _nfssys(MOUNTD_ARGS, &darg);
201 * Wait for incoming calls
203 /*CONSTCOND*/
204 for (;;)
205 (void) pause();
207 /*NOTREACHED*/
208 syslog(LOG_ERR, gettext("Door server exited"));
209 return (NULL);
213 * NFS command service thread code for setup and handling of the
214 * nfs_cmd requests for character set conversion and other future
215 * events.
218 static void *
219 cmd_svc(void *arg)
221 int doorfd = -1;
222 uint_t darg;
224 if ((doorfd = door_create(nfscmd_func, NULL,
225 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
226 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
227 exit(10);
231 * Must pass the doorfd down to the kernel.
233 darg = doorfd;
234 (void) _nfssys(NFSCMD_ARGS, &darg);
237 * Wait for incoming calls
239 /*CONSTCOND*/
240 for (;;)
241 (void) pause();
243 /*NOTREACHED*/
244 syslog(LOG_ERR, gettext("Cmd door server exited"));
245 return (NULL);
248 static void
249 free_logging_data(logging_data *lq)
251 if (lq != NULL) {
252 free(lq->ld_host);
253 free(lq->ld_netid);
255 if (lq->ld_nb != NULL) {
256 free(lq->ld_nb->buf);
257 free(lq->ld_nb);
260 free(lq->ld_path);
261 free(lq->ld_rpath);
263 free(lq);
267 static logging_data *
268 remove_head_of_queue(void)
270 logging_data *lq;
273 * Pull it off the queue.
275 lq = logging_head;
276 if (lq) {
277 logging_head = lq->ld_next;
280 * Drained it.
282 if (logging_head == NULL) {
283 logging_tail = NULL;
287 return (lq);
290 static void
291 do_logging_queue(logging_data *lq)
293 int cleared = 0;
294 char *host;
296 while (lq) {
297 struct cln cln;
299 if (lq->ld_host == NULL) {
300 DTRACE_PROBE(mountd, name_by_lazy);
301 cln_init_lazy(&cln, lq->ld_netid, lq->ld_nb);
302 host = cln_gethost(&cln);
303 } else
304 host = lq->ld_host;
306 audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
308 /* add entry to mount list */
309 if (lq->ld_rpath)
310 mntlist_new(host, lq->ld_rpath);
312 if (lq->ld_host == NULL)
313 cln_fini(&cln);
315 free_logging_data(lq);
316 cleared++;
318 (void) mutex_lock(&logging_queue_lock);
319 lq = remove_head_of_queue();
320 (void) mutex_unlock(&logging_queue_lock);
323 DTRACE_PROBE1(mountd, logging_cleared, cleared);
326 static void *
327 logging_svc(void *arg)
329 logging_data *lq;
331 for (;;) {
332 (void) mutex_lock(&logging_queue_lock);
333 while (logging_head == NULL) {
334 (void) cond_wait(&logging_queue_cv,
335 &logging_queue_lock);
338 lq = remove_head_of_queue();
339 (void) mutex_unlock(&logging_queue_lock);
341 do_logging_queue(lq);
344 /*NOTREACHED*/
345 syslog(LOG_ERR, gettext("Logging server exited"));
346 return (NULL);
349 static int
350 convert_int(int *val, char *str)
352 long lval;
354 if (str == NULL || !isdigit(*str))
355 return (-1);
357 lval = strtol(str, &str, 10);
358 if (*str != '\0' || lval > INT_MAX)
359 return (-2);
361 *val = (int)lval;
362 return (0);
366 main(int argc, char *argv[])
368 int pid;
369 int c;
370 int rpc_svc_fdunlim = 1;
371 int rpc_svc_mode = RPC_SVC_MT_AUTO;
372 int maxrecsz = RPC_MAXDATASIZE;
373 bool_t exclbind = TRUE;
374 bool_t can_do_mlp;
375 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
376 char defval[4];
377 int defvers, ret, bufsz;
378 struct rlimit rl;
379 int listen_backlog = 0;
380 int max_threads = 0;
381 int tmp;
383 int pipe_fd = -1;
386 * Mountd requires uid 0 for:
387 * /etc/rmtab updates (we could chown it to daemon)
388 * /etc/dfs/dfstab reading (it wants to lock out share which
389 * doesn't do any locking before first truncate;
390 * NFS share does; should use fcntl locking instead)
391 * Needed privileges:
392 * auditing
393 * nfs syscall
394 * file dac search (so it can stat all files)
395 * Optional privileges:
396 * MLP
398 can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
399 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
400 PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
401 can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
402 (void) fprintf(stderr,
403 "%s: must be run with sufficient privileges\n",
404 argv[0]);
405 exit(1);
408 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
409 syslog(LOG_ERR, "getrlimit failed");
410 } else {
411 rl.rlim_cur = rl.rlim_max;
412 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
413 syslog(LOG_ERR, "setrlimit failed");
416 (void) enable_extended_FILE_stdio(-1, -1);
418 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
419 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
420 if (ret != SA_OK) {
421 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
422 "failed, using default value");
425 while ((c = getopt(argc, argv, "vrm:")) != EOF) {
426 switch (c) {
427 case 'v':
428 verbose++;
429 break;
430 case 'r':
431 rejecting = 1;
432 break;
433 case 'm':
434 if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
435 (void) fprintf(stderr, "%s: invalid "
436 "max_threads option, using defaults\n",
437 argv[0]);
438 break;
440 max_threads = tmp;
441 break;
442 default:
443 fprintf(stderr, "usage: mountd [-v] [-r]\n");
444 exit(1);
449 * Read in the NFS version values from config file.
451 bufsz = 4;
452 ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
453 SCF_TYPE_INTEGER, NFSD, &bufsz);
454 if (ret == SA_OK) {
455 errno = 0;
456 defvers = strtol(defval, (char **)NULL, 10);
457 if (errno == 0) {
458 mount_vers_min = defvers;
460 * special because NFSv2 is
461 * supported by mount v1 & v2
463 if (defvers == NFS_VERSION)
464 mount_vers_min = MOUNTVERS;
468 bufsz = 4;
469 ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
470 SCF_TYPE_INTEGER, NFSD, &bufsz);
471 if (ret == SA_OK) {
472 errno = 0;
473 defvers = strtol(defval, (char **)NULL, 10);
474 if (errno == 0) {
475 mount_vers_max = defvers;
479 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
480 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
481 if (ret != SA_OK) {
482 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
483 "failed, using default value");
487 * Sanity check versions,
488 * even though we may get versions > MOUNTVERS3, we still need
489 * to start nfsauth service, so continue on regardless of values.
491 if (mount_vers_min > mount_vers_max) {
492 fprintf(stderr, "server_versmin > server_versmax\n");
493 mount_vers_max = mount_vers_min;
495 (void) setlocale(LC_ALL, "");
496 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
497 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
498 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
499 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
501 netgroup_init();
503 #if !defined(TEXT_DOMAIN)
504 #define TEXT_DOMAIN "SYS_TEST"
505 #endif
506 (void) textdomain(TEXT_DOMAIN);
508 /* Don't drop core if the NFS module isn't loaded. */
509 (void) signal(SIGSYS, SIG_IGN);
511 pipe_fd = daemonize_init();
514 * If we coredump it'll be in /core
516 if (chdir("/") < 0)
517 fprintf(stderr, "chdir /: %s\n", strerror(errno));
519 openlog("mountd", LOG_PID, LOG_DAEMON);
522 * establish our lock on the lock file and write our pid to it.
523 * exit if some other process holds the lock, or if there's any
524 * error in writing/locking the file.
526 pid = _enter_daemon_lock(MOUNTD);
527 switch (pid) {
528 case 0:
529 break;
530 case -1:
531 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
532 strerror(errno));
533 exit(2);
534 default:
535 /* daemon was already running */
536 exit(0);
539 audit_mountd_setup(); /* BSM */
542 * Get required system variables
544 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
545 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
546 exit(1);
548 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
549 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
550 exit(1);
554 * Set number of file descriptors to unlimited
556 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
557 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
561 * Tell RPC that we want automatic thread mode.
562 * A new thread will be spawned for each request.
564 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
565 fprintf(stderr, "unable to set automatic MT mode\n");
566 exit(1);
570 * Enable non-blocking mode and maximum record size checks for
571 * connection oriented transports.
573 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
574 fprintf(stderr, "unable to set RPC max record size\n");
578 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
579 * from being hijacked by a bind to a more specific addr.
581 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
582 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
586 * Set the maximum number of outstanding connection
587 * indications (listen backlog) to the value specified.
589 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
590 &listen_backlog)) {
591 fprintf(stderr, "unable to set listen backlog\n");
592 exit(1);
596 * If max_threads was specified, then set the
597 * maximum number of threads to the value specified.
599 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
600 fprintf(stderr, "unable to set max_threads\n");
601 exit(1);
605 * Make sure to unregister any previous versions in case the
606 * user is reconfiguring the server in interesting ways.
608 svc_unreg(MOUNTPROG, MOUNTVERS);
609 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
610 svc_unreg(MOUNTPROG, MOUNTVERS3);
613 * Create the nfsauth thread with same signal disposition
614 * as the main thread. We need to create a separate thread
615 * since mountd() will be both an RPC server (for remote
616 * traffic) _and_ a doors server (for kernel upcalls).
618 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
619 fprintf(stderr,
620 gettext("Failed to create NFSAUTH svc thread\n"));
621 exit(2);
625 * Create the cmd service thread with same signal disposition
626 * as the main thread. We need to create a separate thread
627 * since mountd() will be both an RPC server (for remote
628 * traffic) _and_ a doors server (for kernel upcalls).
630 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
631 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
632 exit(2);
636 * Create an additional thread to service the rmtab and
637 * audit_mountd_mount logging for mount requests. Use the same
638 * signal disposition as the main thread. We create
639 * a separate thread to allow the mount request threads to
640 * clear as soon as possible.
642 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
643 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
644 exit(2);
648 * Create datagram and connection oriented services
650 if (mount_vers_max >= MOUNTVERS) {
651 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
652 fprintf(stderr,
653 "couldn't register datagram_v MOUNTVERS\n");
654 exit(1);
656 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
657 fprintf(stderr,
658 "couldn't register circuit_v MOUNTVERS\n");
659 exit(1);
663 if (mount_vers_max >= MOUNTVERS_POSIX) {
664 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
665 "datagram_v") == 0) {
666 fprintf(stderr,
667 "couldn't register datagram_v MOUNTVERS_POSIX\n");
668 exit(1);
670 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
671 "circuit_v") == 0) {
672 fprintf(stderr,
673 "couldn't register circuit_v MOUNTVERS_POSIX\n");
674 exit(1);
678 if (mount_vers_max >= MOUNTVERS3) {
679 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
680 fprintf(stderr,
681 "couldn't register datagram_v MOUNTVERS3\n");
682 exit(1);
684 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
685 fprintf(stderr,
686 "couldn't register circuit_v MOUNTVERS3\n");
687 exit(1);
692 * Start serving
694 rmtab_load();
696 daemonize_fini(pipe_fd);
698 /* Get rid of the most dangerous basic privileges. */
699 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
700 (char *)NULL);
702 svc_run();
703 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
704 abort();
706 /* NOTREACHED */
707 return (0);
711 * Server procedure switch routine
713 void
714 mnt(struct svc_req *rqstp, SVCXPRT *transp)
716 switch (rqstp->rq_proc) {
717 case NULLPROC:
718 errno = 0;
719 if (!svc_sendreply(transp, xdr_void, (char *)0))
720 log_cant_reply(transp);
721 return;
723 case MOUNTPROC_MNT:
724 (void) mount(rqstp);
725 return;
727 case MOUNTPROC_DUMP:
728 mntlist_send(transp);
729 return;
731 case MOUNTPROC_UMNT:
732 umount(rqstp);
733 return;
735 case MOUNTPROC_UMNTALL:
736 umountall(rqstp);
737 return;
739 case MOUNTPROC_EXPORT:
740 case MOUNTPROC_EXPORTALL:
741 export(rqstp);
742 return;
744 case MOUNTPROC_PATHCONF:
745 if (rqstp->rq_vers == MOUNTVERS_POSIX)
746 mnt_pathconf(rqstp);
747 else
748 svcerr_noproc(transp);
749 return;
751 default:
752 svcerr_noproc(transp);
753 return;
757 void
758 log_cant_reply_cln(struct cln *cln)
760 int saverrno;
761 char *host;
763 saverrno = errno; /* save error code */
765 host = cln_gethost(cln);
766 if (host == NULL)
767 return;
769 errno = saverrno;
770 if (errno == 0)
771 syslog(LOG_ERR, "couldn't send reply to %s", host);
772 else
773 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
776 void
777 log_cant_reply(SVCXPRT *transp)
779 int saverrno;
780 struct cln cln;
782 saverrno = errno; /* save error code */
783 cln_init(&cln, transp);
784 errno = saverrno;
786 log_cant_reply_cln(&cln);
788 cln_fini(&cln);
792 * Answer pathconf questions for the mount point fs
794 static void
795 mnt_pathconf(struct svc_req *rqstp)
797 SVCXPRT *transp;
798 struct pathcnf p;
799 char *path, rpath[MAXPATHLEN];
800 struct stat st;
802 transp = rqstp->rq_xprt;
803 path = NULL;
804 (void) memset((caddr_t)&p, 0, sizeof (p));
806 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
807 svcerr_decode(transp);
808 return;
810 if (lstat(path, &st) < 0) {
811 _PC_SET(_PC_ERROR, p.pc_mask);
812 goto done;
815 * Get a path without symbolic links.
817 if (realpath(path, rpath) == NULL) {
818 syslog(LOG_DEBUG,
819 "mount request: realpath failed on %s: %m",
820 path);
821 _PC_SET(_PC_ERROR, p.pc_mask);
822 goto done;
824 (void) memset((caddr_t)&p, 0, sizeof (p));
826 * can't ask about devices over NFS
828 _PC_SET(_PC_MAX_CANON, p.pc_mask);
829 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
830 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
831 _PC_SET(_PC_VDISABLE, p.pc_mask);
833 errno = 0;
834 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
835 if (errno)
836 _PC_SET(_PC_LINK_MAX, p.pc_mask);
837 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
838 if (errno)
839 _PC_SET(_PC_NAME_MAX, p.pc_mask);
840 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
841 if (errno)
842 _PC_SET(_PC_PATH_MAX, p.pc_mask);
843 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
844 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
845 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
846 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
848 done:
849 errno = 0;
850 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
851 log_cant_reply(transp);
852 if (path != NULL)
853 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
857 * If the rootmount (export) option is specified, the all mount requests for
858 * subdirectories return EACCES.
860 static int
861 checkrootmount(share_t *sh, char *rpath)
863 char *val;
865 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
866 free(val);
867 if (strcmp(sh->sh_path, rpath) != 0)
868 return (0);
869 else
870 return (1);
871 } else
872 return (1);
875 #define MAX_FLAVORS 128
878 * Return only EACCES if client does not have access
879 * to this directory.
880 * "If the server exports only /a/b, an attempt to
881 * mount a/b/c will fail with ENOENT if the directory
882 * does not exist"... However, if the client
883 * does not have access to /a/b, an attacker can
884 * determine whether the directory exists.
885 * This routine checks either existence of the file or
886 * existence of the file name entry in the mount table.
887 * If the file exists and there is no file name entry,
888 * the error returned should be EACCES.
889 * If the file does not exist, it must be determined
890 * whether the client has access to a parent
891 * directory. If the client has access to a parent
892 * directory, the error returned should be ENOENT,
893 * otherwise EACCES.
895 static int
896 mount_enoent_error(struct cln *cln, char *path, char *rpath, int *flavor_list)
898 char *checkpath, *dp;
899 share_t *sh = NULL;
900 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
901 int flavor_count;
903 checkpath = strdup(path);
904 if (checkpath == NULL) {
905 syslog(LOG_ERR, "mount_enoent: no memory");
906 return (EACCES);
909 /* CONSTCOND */
910 while (1) {
911 if (sh) {
912 sharefree(sh);
913 sh = NULL;
916 if ((sh = findentry(rpath)) == NULL &&
917 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
919 * There is no file name entry.
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 if (checkrootmount(sh, rpath) == 0) {
927 * This is a "nosub" only export, in which case,
928 * mounting subdirectories isn't allowed.
929 * If the file (with symbolic links resolved) exists,
930 * the error returned should be EACCES.
932 if (realpath_error == 0)
933 break;
934 } else {
936 * Check permissions in mount table.
938 if (newopts(sh->sh_opts))
939 flavor_count = getclientsflavors_new(sh, cln,
940 flavor_list);
941 else
942 flavor_count = getclientsflavors_old(sh, cln,
943 flavor_list);
944 if (flavor_count != 0) {
946 * Found entry in table and
947 * client has correct permissions.
949 reply_error = ENOENT;
950 break;
955 * Check all parent directories.
957 dp = strrchr(checkpath, '/');
958 if (dp == NULL)
959 break;
960 *dp = '\0';
961 if (strlen(checkpath) == 0)
962 break;
964 * Get the real path (no symbolic links in it)
966 if (realpath(checkpath, rpath) == NULL) {
967 if (errno != ENOENT)
968 break;
969 } else {
970 realpath_error = 0;
974 if (sh)
975 sharefree(sh);
976 free(checkpath);
977 return (reply_error);
981 * We need to inform the caller whether or not we were
982 * able to add a node to the queue. If we are not, then
983 * it is up to the caller to go ahead and log the data.
985 static int
986 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
987 char *rpath, int status, int error)
989 logging_data *lq;
990 struct netbuf *nb;
992 lq = (logging_data *)calloc(1, sizeof (logging_data));
993 if (lq == NULL)
994 goto cleanup;
997 * We might not yet have the host...
999 if (host) {
1000 DTRACE_PROBE1(mountd, log_host, host);
1001 lq->ld_host = strdup(host);
1002 if (lq->ld_host == NULL)
1003 goto cleanup;
1004 } else {
1005 DTRACE_PROBE(mountd, log_no_host);
1007 lq->ld_netid = strdup(transp->xp_netid);
1008 if (lq->ld_netid == NULL)
1009 goto cleanup;
1011 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1012 if (lq->ld_nb == NULL)
1013 goto cleanup;
1015 nb = svc_getrpccaller(transp);
1016 if (nb == NULL) {
1017 DTRACE_PROBE(mountd, e__nb__enqueue);
1018 goto cleanup;
1021 DTRACE_PROBE(mountd, nb_set_enqueue);
1023 lq->ld_nb->maxlen = nb->maxlen;
1024 lq->ld_nb->len = nb->len;
1026 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1027 if (lq->ld_nb->buf == NULL)
1028 goto cleanup;
1030 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1033 lq->ld_path = strdup(path);
1034 if (lq->ld_path == NULL)
1035 goto cleanup;
1037 if (!error) {
1038 lq->ld_rpath = strdup(rpath);
1039 if (lq->ld_rpath == NULL)
1040 goto cleanup;
1043 lq->ld_status = status;
1046 * Add to the tail of the logging queue.
1048 (void) mutex_lock(&logging_queue_lock);
1049 if (logging_tail == NULL) {
1050 logging_tail = logging_head = lq;
1051 } else {
1052 logging_tail->ld_next = lq;
1053 logging_tail = lq;
1055 (void) cond_signal(&logging_queue_cv);
1056 (void) mutex_unlock(&logging_queue_lock);
1058 return (TRUE);
1060 cleanup:
1062 free_logging_data(lq);
1064 return (FALSE);
1068 #define CLN_CLNAMES (1 << 0)
1069 #define CLN_HOST (1 << 1)
1071 static void
1072 cln_init_common(struct cln *cln, SVCXPRT *transp, char *netid,
1073 struct netbuf *nbuf)
1075 if ((cln->transp = transp) != NULL) {
1076 assert(netid == NULL && nbuf == NULL);
1077 cln->netid = transp->xp_netid;
1078 cln->nbuf = svc_getrpccaller(transp);
1079 } else {
1080 cln->netid = netid;
1081 cln->nbuf = nbuf;
1084 cln->nconf = NULL;
1085 cln->clnames = NULL;
1086 cln->host = NULL;
1088 cln->flags = 0;
1091 void
1092 cln_init(struct cln *cln, SVCXPRT *transp)
1094 cln_init_common(cln, transp, NULL, NULL);
1097 void
1098 cln_init_lazy(struct cln *cln, char *netid, struct netbuf *nbuf)
1100 cln_init_common(cln, NULL, netid, nbuf);
1103 void
1104 cln_fini(struct cln *cln)
1106 if (cln->nconf != NULL)
1107 freenetconfigent(cln->nconf);
1109 if (cln->clnames != NULL)
1110 netdir_free(cln->clnames, ND_HOSTSERVLIST);
1112 free(cln->host);
1115 struct netbuf *
1116 cln_getnbuf(struct cln *cln)
1118 return (cln->nbuf);
1121 struct nd_hostservlist *
1122 cln_getclientsnames(struct cln *cln)
1124 if ((cln->flags & CLN_CLNAMES) == 0) {
1126 * nconf is not needed if we do not have nbuf (see
1127 * cln_gethost() too), so we check for nbuf and in a case it is
1128 * NULL we do not try to get nconf.
1130 if (cln->netid != NULL && cln->nbuf != NULL) {
1131 cln->nconf = getnetconfigent(cln->netid);
1132 if (cln->nconf == NULL)
1133 syslog(LOG_ERR, "%s: getnetconfigent failed",
1134 cln->netid);
1137 if (cln->nconf != NULL && cln->nbuf != NULL)
1138 (void) __netdir_getbyaddr_nosrv(cln->nconf,
1139 &cln->clnames, cln->nbuf);
1141 cln->flags |= CLN_CLNAMES;
1144 return (cln->clnames);
1148 * Return B_TRUE if the host is already available at no cost
1150 boolean_t
1151 cln_havehost(struct cln *cln)
1153 return ((cln->flags & (CLN_CLNAMES | CLN_HOST)) != 0);
1156 char *
1157 cln_gethost(struct cln *cln)
1159 if (cln_getclientsnames(cln) != NULL)
1160 return (cln->clnames->h_hostservs[0].h_host);
1162 if ((cln->flags & CLN_HOST) == 0) {
1163 if (cln->nconf == NULL || cln->nbuf == NULL) {
1164 cln->host = strdup("(anon)");
1165 } else {
1166 char host[MAXIPADDRLEN];
1168 if (strcmp(cln->nconf->nc_protofmly, NC_INET) == 0) {
1169 struct sockaddr_in *sa;
1171 /* LINTED pointer alignment */
1172 sa = (struct sockaddr_in *)(cln->nbuf->buf);
1173 (void) inet_ntoa_r(sa->sin_addr, host);
1175 cln->host = strdup(host);
1176 } else if (strcmp(cln->nconf->nc_protofmly,
1177 NC_INET6) == 0) {
1178 struct sockaddr_in6 *sa;
1180 /* LINTED pointer alignment */
1181 sa = (struct sockaddr_in6 *)(cln->nbuf->buf);
1182 (void) inet_ntop(AF_INET6,
1183 sa->sin6_addr.s6_addr,
1184 host, INET6_ADDRSTRLEN);
1186 cln->host = strdup(host);
1187 } else {
1188 syslog(LOG_ERR, gettext("Client's address is "
1189 "neither IPv4 nor IPv6"));
1191 cln->host = strdup("(anon)");
1195 cln->flags |= CLN_HOST;
1198 return (cln->host);
1202 * Check mount requests, add to mounted list if ok
1204 static int
1205 mount(struct svc_req *rqstp)
1207 SVCXPRT *transp;
1208 int version, vers;
1209 struct fhstatus fhs;
1210 struct mountres3 mountres3;
1211 char fh[FHSIZE3];
1212 int len = FHSIZE3;
1213 char *path, rpath[MAXPATHLEN];
1214 share_t *sh = NULL;
1215 struct cln cln;
1216 char *host = NULL;
1217 int error = 0, lofs_tried = 0, enqueued;
1218 int flavor_list[MAX_FLAVORS];
1219 int flavor_count;
1220 ucred_t *uc = NULL;
1222 int audit_status;
1224 transp = rqstp->rq_xprt;
1225 version = rqstp->rq_vers;
1226 path = NULL;
1228 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1229 svcerr_decode(transp);
1230 return (EACCES);
1233 cln_init(&cln, transp);
1236 * Put off getting the name for the client until we
1237 * need it. This is a performance gain. If we are logging,
1238 * then we don't care about performance and might as well
1239 * get the host name now in case we need to spit out an
1240 * error message.
1242 if (verbose) {
1243 DTRACE_PROBE(mountd, name_by_verbose);
1244 if ((host = cln_gethost(&cln)) == NULL) {
1246 * We failed to get a name for the client, even
1247 * 'anon', probably because we ran out of memory.
1248 * In this situation it doesn't make sense to
1249 * allow the mount to succeed.
1251 error = EACCES;
1252 goto reply;
1257 * If the version being used is less than the minimum version,
1258 * the filehandle translation should not be provided to the
1259 * client.
1261 if (rejecting || version < mount_vers_min) {
1262 if (verbose)
1263 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1264 host, path);
1265 error = EACCES;
1266 goto reply;
1270 * Trusted Extension doesn't support nfsv2. nfsv2 client
1271 * uses MOUNT protocol v1 and v2. To prevent circumventing
1272 * TX label policy via using nfsv2 client, reject a mount
1273 * request with version less than 3 and log an error.
1275 if (is_system_labeled()) {
1276 if (version < 3) {
1277 if (verbose)
1278 syslog(LOG_ERR,
1279 "Rejected mount: TX doesn't support NFSv2");
1280 error = EACCES;
1281 goto reply;
1286 * Get the real path (no symbolic links in it)
1288 if (realpath(path, rpath) == NULL) {
1289 error = errno;
1290 if (verbose)
1291 syslog(LOG_ERR,
1292 "mount request: realpath: %s: %m", path);
1293 if (error == ENOENT)
1294 error = mount_enoent_error(&cln, path, rpath,
1295 flavor_list);
1296 goto reply;
1299 if ((sh = findentry(rpath)) == NULL &&
1300 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1301 error = EACCES;
1302 goto reply;
1306 * Check if this is a "nosub" only export, in which case, mounting
1307 * subdirectories isn't allowed. Bug 1184573.
1309 if (checkrootmount(sh, rpath) == 0) {
1310 error = EACCES;
1311 goto reply;
1314 if (newopts(sh->sh_opts))
1315 flavor_count = getclientsflavors_new(sh, &cln, flavor_list);
1316 else
1317 flavor_count = getclientsflavors_old(sh, &cln, flavor_list);
1319 if (flavor_count == 0) {
1320 error = EACCES;
1321 goto reply;
1325 * Check MAC policy here. The server side policy should be
1326 * consistent with client side mount policy, i.e.
1327 * - we disallow an admin_low unlabeled client to mount
1328 * - we disallow mount from a lower labeled client.
1330 if (is_system_labeled()) {
1331 m_label_t *clabel = NULL;
1332 m_label_t *slabel = NULL;
1333 m_label_t admin_low;
1335 if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1336 syslog(LOG_ERR,
1337 "mount request: Failed to get caller's ucred : %m");
1338 error = EACCES;
1339 goto reply;
1341 if ((clabel = ucred_getlabel(uc)) == NULL) {
1342 syslog(LOG_ERR,
1343 "mount request: can't get client label from ucred");
1344 error = EACCES;
1345 goto reply;
1348 bsllow(&admin_low);
1349 if (blequal(&admin_low, clabel)) {
1350 struct sockaddr *ca;
1351 tsol_tpent_t *tp;
1353 ca = (struct sockaddr *)(void *)svc_getrpccaller(
1354 rqstp->rq_xprt)->buf;
1355 if (ca == NULL) {
1356 error = EACCES;
1357 goto reply;
1360 * get trusted network template associated
1361 * with the client.
1363 tp = get_client_template(ca);
1364 if (tp == NULL || tp->host_type != SUN_CIPSO) {
1365 if (tp != NULL)
1366 tsol_freetpent(tp);
1367 error = EACCES;
1368 goto reply;
1370 tsol_freetpent(tp);
1371 } else {
1372 if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1373 error = EACCES;
1374 goto reply;
1377 if (getlabel(rpath, slabel) != 0) {
1378 m_label_free(slabel);
1379 error = EACCES;
1380 goto reply;
1383 if (!bldominates(clabel, slabel)) {
1384 m_label_free(slabel);
1385 error = EACCES;
1386 goto reply;
1388 m_label_free(slabel);
1393 * Now get the filehandle.
1395 * NFS V2 clients get a 32 byte filehandle.
1396 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1397 * the embedded FIDs.
1399 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1401 /* LINTED pointer alignment */
1402 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1403 if (errno == EINVAL &&
1404 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1405 errno = 0;
1406 continue;
1408 error = errno == EINVAL ? EACCES : errno;
1409 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1410 path);
1411 break;
1414 if (version == MOUNTVERS3) {
1415 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1416 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1417 } else {
1418 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1421 reply:
1422 if (uc != NULL)
1423 ucred_free(uc);
1425 switch (version) {
1426 case MOUNTVERS:
1427 case MOUNTVERS_POSIX:
1428 if (error == EINVAL)
1429 fhs.fhs_status = NFSERR_ACCES;
1430 else if (error == EREMOTE)
1431 fhs.fhs_status = NFSERR_REMOTE;
1432 else
1433 fhs.fhs_status = error;
1435 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1436 log_cant_reply_cln(&cln);
1438 audit_status = fhs.fhs_status;
1439 break;
1441 case MOUNTVERS3:
1442 if (!error) {
1443 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1444 flavor_list;
1445 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1446 flavor_count;
1448 } else if (error == ENAMETOOLONG)
1449 error = MNT3ERR_NAMETOOLONG;
1451 mountres3.fhs_status = error;
1452 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1453 log_cant_reply_cln(&cln);
1455 audit_status = mountres3.fhs_status;
1456 break;
1459 if (cln_havehost(&cln))
1460 host = cln_gethost(&cln);
1462 if (verbose)
1463 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1464 (host == NULL) ? "unknown host" : host,
1465 error ? "denied" : "mounted", path);
1468 * If we can not create a queue entry, go ahead and do it
1469 * in the context of this thread.
1471 enqueued = enqueue_logging_data(host, transp, path, rpath,
1472 audit_status, error);
1473 if (enqueued == FALSE) {
1474 if (host == NULL) {
1475 DTRACE_PROBE(mountd, name_by_in_thread);
1476 host = cln_gethost(&cln);
1479 DTRACE_PROBE(mountd, logged_in_thread);
1480 audit_mountd_mount(host, path, audit_status); /* BSM */
1481 if (!error)
1482 mntlist_new(host, rpath); /* add entry to mount list */
1485 if (path != NULL)
1486 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1488 if (sh)
1489 sharefree(sh);
1491 cln_fini(&cln);
1493 return (error);
1497 * Determine whether two paths are within the same file system.
1498 * Returns nonzero (true) if paths are the same, zero (false) if
1499 * they are different. If an error occurs, return false.
1501 * Use the actual FSID if it's available (via getattrat()); otherwise,
1502 * fall back on st_dev.
1504 * With ZFS snapshots, st_dev differs from the regular file system
1505 * versus the snapshot. But the fsid is the same throughout. Thus
1506 * the fsid is a better test.
1508 static int
1509 same_file_system(const char *path1, const char *path2)
1511 uint64_t fsid1, fsid2;
1512 struct stat64 st1, st2;
1513 nvlist_t *nvl1 = NULL;
1514 nvlist_t *nvl2 = NULL;
1516 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1517 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1518 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1519 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1520 nvlist_free(nvl1);
1521 nvlist_free(nvl2);
1523 * We have found fsid's for both paths.
1526 if (fsid1 == fsid2)
1527 return (B_TRUE);
1529 return (B_FALSE);
1532 nvlist_free(nvl1);
1533 nvlist_free(nvl2);
1536 * We were unable to find fsid's for at least one of the paths.
1537 * fall back on st_dev.
1540 if (stat64(path1, &st1) < 0) {
1541 syslog(LOG_NOTICE, "%s: %m", path1);
1542 return (B_FALSE);
1544 if (stat64(path2, &st2) < 0) {
1545 syslog(LOG_NOTICE, "%s: %m", path2);
1546 return (B_FALSE);
1549 if (st1.st_dev == st2.st_dev)
1550 return (B_TRUE);
1552 return (B_FALSE);
1555 share_t *
1556 findentry(char *path)
1558 share_t *sh = NULL;
1559 struct sh_list *shp;
1560 char *p1, *p2;
1562 check_sharetab();
1564 (void) rw_rdlock(&sharetab_lock);
1566 for (shp = share_list; shp; shp = shp->shl_next) {
1567 sh = shp->shl_sh;
1568 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1569 if (*p1 == '\0')
1570 goto done; /* exact match */
1573 * Now compare the pathnames for three cases:
1575 * Parent: /export/foo (no trailing slash on parent)
1576 * Child: /export/foo/bar
1578 * Parent: /export/foo/ (trailing slash on parent)
1579 * Child: /export/foo/bar
1581 * Parent: /export/foo/ (no trailing slash on child)
1582 * Child: /export/foo
1584 if ((*p1 == '\0' && *p2 == '/') ||
1585 (*p1 == '\0' && *(p1-1) == '/') ||
1586 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1588 * We have a subdirectory. Test whether the
1589 * subdirectory is in the same file system.
1591 if (same_file_system(path, sh->sh_path))
1592 goto done;
1595 done:
1596 sh = shp ? sharedup(sh) : NULL;
1598 (void) rw_unlock(&sharetab_lock);
1600 return (sh);
1604 static int
1605 is_substring(char **mntp, char **path)
1607 char *p1 = *mntp, *p2 = *path;
1609 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1610 return (1);
1611 else if (*p1 == '\0' && *p2 == '/')
1612 return (1);
1613 else if (*p1 == '\0' && *(p1-1) == '/') {
1614 *path = --p2; /* we need the slash in p2 */
1615 return (1);
1616 } else if (*p2 == '\0') {
1617 while (*p1 == '/')
1618 p1++;
1619 if (*p1 == '\0') /* exact match */
1620 return (1);
1622 return (0);
1626 * find_lofsentry() searches for the real path which this requested LOFS path
1627 * (rpath) shadows. If found, it will return the sharetab entry of
1628 * the real path that corresponds to the LOFS path.
1629 * We first search mnttab to see if the requested path is an automounted
1630 * path. If it is an automounted path, it will trigger the mount by stat()ing
1631 * the requested path. Note that it is important to check that this path is
1632 * actually an automounted path, otherwise we would stat() a path which may
1633 * turn out to be NFS and block indefinitely on a dead server. The automounter
1634 * times-out if the server is dead, so there's no risk of hanging this
1635 * thread waiting for stat().
1636 * After the mount has been triggered (if necessary), we look for a
1637 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1638 * is a substring of the rpath. If found, we construct a new path by
1639 * concatenating the mnt_special and the remaining of rpath, call findentry()
1640 * to make sure the 'real path' is shared.
1642 static share_t *
1643 find_lofsentry(char *rpath, int *done_flag)
1645 struct stat r_stbuf;
1646 mntlist_t *ml, *mntl, *mntpnt = NULL;
1647 share_t *retcode = NULL;
1648 char tmp_path[MAXPATHLEN];
1649 int mntpnt_len = 0, tmp;
1650 char *p1, *p2;
1652 if ((*done_flag)++)
1653 return (retcode);
1656 * While fsgetmntlist() uses lockf() to
1657 * lock the mnttab before reading it in,
1658 * the lock ignores threads in the same process.
1659 * Read in the mnttab with the protection of a mutex.
1661 (void) mutex_lock(&mnttab_lock);
1662 mntl = fsgetmntlist();
1663 (void) mutex_unlock(&mnttab_lock);
1666 * Obtain the mountpoint for the requested path.
1668 for (ml = mntl; ml; ml = ml->mntl_next) {
1669 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1670 *p1 == *p2 && *p1; p1++, p2++)
1672 if (is_substring(&p1, &p2) &&
1673 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1674 mntpnt = ml;
1675 mntpnt_len = tmp;
1680 * If the path needs to be autoFS mounted, trigger the mount by
1681 * stat()ing it. This is determined by checking whether the
1682 * mountpoint we just found is of type autofs.
1684 if (mntpnt != NULL &&
1685 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1687 * The requested path is a substring of an autoFS filesystem.
1688 * Trigger the mount.
1690 if (stat(rpath, &r_stbuf) < 0) {
1691 if (verbose)
1692 syslog(LOG_NOTICE, "%s: %m", rpath);
1693 goto done;
1695 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1697 * The requested path is a directory, stat(2) it
1698 * again with a trailing '.' to force the autoFS
1699 * module to trigger the mount of indirect
1700 * automount entries, such as /net/jurassic/.
1702 if (strlen(rpath) + 2 > MAXPATHLEN) {
1703 if (verbose) {
1704 syslog(LOG_NOTICE,
1705 "%s/.: exceeds MAXPATHLEN %d",
1706 rpath, MAXPATHLEN);
1708 goto done;
1710 (void) strcpy(tmp_path, rpath);
1711 (void) strcat(tmp_path, "/.");
1713 if (stat(tmp_path, &r_stbuf) < 0) {
1714 if (verbose)
1715 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1716 goto done;
1721 * The mount has been triggered, re-read mnttab to pick up
1722 * the changes made by autoFS.
1724 fsfreemntlist(mntl);
1725 (void) mutex_lock(&mnttab_lock);
1726 mntl = fsgetmntlist();
1727 (void) mutex_unlock(&mnttab_lock);
1731 * The autoFS mountpoint has been triggered if necessary,
1732 * now search mnttab again to determine if the requested path
1733 * is an LOFS mount of a shared path.
1735 mntpnt_len = 0;
1736 for (ml = mntl; ml; ml = ml->mntl_next) {
1737 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1738 continue;
1740 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1741 *p1 == *p2 && *p1; p1++, p2++)
1744 if (is_substring(&p1, &p2) &&
1745 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1746 mntpnt_len = tmp;
1748 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1749 MAXPATHLEN) {
1750 if (verbose) {
1751 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1752 ml->mntl_mnt->mnt_special, p2,
1753 MAXPATHLEN);
1755 if (retcode)
1756 sharefree(retcode);
1757 retcode = NULL;
1758 goto done;
1761 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1762 (void) strcat(tmp_path, p2);
1763 if (retcode)
1764 sharefree(retcode);
1765 retcode = findentry(tmp_path);
1769 if (retcode) {
1770 assert(strlen(tmp_path) > 0);
1771 (void) strcpy(rpath, tmp_path);
1774 done:
1775 fsfreemntlist(mntl);
1776 return (retcode);
1780 * Determine whether an access list grants rights to a particular host.
1781 * We match on aliases of the hostname as well as on the canonical name.
1782 * Names in the access list may be either hosts or netgroups; they're
1783 * not distinguished syntactically. We check for hosts first because
1784 * it's cheaper, then try netgroups.
1786 * Return values:
1787 * 1 - access is granted
1788 * 0 - access is denied
1789 * -1 - an error occured
1792 in_access_list(struct cln *cln,
1793 char *access_list) /* N.B. we clobber this "input" parameter */
1795 char addr[INET_ADDRSTRLEN];
1796 char buff[256];
1797 int nentries = 0;
1798 char *cstr = access_list;
1799 char *gr = access_list;
1800 int i;
1801 int response;
1802 int ret;
1803 struct netbuf *pnb;
1804 struct nd_hostservlist *clnames = NULL;
1806 /* If no access list - then it's unrestricted */
1807 if (access_list == NULL || *access_list == '\0')
1808 return (1);
1810 if ((pnb = cln_getnbuf(cln)) == NULL)
1811 return (-1);
1813 for (;;) {
1814 if ((cstr = strpbrk(cstr, "[:")) != NULL) {
1815 if (*cstr == ':') {
1816 *cstr = '\0';
1817 } else {
1818 assert(*cstr == '[');
1819 cstr = strchr(cstr + 1, ']');
1820 if (cstr == NULL)
1821 return (-1);
1822 cstr++;
1823 continue;
1828 * If the list name has a '-' prepended then a match of
1829 * the following name implies failure instead of success.
1831 if (*gr == '-') {
1832 response = 0;
1833 gr++;
1834 } else {
1835 response = 1;
1839 * First check if we have '@' entry, as it doesn't
1840 * require client hostname.
1842 if (*gr == '@') {
1843 gr++;
1845 /* Netname support */
1846 if (!isdigit(*gr) && *gr != '[') {
1847 struct netent n, *np;
1849 if ((np = getnetbyname_r(gr, &n, buff,
1850 sizeof (buff))) != NULL &&
1851 np->n_net != 0) {
1852 while ((np->n_net & 0xFF000000u) == 0)
1853 np->n_net <<= 8;
1854 np->n_net = htonl(np->n_net);
1855 if (inet_ntop(AF_INET, &np->n_net, addr,
1856 INET_ADDRSTRLEN) == NULL)
1857 break;
1858 ret = inet_matchaddr(pnb->buf, addr);
1859 if (ret == -1) {
1860 if (errno == EINVAL) {
1861 syslog(LOG_WARNING,
1862 "invalid access "
1863 "list entry: %s",
1864 addr);
1866 return (-1);
1867 } else if (ret == 1) {
1868 return (response);
1871 } else {
1872 ret = inet_matchaddr(pnb->buf, gr);
1873 if (ret == -1) {
1874 if (errno == EINVAL) {
1875 syslog(LOG_WARNING,
1876 "invalid access list "
1877 "entry: %s", gr);
1879 return (-1);
1880 } else if (ret == 1) {
1881 return (response);
1885 goto next;
1889 * No other checks can be performed if client address
1890 * can't be resolved.
1892 if ((clnames = cln_getclientsnames(cln)) == NULL)
1893 goto next;
1895 /* Otherwise loop through all client hostname aliases */
1896 for (i = 0; i < clnames->h_cnt; i++) {
1897 char *host = clnames->h_hostservs[i].h_host;
1900 * If the list name begins with a dot then
1901 * do a domain name suffix comparison.
1902 * A single dot matches any name with no
1903 * suffix.
1905 if (*gr == '.') {
1906 if (*(gr + 1) == '\0') { /* single dot */
1907 if (strchr(host, '.') == NULL)
1908 return (response);
1909 } else {
1910 int off = strlen(host) - strlen(gr);
1911 if (off > 0 &&
1912 strcasecmp(host + off, gr) == 0) {
1913 return (response);
1916 } else {
1917 /* Just do a hostname match */
1918 if (strcasecmp(gr, host) == 0)
1919 return (response);
1923 nentries++;
1925 next:
1926 if (cstr == NULL)
1927 break;
1929 gr = ++cstr;
1932 if (clnames == NULL)
1933 return (0);
1935 return (netgroup_check(clnames, access_list, nentries));
1939 static char *optlist[] = {
1940 #define OPT_RO 0
1941 SHOPT_RO,
1942 #define OPT_RW 1
1943 SHOPT_RW,
1944 #define OPT_ROOT 2
1945 SHOPT_ROOT,
1946 #define OPT_SECURE 3
1947 SHOPT_SECURE,
1948 #define OPT_ANON 4
1949 SHOPT_ANON,
1950 #define OPT_WINDOW 5
1951 SHOPT_WINDOW,
1952 #define OPT_NOSUID 6
1953 SHOPT_NOSUID,
1954 #define OPT_ACLOK 7
1955 SHOPT_ACLOK,
1956 #define OPT_SEC 8
1957 SHOPT_SEC,
1958 #define OPT_NONE 9
1959 SHOPT_NONE,
1960 #define OPT_UIDMAP 10
1961 SHOPT_UIDMAP,
1962 #define OPT_GIDMAP 11
1963 SHOPT_GIDMAP,
1964 NULL
1967 static int
1968 map_flavor(char *str)
1970 seconfig_t sec;
1972 if (nfs_getseconfig_byname(str, &sec))
1973 return (-1);
1975 return (sec.sc_nfsnum);
1979 * If the option string contains a "sec="
1980 * option, then use new option syntax.
1982 static int
1983 newopts(char *opts)
1985 char *head, *p, *val;
1987 if (!opts || *opts == '\0')
1988 return (0);
1990 head = strdup(opts);
1991 if (head == NULL) {
1992 syslog(LOG_ERR, "opts: no memory");
1993 return (0);
1996 p = head;
1997 while (*p) {
1998 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
1999 free(head);
2000 return (1);
2004 free(head);
2005 return (0);
2009 * Given an export and the clients hostname(s)
2010 * determine the security flavors that this
2011 * client is permitted to use.
2013 * This routine is called only for "old" syntax, i.e.
2014 * only one security flavor is allowed. So we need
2015 * to determine two things: the particular flavor,
2016 * and whether the client is allowed to use this
2017 * flavor, i.e. is in the access list.
2019 * Note that if there is no access list, then the
2020 * default is that access is granted.
2022 static int
2023 getclientsflavors_old(share_t *sh, struct cln *cln, int *flavors)
2025 char *opts, *p, *val;
2026 boolean_t ok = B_FALSE;
2027 int defaultaccess = 1;
2028 boolean_t reject = B_FALSE;
2030 opts = strdup(sh->sh_opts);
2031 if (opts == NULL) {
2032 syslog(LOG_ERR, "getclientsflavors: no memory");
2033 return (0);
2036 flavors[0] = AUTH_SYS;
2037 p = opts;
2039 while (*p) {
2041 switch (getsubopt(&p, optlist, &val)) {
2042 case OPT_SECURE:
2043 flavors[0] = AUTH_DES;
2044 break;
2046 case OPT_RO:
2047 case OPT_RW:
2048 defaultaccess = 0;
2049 if (in_access_list(cln, val) > 0)
2050 ok = B_TRUE;
2051 break;
2053 case OPT_NONE:
2054 defaultaccess = 0;
2055 if (in_access_list(cln, val) > 0)
2056 reject = B_TRUE;
2060 free(opts);
2062 /* none takes precedence over everything else */
2063 if (reject)
2064 ok = B_FALSE;
2066 return (defaultaccess || ok);
2070 * Given an export and the clients hostname(s)
2071 * determine the security flavors that this
2072 * client is permitted to use.
2074 * This is somewhat more complicated than the "old"
2075 * routine because the options may contain multiple
2076 * security flavors (sec=) each with its own access
2077 * lists. So a client could be granted access based
2078 * on a number of security flavors. Note that the
2079 * type of access might not always be the same, the
2080 * client may get readonly access with one flavor
2081 * and readwrite with another, however the client
2082 * is not told this detail, it gets only the list
2083 * of flavors, and only if the client is using
2084 * version 3 of the mount protocol.
2086 static int
2087 getclientsflavors_new(share_t *sh, struct cln *cln, int *flavors)
2089 char *opts, *p, *val;
2090 char *lasts;
2091 char *f;
2092 boolean_t defaultaccess = B_TRUE; /* default access is rw */
2093 boolean_t access_ok = B_FALSE;
2094 int count, c;
2095 boolean_t reject = B_FALSE;
2097 opts = strdup(sh->sh_opts);
2098 if (opts == NULL) {
2099 syslog(LOG_ERR, "getclientsflavors: no memory");
2100 return (0);
2103 p = opts;
2104 count = c = 0;
2106 while (*p) {
2107 switch (getsubopt(&p, optlist, &val)) {
2108 case OPT_SEC:
2109 if (reject)
2110 access_ok = B_FALSE;
2113 * Before a new sec=xxx option, check if we need
2114 * to move the c index back to the previous count.
2116 if (!defaultaccess && !access_ok) {
2117 c = count;
2120 /* get all the sec=f1[:f2] flavors */
2121 while ((f = strtok_r(val, ":", &lasts)) != NULL) {
2122 flavors[c++] = map_flavor(f);
2123 val = NULL;
2126 /* for a new sec=xxx option, default is rw access */
2127 defaultaccess = B_TRUE;
2128 access_ok = B_FALSE;
2129 reject = B_FALSE;
2130 break;
2132 case OPT_RO:
2133 case OPT_RW:
2134 defaultaccess = B_FALSE;
2135 if (in_access_list(cln, val) > 0)
2136 access_ok = B_TRUE;
2137 break;
2139 case OPT_NONE:
2140 defaultaccess = B_FALSE;
2141 if (in_access_list(cln, val) > 0)
2142 reject = B_TRUE; /* none overides rw/ro */
2143 break;
2147 if (reject)
2148 access_ok = B_FALSE;
2150 if (!defaultaccess && !access_ok)
2151 c = count;
2153 free(opts);
2155 return (c);
2159 * This is a tricky piece of code that parses the
2160 * share options looking for a match on the auth
2161 * flavor that the client is using. If it finds
2162 * a match, then the client is given ro, rw, or
2163 * no access depending whether it is in the access
2164 * list. There is a special case for "secure"
2165 * flavor. Other flavors are values of the new "sec=" option.
2168 check_client(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2169 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2170 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2172 if (newopts(sh->sh_opts))
2173 return (check_client_new(sh, cln, flavor, clnt_uid, clnt_gid,
2174 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2175 srv_gids));
2176 else
2177 return (check_client_old(sh, cln, flavor, clnt_uid, clnt_gid,
2178 clnt_ngids, clnt_gids, srv_uid, srv_gid, srv_ngids,
2179 srv_gids));
2182 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2185 * Get supplemental groups for uid
2187 static int
2188 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2190 struct passwd pwd;
2191 char *pwbuf = alloca(pw_size);
2192 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2193 int tmpngrps;
2195 if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
2196 return (-1);
2198 tmpgrps[0] = pwd.pw_gid;
2200 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2201 if (tmpngrps <= 0) {
2202 syslog(LOG_WARNING,
2203 "getusergroups(): Unable to get groups for user %s",
2204 pwd.pw_name);
2206 return (-1);
2209 *grps = malloc(tmpngrps * sizeof (gid_t));
2210 if (*grps == NULL) {
2211 syslog(LOG_ERR,
2212 "getusergroups(): Memory allocation failed: %m");
2214 return (-1);
2217 *ngrps = tmpngrps;
2218 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2220 return (0);
2224 * is_a_number(number)
2226 * is the string a number in one of the forms we want to use?
2229 static int
2230 is_a_number(char *number)
2232 int ret = 1;
2233 int hex = 0;
2235 if (strncmp(number, "0x", 2) == 0) {
2236 number += 2;
2237 hex = 1;
2238 } else if (*number == '-') {
2239 number++; /* skip the minus */
2241 while (ret == 1 && *number != '\0') {
2242 if (hex) {
2243 ret = isxdigit(*number++);
2244 } else {
2245 ret = isdigit(*number++);
2248 return (ret);
2251 static boolean_t
2252 get_uid(char *value, uid_t *uid)
2254 if (!is_a_number(value)) {
2255 struct passwd *pw;
2257 * in this case it would have to be a
2258 * user name
2260 pw = getpwnam(value);
2261 if (pw == NULL)
2262 return (B_FALSE);
2263 *uid = pw->pw_uid;
2264 endpwent();
2265 } else {
2266 uint64_t intval;
2267 intval = strtoull(value, NULL, 0);
2268 if (intval > UID_MAX && intval != -1)
2269 return (B_FALSE);
2270 *uid = (uid_t)intval;
2273 return (B_TRUE);
2276 static boolean_t
2277 get_gid(char *value, gid_t *gid)
2279 if (!is_a_number(value)) {
2280 struct group *gr;
2282 * in this case it would have to be a
2283 * group name
2285 gr = getgrnam(value);
2286 if (gr == NULL)
2287 return (B_FALSE);
2288 *gid = gr->gr_gid;
2289 endgrent();
2290 } else {
2291 uint64_t intval;
2292 intval = strtoull(value, NULL, 0);
2293 if (intval > UID_MAX && intval != -1)
2294 return (B_FALSE);
2295 *gid = (gid_t)intval;
2298 return (B_TRUE);
2301 static int
2302 check_client_old(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2303 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2304 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2306 char *opts, *p, *val;
2307 int match; /* Set when a flavor is matched */
2308 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2309 int list = 0; /* Set when "ro", "rw" is found */
2310 int ro_val = 0; /* Set if ro option is 'ro=' */
2311 int rw_val = 0; /* Set if rw option is 'rw=' */
2313 boolean_t map_deny = B_FALSE;
2315 opts = strdup(sh->sh_opts);
2316 if (opts == NULL) {
2317 syslog(LOG_ERR, "check_client: no memory");
2318 return (0);
2322 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2323 * locally for all of them
2325 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2326 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2327 perm |= NFSAUTH_GROUPS;
2329 p = opts;
2330 match = AUTH_UNIX;
2332 while (*p) {
2333 switch (getsubopt(&p, optlist, &val)) {
2335 case OPT_SECURE:
2336 match = AUTH_DES;
2338 if (perm & NFSAUTH_GROUPS) {
2339 free(*srv_gids);
2340 *srv_ngids = 0;
2341 *srv_gids = NULL;
2342 perm &= ~NFSAUTH_GROUPS;
2345 break;
2347 case OPT_RO:
2348 list++;
2349 if (val != NULL)
2350 ro_val++;
2351 if (in_access_list(cln, val) > 0)
2352 perm |= NFSAUTH_RO;
2353 break;
2355 case OPT_RW:
2356 list++;
2357 if (val != NULL)
2358 rw_val++;
2359 if (in_access_list(cln, val) > 0)
2360 perm |= NFSAUTH_RW;
2361 break;
2363 case OPT_ROOT:
2365 * Check if the client is in
2366 * the root list. Only valid
2367 * for AUTH_SYS.
2369 if (flavor != AUTH_SYS)
2370 break;
2372 if (val == NULL || *val == '\0')
2373 break;
2375 if (clnt_uid != 0)
2376 break;
2378 if (in_access_list(cln, val) > 0) {
2379 perm |= NFSAUTH_ROOT;
2380 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2381 map_deny = B_FALSE;
2383 if (perm & NFSAUTH_GROUPS) {
2384 free(*srv_gids);
2385 *srv_ngids = 0;
2386 *srv_gids = NULL;
2387 perm &= ~NFSAUTH_GROUPS;
2390 break;
2392 case OPT_NONE:
2394 * Check if the client should have no access
2395 * to this share at all. This option behaves
2396 * more like "root" than either "rw" or "ro".
2398 if (in_access_list(cln, val) > 0)
2399 perm |= NFSAUTH_DENIED;
2400 break;
2402 case OPT_UIDMAP: {
2403 char *c;
2404 char *n;
2407 * The uidmap is supported for AUTH_SYS only.
2409 if (flavor != AUTH_SYS)
2410 break;
2412 if (perm & NFSAUTH_UIDMAP || map_deny)
2413 break;
2415 for (c = val; c != NULL; c = n) {
2416 char *s;
2417 char *al;
2418 uid_t srv;
2420 n = strchr(c, '~');
2421 if (n != NULL)
2422 *n++ = '\0';
2424 s = strchr(c, ':');
2425 if (s != NULL) {
2426 *s++ = '\0';
2427 al = strchr(s, ':');
2428 if (al != NULL)
2429 *al++ = '\0';
2432 if (s == NULL || al == NULL)
2433 continue;
2435 if (*c == '\0') {
2436 if (clnt_uid != (uid_t)-1)
2437 continue;
2438 } else if (strcmp(c, "*") != 0) {
2439 uid_t clnt;
2441 if (!get_uid(c, &clnt))
2442 continue;
2444 if (clnt_uid != clnt)
2445 continue;
2448 if (*s == '\0')
2449 srv = UID_NOBODY;
2450 else if (!get_uid(s, &srv))
2451 continue;
2452 else if (srv == (uid_t)-1) {
2453 map_deny = B_TRUE;
2454 break;
2457 if (in_access_list(cln, al) > 0) {
2458 *srv_uid = srv;
2459 perm |= NFSAUTH_UIDMAP;
2461 if (perm & NFSAUTH_GROUPS) {
2462 free(*srv_gids);
2463 *srv_ngids = 0;
2464 *srv_gids = NULL;
2465 perm &= ~NFSAUTH_GROUPS;
2468 break;
2472 break;
2475 case OPT_GIDMAP: {
2476 char *c;
2477 char *n;
2480 * The gidmap is supported for AUTH_SYS only.
2482 if (flavor != AUTH_SYS)
2483 break;
2485 if (perm & NFSAUTH_GIDMAP || map_deny)
2486 break;
2488 for (c = val; c != NULL; c = n) {
2489 char *s;
2490 char *al;
2491 gid_t srv;
2493 n = strchr(c, '~');
2494 if (n != NULL)
2495 *n++ = '\0';
2497 s = strchr(c, ':');
2498 if (s != NULL) {
2499 *s++ = '\0';
2500 al = strchr(s, ':');
2501 if (al != NULL)
2502 *al++ = '\0';
2505 if (s == NULL || al == NULL)
2506 break;
2508 if (*c == '\0') {
2509 if (clnt_gid != (gid_t)-1)
2510 continue;
2511 } else if (strcmp(c, "*") != 0) {
2512 gid_t clnt;
2514 if (!get_gid(c, &clnt))
2515 continue;
2517 if (clnt_gid != clnt)
2518 continue;
2521 if (*s == '\0')
2522 srv = UID_NOBODY;
2523 else if (!get_gid(s, &srv))
2524 continue;
2525 else if (srv == (gid_t)-1) {
2526 map_deny = B_TRUE;
2527 break;
2530 if (in_access_list(cln, al) > 0) {
2531 *srv_gid = srv;
2532 perm |= NFSAUTH_GIDMAP;
2534 if (perm & NFSAUTH_GROUPS) {
2535 free(*srv_gids);
2536 *srv_ngids = 0;
2537 *srv_gids = NULL;
2538 perm &= ~NFSAUTH_GROUPS;
2541 break;
2545 break;
2548 default:
2549 break;
2553 free(opts);
2555 if (perm & NFSAUTH_ROOT) {
2556 *srv_uid = 0;
2557 *srv_gid = 0;
2560 if (map_deny)
2561 perm |= NFSAUTH_DENIED;
2563 if (!(perm & NFSAUTH_UIDMAP))
2564 *srv_uid = clnt_uid;
2565 if (!(perm & NFSAUTH_GIDMAP))
2566 *srv_gid = clnt_gid;
2568 if (flavor != match || perm & NFSAUTH_DENIED)
2569 return (NFSAUTH_DENIED);
2571 if (list) {
2573 * If the client doesn't match an "ro" or "rw"
2574 * list then set no access.
2576 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2577 perm |= NFSAUTH_DENIED;
2578 } else {
2580 * The client matched a flavor entry that
2581 * has no explicit "rw" or "ro" determination.
2582 * Default it to "rw".
2584 perm |= NFSAUTH_RW;
2588 * The client may show up in both ro= and rw=
2589 * lists. If so, then turn off the RO access
2590 * bit leaving RW access.
2592 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2594 * Logically cover all permutations of rw=,ro=.
2595 * In the case where, rw,ro=<host> we would like
2596 * to remove RW access for the host. In all other cases
2597 * RW wins the precedence battle.
2599 if (!rw_val && ro_val) {
2600 perm &= ~(NFSAUTH_RW);
2601 } else {
2602 perm &= ~(NFSAUTH_RO);
2606 return (perm);
2610 * Check if the client has access by using a flavor different from
2611 * the given "flavor". If "flavor" is not in the flavor list,
2612 * return TRUE to indicate that this "flavor" is a wrong sec.
2614 static bool_t
2615 is_wrongsec(share_t *sh, struct cln *cln, int flavor)
2617 int flavor_list[MAX_FLAVORS];
2618 int flavor_count, i;
2620 /* get the flavor list that the client has access with */
2621 flavor_count = getclientsflavors_new(sh, cln, flavor_list);
2623 if (flavor_count == 0)
2624 return (FALSE);
2627 * Check if the given "flavor" is in the flavor_list.
2629 for (i = 0; i < flavor_count; i++) {
2630 if (flavor == flavor_list[i])
2631 return (FALSE);
2635 * If "flavor" is not in the flavor_list, return TRUE to indicate
2636 * that the client should have access by using a security flavor
2637 * different from this "flavor".
2639 return (TRUE);
2643 * Given an export and the client's hostname, we
2644 * check the security options to see whether the
2645 * client is allowed to use the given security flavor.
2647 * The strategy is to proceed through the options looking
2648 * for a flavor match, then pay attention to the ro, rw,
2649 * and root options.
2651 * Note that an entry may list several flavors in a
2652 * single entry, e.g.
2654 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2658 static int
2659 check_client_new(share_t *sh, struct cln *cln, int flavor, uid_t clnt_uid,
2660 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2661 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2663 char *opts, *p, *val;
2664 char *lasts;
2665 char *f;
2666 int match = 0; /* Set when a flavor is matched */
2667 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2668 int list = 0; /* Set when "ro", "rw" is found */
2669 int ro_val = 0; /* Set if ro option is 'ro=' */
2670 int rw_val = 0; /* Set if rw option is 'rw=' */
2672 boolean_t map_deny = B_FALSE;
2674 opts = strdup(sh->sh_opts);
2675 if (opts == NULL) {
2676 syslog(LOG_ERR, "check_client: no memory");
2677 return (0);
2681 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2682 * locally for all of them
2684 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2685 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2686 perm |= NFSAUTH_GROUPS;
2688 p = opts;
2690 while (*p) {
2691 switch (getsubopt(&p, optlist, &val)) {
2693 case OPT_SEC:
2694 if (match)
2695 goto done;
2697 while ((f = strtok_r(val, ":", &lasts))
2698 != NULL) {
2699 if (flavor == map_flavor(f)) {
2700 match = 1;
2701 break;
2703 val = NULL;
2705 break;
2707 case OPT_RO:
2708 if (!match)
2709 break;
2711 list++;
2712 if (val != NULL)
2713 ro_val++;
2714 if (in_access_list(cln, val) > 0)
2715 perm |= NFSAUTH_RO;
2716 break;
2718 case OPT_RW:
2719 if (!match)
2720 break;
2722 list++;
2723 if (val != NULL)
2724 rw_val++;
2725 if (in_access_list(cln, val) > 0)
2726 perm |= NFSAUTH_RW;
2727 break;
2729 case OPT_ROOT:
2731 * Check if the client is in
2732 * the root list. Only valid
2733 * for AUTH_SYS.
2735 if (flavor != AUTH_SYS)
2736 break;
2738 if (!match)
2739 break;
2741 if (val == NULL || *val == '\0')
2742 break;
2744 if (clnt_uid != 0)
2745 break;
2747 if (in_access_list(cln, val) > 0) {
2748 perm |= NFSAUTH_ROOT;
2749 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2750 map_deny = B_FALSE;
2752 if (perm & NFSAUTH_GROUPS) {
2753 free(*srv_gids);
2754 *srv_gids = NULL;
2755 *srv_ngids = 0;
2756 perm &= ~NFSAUTH_GROUPS;
2759 break;
2761 case OPT_NONE:
2763 * Check if the client should have no access
2764 * to this share at all. This option behaves
2765 * more like "root" than either "rw" or "ro".
2767 if (in_access_list(cln, val) > 0)
2768 perm |= NFSAUTH_DENIED;
2769 break;
2771 case OPT_UIDMAP: {
2772 char *c;
2773 char *n;
2776 * The uidmap is supported for AUTH_SYS only.
2778 if (flavor != AUTH_SYS)
2779 break;
2781 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2782 break;
2784 for (c = val; c != NULL; c = n) {
2785 char *s;
2786 char *al;
2787 uid_t srv;
2789 n = strchr(c, '~');
2790 if (n != NULL)
2791 *n++ = '\0';
2793 s = strchr(c, ':');
2794 if (s != NULL) {
2795 *s++ = '\0';
2796 al = strchr(s, ':');
2797 if (al != NULL)
2798 *al++ = '\0';
2801 if (s == NULL || al == NULL)
2802 continue;
2804 if (*c == '\0') {
2805 if (clnt_uid != (uid_t)-1)
2806 continue;
2807 } else if (strcmp(c, "*") != 0) {
2808 uid_t clnt;
2810 if (!get_uid(c, &clnt))
2811 continue;
2813 if (clnt_uid != clnt)
2814 continue;
2817 if (*s == '\0')
2818 srv = UID_NOBODY;
2819 else if (!get_uid(s, &srv))
2820 continue;
2821 else if (srv == (uid_t)-1) {
2822 map_deny = B_TRUE;
2823 break;
2826 if (in_access_list(cln, al) > 0) {
2827 *srv_uid = srv;
2828 perm |= NFSAUTH_UIDMAP;
2830 if (perm & NFSAUTH_GROUPS) {
2831 free(*srv_gids);
2832 *srv_gids = NULL;
2833 *srv_ngids = 0;
2834 perm &= ~NFSAUTH_GROUPS;
2837 break;
2841 break;
2844 case OPT_GIDMAP: {
2845 char *c;
2846 char *n;
2849 * The gidmap is supported for AUTH_SYS only.
2851 if (flavor != AUTH_SYS)
2852 break;
2854 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2855 break;
2857 for (c = val; c != NULL; c = n) {
2858 char *s;
2859 char *al;
2860 gid_t srv;
2862 n = strchr(c, '~');
2863 if (n != NULL)
2864 *n++ = '\0';
2866 s = strchr(c, ':');
2867 if (s != NULL) {
2868 *s++ = '\0';
2869 al = strchr(s, ':');
2870 if (al != NULL)
2871 *al++ = '\0';
2874 if (s == NULL || al == NULL)
2875 break;
2877 if (*c == '\0') {
2878 if (clnt_gid != (gid_t)-1)
2879 continue;
2880 } else if (strcmp(c, "*") != 0) {
2881 gid_t clnt;
2883 if (!get_gid(c, &clnt))
2884 continue;
2886 if (clnt_gid != clnt)
2887 continue;
2890 if (*s == '\0')
2891 srv = UID_NOBODY;
2892 else if (!get_gid(s, &srv))
2893 continue;
2894 else if (srv == (gid_t)-1) {
2895 map_deny = B_TRUE;
2896 break;
2899 if (in_access_list(cln, al) > 0) {
2900 *srv_gid = srv;
2901 perm |= NFSAUTH_GIDMAP;
2903 if (perm & NFSAUTH_GROUPS) {
2904 free(*srv_gids);
2905 *srv_gids = NULL;
2906 *srv_ngids = 0;
2907 perm &= ~NFSAUTH_GROUPS;
2910 break;
2914 break;
2917 default:
2918 break;
2922 done:
2923 if (perm & NFSAUTH_ROOT) {
2924 *srv_uid = 0;
2925 *srv_gid = 0;
2928 if (map_deny)
2929 perm |= NFSAUTH_DENIED;
2931 if (!(perm & NFSAUTH_UIDMAP))
2932 *srv_uid = clnt_uid;
2933 if (!(perm & NFSAUTH_GIDMAP))
2934 *srv_gid = clnt_gid;
2937 * If no match then set the perm accordingly
2939 if (!match || perm & NFSAUTH_DENIED) {
2940 free(opts);
2941 return (NFSAUTH_DENIED);
2944 if (list) {
2946 * If the client doesn't match an "ro" or "rw" list then
2947 * check if it may have access by using a different flavor.
2948 * If so, return NFSAUTH_WRONGSEC.
2949 * If not, return NFSAUTH_DENIED.
2951 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2952 if (is_wrongsec(sh, cln, flavor))
2953 perm |= NFSAUTH_WRONGSEC;
2954 else
2955 perm |= NFSAUTH_DENIED;
2957 } else {
2959 * The client matched a flavor entry that
2960 * has no explicit "rw" or "ro" determination.
2961 * Make sure it defaults to "rw".
2963 perm |= NFSAUTH_RW;
2967 * The client may show up in both ro= and rw=
2968 * lists. If so, then turn off the RO access
2969 * bit leaving RW access.
2971 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2973 * Logically cover all permutations of rw=,ro=.
2974 * In the case where, rw,ro=<host> we would like
2975 * to remove RW access for the host. In all other cases
2976 * RW wins the precedence battle.
2978 if (!rw_val && ro_val) {
2979 perm &= ~(NFSAUTH_RW);
2980 } else {
2981 perm &= ~(NFSAUTH_RO);
2985 free(opts);
2987 return (perm);
2990 void
2991 check_sharetab()
2993 FILE *f;
2994 struct stat st;
2995 static timestruc_t last_sharetab_time;
2996 timestruc_t prev_sharetab_time;
2997 share_t *sh;
2998 struct sh_list *shp, *shp_prev;
2999 int res, c = 0;
3002 * read in /etc/dfs/sharetab if it has changed
3004 if (stat(SHARETAB, &st) != 0) {
3005 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3006 return;
3009 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
3010 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
3012 * No change.
3014 return;
3018 * Remember the mod time, then after getting the
3019 * write lock check again. If another thread
3020 * already did the update, then there's no
3021 * work to do.
3023 prev_sharetab_time = last_sharetab_time;
3025 (void) rw_wrlock(&sharetab_lock);
3027 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3028 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3029 (void) rw_unlock(&sharetab_lock);
3030 return;
3034 * Note that since the sharetab is now in memory
3035 * and a snapshot is taken, we no longer have to
3036 * lock the file.
3038 f = fopen(SHARETAB, "r");
3039 if (f == NULL) {
3040 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3041 (void) rw_unlock(&sharetab_lock);
3042 return;
3046 * Once we are sure /etc/dfs/sharetab has been
3047 * modified, flush netgroup cache entries.
3049 netgrp_cache_flush();
3051 sh_free(share_list); /* free old list */
3052 share_list = NULL;
3054 while ((res = getshare(f, &sh)) > 0) {
3055 c++;
3056 if (strcmp(sh->sh_fstype, "nfs") != 0)
3057 continue;
3059 shp = malloc(sizeof (*shp));
3060 if (shp == NULL)
3061 goto alloc_failed;
3062 if (share_list == NULL)
3063 share_list = shp;
3064 else
3065 /* LINTED not used before set */
3066 shp_prev->shl_next = shp;
3067 shp_prev = shp;
3068 shp->shl_next = NULL;
3069 shp->shl_sh = sharedup(sh);
3070 if (shp->shl_sh == NULL)
3071 goto alloc_failed;
3074 if (res < 0)
3075 syslog(LOG_ERR, "%s: invalid at line %d\n",
3076 SHARETAB, c + 1);
3078 if (stat(SHARETAB, &st) != 0) {
3079 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3080 (void) fclose(f);
3081 (void) rw_unlock(&sharetab_lock);
3082 return;
3085 last_sharetab_time = st.st_mtim;
3086 (void) fclose(f);
3087 (void) rw_unlock(&sharetab_lock);
3089 return;
3091 alloc_failed:
3093 syslog(LOG_ERR, "check_sharetab: no memory");
3094 sh_free(share_list);
3095 share_list = NULL;
3096 (void) fclose(f);
3097 (void) rw_unlock(&sharetab_lock);
3100 static void
3101 sh_free(struct sh_list *shp)
3103 struct sh_list *next;
3105 while (shp) {
3106 sharefree(shp->shl_sh);
3107 next = shp->shl_next;
3108 free(shp);
3109 shp = next;
3115 * Remove an entry from mounted list
3117 static void
3118 umount(struct svc_req *rqstp)
3120 char *host, *path, *remove_path;
3121 char rpath[MAXPATHLEN];
3122 SVCXPRT *transp;
3123 struct cln cln;
3125 transp = rqstp->rq_xprt;
3126 path = NULL;
3127 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3128 svcerr_decode(transp);
3129 return;
3132 cln_init(&cln, transp);
3134 errno = 0;
3135 if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3136 log_cant_reply_cln(&cln);
3138 host = cln_gethost(&cln);
3139 if (host == NULL) {
3141 * Without the hostname we can't do audit or delete
3142 * this host from the mount entries.
3144 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3145 return;
3148 if (verbose)
3149 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3151 audit_mountd_umount(host, path);
3153 remove_path = rpath; /* assume we will use the cannonical path */
3154 if (realpath(path, rpath) == NULL) {
3155 if (verbose)
3156 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3157 remove_path = path; /* use path provided instead */
3160 mntlist_delete(host, remove_path); /* remove from mount list */
3162 cln_fini(&cln);
3164 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3168 * Remove all entries for one machine from mounted list
3170 static void
3171 umountall(struct svc_req *rqstp)
3173 SVCXPRT *transp;
3174 char *host;
3175 struct cln cln;
3177 transp = rqstp->rq_xprt;
3178 if (!svc_getargs(transp, xdr_void, NULL)) {
3179 svcerr_decode(transp);
3180 return;
3183 * We assume that this call is asynchronous and made via rpcbind
3184 * callit routine. Therefore return control immediately. The error
3185 * causes rpcbind to remain silent, as opposed to every machine
3186 * on the net blasting the requester with a response.
3188 svcerr_systemerr(transp);
3190 cln_init(&cln, transp);
3192 host = cln_gethost(&cln);
3193 if (host == NULL) {
3194 /* Can't do anything without the name of the client */
3195 return;
3199 * Remove all hosts entries from mount list
3201 mntlist_delete_all(host);
3203 if (verbose)
3204 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3206 cln_fini(&cln);
3209 void *
3210 exmalloc(size_t size)
3212 void *ret;
3214 if ((ret = malloc(size)) == NULL) {
3215 syslog(LOG_ERR, "Out of memory");
3216 exit(1);
3218 return (ret);
3221 static tsol_tpent_t *
3222 get_client_template(struct sockaddr *sock)
3224 in_addr_t v4client;
3225 in6_addr_t v6client;
3226 char v4_addr[INET_ADDRSTRLEN];
3227 char v6_addr[INET6_ADDRSTRLEN];
3228 tsol_rhent_t *rh;
3229 tsol_tpent_t *tp;
3231 switch (sock->sa_family) {
3232 case AF_INET:
3233 v4client = ((struct sockaddr_in *)(void *)sock)->
3234 sin_addr.s_addr;
3235 if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
3236 NULL)
3237 return (NULL);
3238 rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
3239 if (rh == NULL)
3240 return (NULL);
3241 tp = tsol_gettpbyname(rh->rh_template);
3242 tsol_freerhent(rh);
3243 return (tp);
3244 break;
3245 case AF_INET6:
3246 v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
3247 if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
3248 NULL)
3249 return (NULL);
3250 rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
3251 if (rh == NULL)
3252 return (NULL);
3253 tp = tsol_gettpbyname(rh->rh_template);
3254 tsol_freerhent(rh);
3255 return (tp);
3256 break;
3257 default:
3258 return (NULL);