5296 Support for more than 16 groups with AUTH_SYS
[illumos-gate.git] / usr / src / cmd / fs.d / nfs / mountd / mountd.c
blob0d9ac76d30569f7c4eefc82d3da40eae1fa72cf9
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 2014 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 fd);
94 struct sh_list *share_list;
96 rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
97 static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
99 static mutex_t logging_queue_lock;
100 static cond_t logging_queue_cv;
102 static share_t *find_lofsentry(char *, int *);
103 static int getclientsnames_lazy(char *, struct netbuf **,
104 struct nd_hostservlist **);
105 static int getclientsnames(SVCXPRT *, struct netbuf **,
106 struct nd_hostservlist **);
107 static int getclientsflavors_old(share_t *, SVCXPRT *, struct netbuf **,
108 struct nd_hostservlist **, int *);
109 static int getclientsflavors_new(share_t *, SVCXPRT *, struct netbuf **,
110 struct nd_hostservlist **, int *);
111 static int check_client_old(share_t *, SVCXPRT *, struct netbuf **,
112 struct nd_hostservlist **, int, uid_t, gid_t, uint_t, gid_t *, uid_t *,
113 gid_t *, uint_t *, gid_t **);
114 static int check_client_new(share_t *, SVCXPRT *, struct netbuf **,
115 struct nd_hostservlist **, int, uid_t, gid_t, uint_t, gid_t *, uid_t *,
116 gid_t *i, uint_t *, gid_t **);
117 static void mnt(struct svc_req *, SVCXPRT *);
118 static void mnt_pathconf(struct svc_req *);
119 static int mount(struct svc_req *r);
120 static void sh_free(struct sh_list *);
121 static void umount(struct svc_req *);
122 static void umountall(struct svc_req *);
123 static void sigexit(int);
124 static int newopts(char *);
125 static tsol_tpent_t *get_client_template(struct sockaddr *);
127 static int verbose;
128 static int rejecting;
129 static int mount_vers_min = MOUNTVERS;
130 static int mount_vers_max = MOUNTVERS3;
132 /* Needs to be accessed by nfscmd.c */
133 int in_access_list(SVCXPRT *, struct netbuf **,
134 struct nd_hostservlist **, char *);
136 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
138 thread_t nfsauth_thread;
139 thread_t cmd_thread;
140 thread_t logging_thread;
142 typedef struct logging_data {
143 char *ld_host;
144 char *ld_path;
145 char *ld_rpath;
146 int ld_status;
147 char *ld_netid;
148 struct netbuf *ld_nb;
149 struct logging_data *ld_next;
150 } logging_data;
152 static logging_data *logging_head = NULL;
153 static logging_data *logging_tail = NULL;
156 * Our copy of some system variables obtained using sysconf(3c)
158 static long ngroups_max; /* _SC_NGROUPS_MAX */
159 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
161 /* ARGSUSED */
162 static void *
163 nfsauth_svc(void *arg)
165 int doorfd = -1;
166 uint_t darg;
167 #ifdef DEBUG
168 int dfd;
169 #endif
171 if ((doorfd = door_create(nfsauth_func, NULL,
172 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
173 syslog(LOG_ERR, "Unable to create door: %m\n");
174 exit(10);
177 #ifdef DEBUG
179 * Create a file system path for the door
181 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
182 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
183 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
184 (void) close(doorfd);
185 exit(11);
189 * Clean up any stale namespace associations
191 (void) fdetach(MOUNTD_DOOR);
194 * Register in namespace to pass to the kernel to door_ki_open
196 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
197 syslog(LOG_ERR, "Unable to fattach door: %m\n");
198 (void) close(dfd);
199 (void) close(doorfd);
200 exit(12);
202 (void) close(dfd);
203 #endif
206 * Must pass the doorfd down to the kernel.
208 darg = doorfd;
209 (void) _nfssys(MOUNTD_ARGS, &darg);
212 * Wait for incoming calls
214 /*CONSTCOND*/
215 for (;;)
216 (void) pause();
218 /*NOTREACHED*/
219 syslog(LOG_ERR, gettext("Door server exited"));
220 return (NULL);
224 * NFS command service thread code for setup and handling of the
225 * nfs_cmd requests for character set conversion and other future
226 * events.
229 static void *
230 cmd_svc(void *arg)
232 int doorfd = -1;
233 uint_t darg;
235 if ((doorfd = door_create(nfscmd_func, NULL,
236 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
237 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
238 exit(10);
242 * Must pass the doorfd down to the kernel.
244 darg = doorfd;
245 (void) _nfssys(NFSCMD_ARGS, &darg);
248 * Wait for incoming calls
250 /*CONSTCOND*/
251 for (;;)
252 (void) pause();
254 /*NOTREACHED*/
255 syslog(LOG_ERR, gettext("Cmd door server exited"));
256 return (NULL);
259 static void
260 free_logging_data(logging_data *lq)
262 if (lq != NULL) {
263 free(lq->ld_host);
264 free(lq->ld_netid);
266 if (lq->ld_nb != NULL) {
267 free(lq->ld_nb->buf);
268 free(lq->ld_nb);
271 free(lq->ld_path);
272 free(lq->ld_rpath);
274 free(lq);
278 static logging_data *
279 remove_head_of_queue(void)
281 logging_data *lq;
284 * Pull it off the queue.
286 lq = logging_head;
287 if (lq) {
288 logging_head = lq->ld_next;
291 * Drained it.
293 if (logging_head == NULL) {
294 logging_tail = NULL;
298 return (lq);
301 static void
302 do_logging_queue(logging_data *lq)
304 int cleared = 0;
305 char *host;
307 struct nd_hostservlist *clnames;
309 while (lq) {
310 if (lq->ld_host == NULL) {
311 DTRACE_PROBE(mountd, name_by_lazy);
312 if (getclientsnames_lazy(lq->ld_netid,
313 &lq->ld_nb, &clnames) != 0)
314 host = NULL;
315 else
316 host = clnames->h_hostservs[0].h_host;
317 } else
318 host = lq->ld_host;
320 audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
322 /* add entry to mount list */
323 if (lq->ld_rpath)
324 mntlist_new(host, lq->ld_rpath);
326 if (lq->ld_host != host)
327 netdir_free(clnames, ND_HOSTSERVLIST);
329 free_logging_data(lq);
330 cleared++;
332 (void) mutex_lock(&logging_queue_lock);
333 lq = remove_head_of_queue();
334 (void) mutex_unlock(&logging_queue_lock);
337 DTRACE_PROBE1(mountd, logging_cleared, cleared);
340 static void *
341 logging_svc(void *arg)
343 logging_data *lq;
345 for (;;) {
346 (void) mutex_lock(&logging_queue_lock);
347 while (logging_head == NULL) {
348 (void) cond_wait(&logging_queue_cv,
349 &logging_queue_lock);
352 lq = remove_head_of_queue();
353 (void) mutex_unlock(&logging_queue_lock);
355 do_logging_queue(lq);
358 /*NOTREACHED*/
359 syslog(LOG_ERR, gettext("Logging server exited"));
360 return (NULL);
363 static int
364 convert_int(int *val, char *str)
366 long lval;
368 if (str == NULL || !isdigit(*str))
369 return (-1);
371 lval = strtol(str, &str, 10);
372 if (*str != '\0' || lval > INT_MAX)
373 return (-2);
375 *val = (int)lval;
376 return (0);
380 main(int argc, char *argv[])
382 int pid;
383 int c;
384 int rpc_svc_fdunlim = 1;
385 int rpc_svc_mode = RPC_SVC_MT_AUTO;
386 int maxrecsz = RPC_MAXDATASIZE;
387 bool_t exclbind = TRUE;
388 bool_t can_do_mlp;
389 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
390 char defval[4];
391 int defvers, ret, bufsz;
392 struct rlimit rl;
393 int listen_backlog = 0;
394 int max_threads = 0;
395 int tmp;
397 int pipe_fd = -1;
400 * Mountd requires uid 0 for:
401 * /etc/rmtab updates (we could chown it to daemon)
402 * /etc/dfs/dfstab reading (it wants to lock out share which
403 * doesn't do any locking before first truncate;
404 * NFS share does; should use fcntl locking instead)
405 * Needed privileges:
406 * auditing
407 * nfs syscall
408 * file dac search (so it can stat all files)
409 * Optional privileges:
410 * MLP
412 can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
413 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
414 PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
415 can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
416 (void) fprintf(stderr,
417 "%s: must be run with sufficient privileges\n",
418 argv[0]);
419 exit(1);
422 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
423 syslog(LOG_ERR, "getrlimit failed");
424 } else {
425 rl.rlim_cur = rl.rlim_max;
426 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
427 syslog(LOG_ERR, "setrlimit failed");
430 (void) enable_extended_FILE_stdio(-1, -1);
432 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
433 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
434 if (ret != SA_OK) {
435 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
436 "failed, using default value");
439 while ((c = getopt(argc, argv, "vrm:")) != EOF) {
440 switch (c) {
441 case 'v':
442 verbose++;
443 break;
444 case 'r':
445 rejecting = 1;
446 break;
447 case 'm':
448 if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
449 (void) fprintf(stderr, "%s: invalid "
450 "max_threads option, using defaults\n",
451 argv[0]);
452 break;
454 max_threads = tmp;
455 break;
456 default:
457 fprintf(stderr, "usage: mountd [-v] [-r]\n");
458 exit(1);
463 * Read in the NFS version values from config file.
465 bufsz = 4;
466 ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
467 SCF_TYPE_INTEGER, NFSD, &bufsz);
468 if (ret == SA_OK) {
469 errno = 0;
470 defvers = strtol(defval, (char **)NULL, 10);
471 if (errno == 0) {
472 mount_vers_min = defvers;
474 * special because NFSv2 is
475 * supported by mount v1 & v2
477 if (defvers == NFS_VERSION)
478 mount_vers_min = MOUNTVERS;
482 bufsz = 4;
483 ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
484 SCF_TYPE_INTEGER, NFSD, &bufsz);
485 if (ret == SA_OK) {
486 errno = 0;
487 defvers = strtol(defval, (char **)NULL, 10);
488 if (errno == 0) {
489 mount_vers_max = defvers;
493 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
494 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
495 if (ret != SA_OK) {
496 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
497 "failed, using default value");
501 * Sanity check versions,
502 * even though we may get versions > MOUNTVERS3, we still need
503 * to start nfsauth service, so continue on regardless of values.
505 if (mount_vers_min > mount_vers_max) {
506 fprintf(stderr, "server_versmin > server_versmax\n");
507 mount_vers_max = mount_vers_min;
509 (void) setlocale(LC_ALL, "");
510 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
511 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
512 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
513 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
515 netgroup_init();
517 #if !defined(TEXT_DOMAIN)
518 #define TEXT_DOMAIN "SYS_TEST"
519 #endif
520 (void) textdomain(TEXT_DOMAIN);
522 /* Don't drop core if the NFS module isn't loaded. */
523 (void) signal(SIGSYS, SIG_IGN);
525 pipe_fd = daemonize_init();
528 * If we coredump it'll be in /core
530 if (chdir("/") < 0)
531 fprintf(stderr, "chdir /: %s\n", strerror(errno));
533 openlog("mountd", LOG_PID, LOG_DAEMON);
536 * establish our lock on the lock file and write our pid to it.
537 * exit if some other process holds the lock, or if there's any
538 * error in writing/locking the file.
540 pid = _enter_daemon_lock(MOUNTD);
541 switch (pid) {
542 case 0:
543 break;
544 case -1:
545 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
546 strerror(errno));
547 exit(2);
548 default:
549 /* daemon was already running */
550 exit(0);
553 audit_mountd_setup(); /* BSM */
556 * Get required system variables
558 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
559 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
560 exit(1);
562 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
563 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
564 exit(1);
568 * Set number of file descriptors to unlimited
570 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
571 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
575 * Tell RPC that we want automatic thread mode.
576 * A new thread will be spawned for each request.
578 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
579 fprintf(stderr, "unable to set automatic MT mode\n");
580 exit(1);
584 * Enable non-blocking mode and maximum record size checks for
585 * connection oriented transports.
587 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
588 fprintf(stderr, "unable to set RPC max record size\n");
592 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
593 * from being hijacked by a bind to a more specific addr.
595 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
596 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
600 * Set the maximum number of outstanding connection
601 * indications (listen backlog) to the value specified.
603 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
604 &listen_backlog)) {
605 fprintf(stderr, "unable to set listen backlog\n");
606 exit(1);
610 * If max_threads was specified, then set the
611 * maximum number of threads to the value specified.
613 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
614 fprintf(stderr, "unable to set max_threads\n");
615 exit(1);
619 * Make sure to unregister any previous versions in case the
620 * user is reconfiguring the server in interesting ways.
622 svc_unreg(MOUNTPROG, MOUNTVERS);
623 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
624 svc_unreg(MOUNTPROG, MOUNTVERS3);
627 * Create the nfsauth thread with same signal disposition
628 * as the main thread. We need to create a separate thread
629 * since mountd() will be both an RPC server (for remote
630 * traffic) _and_ a doors server (for kernel upcalls).
632 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
633 fprintf(stderr,
634 gettext("Failed to create NFSAUTH svc thread\n"));
635 exit(2);
639 * Create the cmd service thread with same signal disposition
640 * as the main thread. We need to create a separate thread
641 * since mountd() will be both an RPC server (for remote
642 * traffic) _and_ a doors server (for kernel upcalls).
644 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
645 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
646 exit(2);
650 * Create an additional thread to service the rmtab and
651 * audit_mountd_mount logging for mount requests. Use the same
652 * signal disposition as the main thread. We create
653 * a separate thread to allow the mount request threads to
654 * clear as soon as possible.
656 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
657 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
658 exit(2);
662 * Create datagram and connection oriented services
664 if (mount_vers_max >= MOUNTVERS) {
665 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
666 fprintf(stderr,
667 "couldn't register datagram_v MOUNTVERS\n");
668 exit(1);
670 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
671 fprintf(stderr,
672 "couldn't register circuit_v MOUNTVERS\n");
673 exit(1);
677 if (mount_vers_max >= MOUNTVERS_POSIX) {
678 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
679 "datagram_v") == 0) {
680 fprintf(stderr,
681 "couldn't register datagram_v MOUNTVERS_POSIX\n");
682 exit(1);
684 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
685 "circuit_v") == 0) {
686 fprintf(stderr,
687 "couldn't register circuit_v MOUNTVERS_POSIX\n");
688 exit(1);
692 if (mount_vers_max >= MOUNTVERS3) {
693 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
694 fprintf(stderr,
695 "couldn't register datagram_v MOUNTVERS3\n");
696 exit(1);
698 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
699 fprintf(stderr,
700 "couldn't register circuit_v MOUNTVERS3\n");
701 exit(1);
706 * Start serving
708 rmtab_load();
710 daemonize_fini(pipe_fd);
712 /* Get rid of the most dangerous basic privileges. */
713 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
714 (char *)NULL);
716 svc_run();
717 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
718 abort();
720 /* NOTREACHED */
721 return (0);
725 * Server procedure switch routine
727 void
728 mnt(struct svc_req *rqstp, SVCXPRT *transp)
730 switch (rqstp->rq_proc) {
731 case NULLPROC:
732 errno = 0;
733 if (!svc_sendreply(transp, xdr_void, (char *)0))
734 log_cant_reply(transp);
735 return;
737 case MOUNTPROC_MNT:
738 (void) mount(rqstp);
739 return;
741 case MOUNTPROC_DUMP:
742 mntlist_send(transp);
743 return;
745 case MOUNTPROC_UMNT:
746 umount(rqstp);
747 return;
749 case MOUNTPROC_UMNTALL:
750 umountall(rqstp);
751 return;
753 case MOUNTPROC_EXPORT:
754 case MOUNTPROC_EXPORTALL:
755 export(rqstp);
756 return;
758 case MOUNTPROC_PATHCONF:
759 if (rqstp->rq_vers == MOUNTVERS_POSIX)
760 mnt_pathconf(rqstp);
761 else
762 svcerr_noproc(transp);
763 return;
765 default:
766 svcerr_noproc(transp);
767 return;
771 /* Set up anonymous client */
773 struct nd_hostservlist *
774 anon_client(char *host)
776 struct nd_hostservlist *anon_hsl;
777 struct nd_hostserv *anon_hs;
779 anon_hsl = malloc(sizeof (*anon_hsl));
780 if (anon_hsl == NULL)
781 return (NULL);
783 anon_hs = malloc(sizeof (*anon_hs));
784 if (anon_hs == NULL) {
785 free(anon_hsl);
786 return (NULL);
789 if (host == NULL)
790 anon_hs->h_host = strdup("(anon)");
791 else
792 anon_hs->h_host = strdup(host);
794 if (anon_hs->h_host == NULL) {
795 free(anon_hs);
796 free(anon_hsl);
797 return (NULL);
799 anon_hs->h_serv = '\0';
801 anon_hsl->h_cnt = 1;
802 anon_hsl->h_hostservs = anon_hs;
804 return (anon_hsl);
807 static int
808 getclientsnames_common(struct netconfig *nconf, struct netbuf **nbuf,
809 struct nd_hostservlist **serv)
811 char host[MAXIPADDRLEN];
813 assert(*nbuf != NULL);
816 * Use the this API instead of the netdir_getbyaddr()
817 * to avoid service lookup.
819 if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf) != 0) {
820 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
821 struct sockaddr_in *sa;
823 /* LINTED pointer alignment */
824 sa = (struct sockaddr_in *)((*nbuf)->buf);
825 (void) inet_ntoa_r(sa->sin_addr, host);
826 } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
827 struct sockaddr_in6 *sa;
829 /* LINTED pointer alignment */
830 sa = (struct sockaddr_in6 *)((*nbuf)->buf);
831 (void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr,
832 host, INET6_ADDRSTRLEN);
833 } else {
834 syslog(LOG_ERR, gettext(
835 "Client's address is neither IPv4 nor IPv6"));
836 return (EINVAL);
839 *serv = anon_client(host);
840 if (*serv == NULL)
841 return (ENOMEM);
844 assert(*serv != NULL);
845 return (0);
849 * Get the client's hostname from the copy of the
850 * relevant transport handle parts.
851 * If the name is not available then return "(anon)".
853 static int
854 getclientsnames_lazy(char *netid, struct netbuf **nbuf,
855 struct nd_hostservlist **serv)
857 struct netconfig *nconf;
858 int rc;
860 nconf = getnetconfigent(netid);
861 if (nconf == NULL) {
862 syslog(LOG_ERR, "%s: getnetconfigent failed", netid);
863 *serv = anon_client(NULL);
864 if (*serv == NULL)
865 return (ENOMEM);
866 return (0);
869 rc = getclientsnames_common(nconf, nbuf, serv);
870 freenetconfigent(nconf);
871 return (rc);
875 * Get the client's hostname from the transport handle.
876 * If the name is not available then return "(anon)".
879 getclientsnames(SVCXPRT *transp, struct netbuf **nbuf,
880 struct nd_hostservlist **serv)
882 struct netconfig *nconf;
883 int rc;
885 nconf = getnetconfigent(transp->xp_netid);
886 if (nconf == NULL) {
887 syslog(LOG_ERR, "%s: getnetconfigent failed",
888 transp->xp_netid);
889 *serv = anon_client(NULL);
890 if (*serv == NULL)
891 return (ENOMEM);
892 return (0);
895 *nbuf = svc_getrpccaller(transp);
896 if (*nbuf == NULL) {
897 freenetconfigent(nconf);
898 *serv = anon_client(NULL);
899 if (*serv == NULL)
900 return (ENOMEM);
901 return (0);
904 rc = getclientsnames_common(nconf, nbuf, serv);
905 freenetconfigent(nconf);
906 return (rc);
909 void
910 log_cant_reply(SVCXPRT *transp)
912 int saverrno;
913 struct nd_hostservlist *clnames = NULL;
914 register char *host;
915 struct netbuf *nb;
917 saverrno = errno; /* save error code */
918 if (getclientsnames(transp, &nb, &clnames) != 0)
919 return;
920 host = clnames->h_hostservs->h_host;
922 errno = saverrno;
923 if (errno == 0)
924 syslog(LOG_ERR, "couldn't send reply to %s", host);
925 else
926 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
928 netdir_free(clnames, ND_HOSTSERVLIST);
932 * Answer pathconf questions for the mount point fs
934 static void
935 mnt_pathconf(struct svc_req *rqstp)
937 SVCXPRT *transp;
938 struct pathcnf p;
939 char *path, rpath[MAXPATHLEN];
940 struct stat st;
942 transp = rqstp->rq_xprt;
943 path = NULL;
944 (void) memset((caddr_t)&p, 0, sizeof (p));
946 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
947 svcerr_decode(transp);
948 return;
950 if (lstat(path, &st) < 0) {
951 _PC_SET(_PC_ERROR, p.pc_mask);
952 goto done;
955 * Get a path without symbolic links.
957 if (realpath(path, rpath) == NULL) {
958 syslog(LOG_DEBUG,
959 "mount request: realpath failed on %s: %m",
960 path);
961 _PC_SET(_PC_ERROR, p.pc_mask);
962 goto done;
964 (void) memset((caddr_t)&p, 0, sizeof (p));
966 * can't ask about devices over NFS
968 _PC_SET(_PC_MAX_CANON, p.pc_mask);
969 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
970 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
971 _PC_SET(_PC_VDISABLE, p.pc_mask);
973 errno = 0;
974 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
975 if (errno)
976 _PC_SET(_PC_LINK_MAX, p.pc_mask);
977 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
978 if (errno)
979 _PC_SET(_PC_NAME_MAX, p.pc_mask);
980 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
981 if (errno)
982 _PC_SET(_PC_PATH_MAX, p.pc_mask);
983 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
984 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
985 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
986 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
988 done:
989 errno = 0;
990 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
991 log_cant_reply(transp);
992 if (path != NULL)
993 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
997 * If the rootmount (export) option is specified, the all mount requests for
998 * subdirectories return EACCES.
1000 static int
1001 checkrootmount(share_t *sh, char *rpath)
1003 char *val;
1005 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
1006 free(val);
1007 if (strcmp(sh->sh_path, rpath) != 0)
1008 return (0);
1009 else
1010 return (1);
1011 } else
1012 return (1);
1015 #define MAX_FLAVORS 128
1018 * Return only EACCES if client does not have access
1019 * to this directory.
1020 * "If the server exports only /a/b, an attempt to
1021 * mount a/b/c will fail with ENOENT if the directory
1022 * does not exist"... However, if the client
1023 * does not have access to /a/b, an attacker can
1024 * determine whether the directory exists.
1025 * This routine checks either existence of the file or
1026 * existence of the file name entry in the mount table.
1027 * If the file exists and there is no file name entry,
1028 * the error returned should be EACCES.
1029 * If the file does not exist, it must be determined
1030 * whether the client has access to a parent
1031 * directory. If the client has access to a parent
1032 * directory, the error returned should be ENOENT,
1033 * otherwise EACCES.
1035 static int
1036 mount_enoent_error(SVCXPRT *transp, char *path, char *rpath,
1037 struct nd_hostservlist **clnames, struct netbuf **nb, int *flavor_list)
1039 char *checkpath, *dp;
1040 share_t *sh = NULL;
1041 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
1042 int flavor_count;
1044 checkpath = strdup(path);
1045 if (checkpath == NULL) {
1046 syslog(LOG_ERR, "mount_enoent: no memory");
1047 return (EACCES);
1050 /* CONSTCOND */
1051 while (1) {
1052 if (sh) {
1053 sharefree(sh);
1054 sh = NULL;
1057 if ((sh = findentry(rpath)) == NULL &&
1058 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1060 * There is no file name entry.
1061 * If the file (with symbolic links resolved) exists,
1062 * the error returned should be EACCES.
1064 if (realpath_error == 0)
1065 break;
1066 } else if (checkrootmount(sh, rpath) == 0) {
1068 * This is a "nosub" only export, in which case,
1069 * mounting subdirectories isn't allowed.
1070 * If the file (with symbolic links resolved) exists,
1071 * the error returned should be EACCES.
1073 if (realpath_error == 0)
1074 break;
1075 } else {
1077 * Check permissions in mount table.
1079 if (newopts(sh->sh_opts))
1080 flavor_count = getclientsflavors_new(sh,
1081 transp, nb, clnames, flavor_list);
1082 else
1083 flavor_count = getclientsflavors_old(sh,
1084 transp, nb, clnames, flavor_list);
1085 if (flavor_count != 0) {
1087 * Found entry in table and
1088 * client has correct permissions.
1090 reply_error = ENOENT;
1091 break;
1096 * Check all parent directories.
1098 dp = strrchr(checkpath, '/');
1099 if (dp == NULL)
1100 break;
1101 *dp = '\0';
1102 if (strlen(checkpath) == 0)
1103 break;
1105 * Get the real path (no symbolic links in it)
1107 if (realpath(checkpath, rpath) == NULL) {
1108 if (errno != ENOENT)
1109 break;
1110 } else {
1111 realpath_error = 0;
1115 if (sh)
1116 sharefree(sh);
1117 free(checkpath);
1118 return (reply_error);
1122 * We need to inform the caller whether or not we were
1123 * able to add a node to the queue. If we are not, then
1124 * it is up to the caller to go ahead and log the data.
1126 static int
1127 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
1128 char *rpath, int status, int error)
1130 logging_data *lq;
1131 struct netbuf *nb;
1133 lq = (logging_data *)calloc(1, sizeof (logging_data));
1134 if (lq == NULL)
1135 goto cleanup;
1138 * We might not yet have the host...
1140 if (host) {
1141 DTRACE_PROBE1(mountd, log_host, host);
1142 lq->ld_host = strdup(host);
1143 if (lq->ld_host == NULL)
1144 goto cleanup;
1145 } else {
1146 DTRACE_PROBE(mountd, log_no_host);
1148 lq->ld_netid = strdup(transp->xp_netid);
1149 if (lq->ld_netid == NULL)
1150 goto cleanup;
1152 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1153 if (lq->ld_nb == NULL)
1154 goto cleanup;
1156 nb = svc_getrpccaller(transp);
1157 if (nb == NULL) {
1158 DTRACE_PROBE(mountd, e__nb__enqueue);
1159 goto cleanup;
1162 DTRACE_PROBE(mountd, nb_set_enqueue);
1164 lq->ld_nb->maxlen = nb->maxlen;
1165 lq->ld_nb->len = nb->len;
1167 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1168 if (lq->ld_nb->buf == NULL)
1169 goto cleanup;
1171 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1174 lq->ld_path = strdup(path);
1175 if (lq->ld_path == NULL)
1176 goto cleanup;
1178 if (!error) {
1179 lq->ld_rpath = strdup(rpath);
1180 if (lq->ld_rpath == NULL)
1181 goto cleanup;
1184 lq->ld_status = status;
1187 * Add to the tail of the logging queue.
1189 (void) mutex_lock(&logging_queue_lock);
1190 if (logging_tail == NULL) {
1191 logging_tail = logging_head = lq;
1192 } else {
1193 logging_tail->ld_next = lq;
1194 logging_tail = lq;
1196 (void) cond_signal(&logging_queue_cv);
1197 (void) mutex_unlock(&logging_queue_lock);
1199 return (TRUE);
1201 cleanup:
1203 free_logging_data(lq);
1205 return (FALSE);
1209 * Check mount requests, add to mounted list if ok
1211 static int
1212 mount(struct svc_req *rqstp)
1214 SVCXPRT *transp;
1215 int version, vers;
1216 struct fhstatus fhs;
1217 struct mountres3 mountres3;
1218 char fh[FHSIZE3];
1219 int len = FHSIZE3;
1220 char *path, rpath[MAXPATHLEN];
1221 share_t *sh = NULL;
1222 struct nd_hostservlist *clnames = NULL;
1223 char *host = NULL;
1224 int error = 0, lofs_tried = 0, enqueued;
1225 int flavor_list[MAX_FLAVORS];
1226 int flavor_count;
1227 struct netbuf *nb = NULL;
1228 ucred_t *uc = NULL;
1230 int audit_status;
1232 transp = rqstp->rq_xprt;
1233 version = rqstp->rq_vers;
1234 path = NULL;
1236 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1237 svcerr_decode(transp);
1238 return (EACCES);
1242 * Put off getting the name for the client until we
1243 * need it. This is a performance gain. If we are logging,
1244 * then we don't care about performance and might as well
1245 * get the host name now in case we need to spit out an
1246 * error message.
1248 if (verbose) {
1249 DTRACE_PROBE(mountd, name_by_verbose);
1250 if (getclientsnames(transp, &nb, &clnames) != 0) {
1252 * We failed to get a name for the client, even
1253 * 'anon', probably because we ran out of memory.
1254 * In this situation it doesn't make sense to
1255 * allow the mount to succeed.
1257 error = EACCES;
1258 goto reply;
1260 host = clnames->h_hostservs[0].h_host;
1264 * If the version being used is less than the minimum version,
1265 * the filehandle translation should not be provided to the
1266 * client.
1268 if (rejecting || version < mount_vers_min) {
1269 if (verbose)
1270 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1271 host, path);
1272 error = EACCES;
1273 goto reply;
1277 * Trusted Extension doesn't support nfsv2. nfsv2 client
1278 * uses MOUNT protocol v1 and v2. To prevent circumventing
1279 * TX label policy via using nfsv2 client, reject a mount
1280 * request with version less than 3 and log an error.
1282 if (is_system_labeled()) {
1283 if (version < 3) {
1284 if (verbose)
1285 syslog(LOG_ERR,
1286 "Rejected mount: TX doesn't support NFSv2");
1287 error = EACCES;
1288 goto reply;
1293 * Get the real path (no symbolic links in it)
1295 if (realpath(path, rpath) == NULL) {
1296 error = errno;
1297 if (verbose)
1298 syslog(LOG_ERR,
1299 "mount request: realpath: %s: %m", path);
1300 if (error == ENOENT)
1301 error = mount_enoent_error(transp, path, rpath,
1302 &clnames, &nb, flavor_list);
1303 goto reply;
1306 if ((sh = findentry(rpath)) == NULL &&
1307 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1308 error = EACCES;
1309 goto reply;
1313 * Check if this is a "nosub" only export, in which case, mounting
1314 * subdirectories isn't allowed. Bug 1184573.
1316 if (checkrootmount(sh, rpath) == 0) {
1317 error = EACCES;
1318 goto reply;
1321 if (newopts(sh->sh_opts))
1322 flavor_count = getclientsflavors_new(sh, transp, &nb, &clnames,
1323 flavor_list);
1324 else
1325 flavor_count = getclientsflavors_old(sh, transp, &nb, &clnames,
1326 flavor_list);
1328 if (clnames)
1329 host = clnames->h_hostservs[0].h_host;
1331 if (flavor_count == 0) {
1332 error = EACCES;
1333 goto reply;
1337 * Check MAC policy here. The server side policy should be
1338 * consistent with client side mount policy, i.e.
1339 * - we disallow an admin_low unlabeled client to mount
1340 * - we disallow mount from a lower labeled client.
1342 if (is_system_labeled()) {
1343 m_label_t *clabel = NULL;
1344 m_label_t *slabel = NULL;
1345 m_label_t admin_low;
1347 if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1348 syslog(LOG_ERR,
1349 "mount request: Failed to get caller's ucred : %m");
1350 error = EACCES;
1351 goto reply;
1353 if ((clabel = ucred_getlabel(uc)) == NULL) {
1354 syslog(LOG_ERR,
1355 "mount request: can't get client label from ucred");
1356 error = EACCES;
1357 goto reply;
1360 bsllow(&admin_low);
1361 if (blequal(&admin_low, clabel)) {
1362 struct sockaddr *ca;
1363 tsol_tpent_t *tp;
1365 ca = (struct sockaddr *)(void *)svc_getrpccaller(
1366 rqstp->rq_xprt)->buf;
1367 if (ca == NULL) {
1368 error = EACCES;
1369 goto reply;
1372 * get trusted network template associated
1373 * with the client.
1375 tp = get_client_template(ca);
1376 if (tp == NULL || tp->host_type != SUN_CIPSO) {
1377 if (tp != NULL)
1378 tsol_freetpent(tp);
1379 error = EACCES;
1380 goto reply;
1382 tsol_freetpent(tp);
1383 } else {
1384 if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1385 error = EACCES;
1386 goto reply;
1389 if (getlabel(rpath, slabel) != 0) {
1390 m_label_free(slabel);
1391 error = EACCES;
1392 goto reply;
1395 if (!bldominates(clabel, slabel)) {
1396 m_label_free(slabel);
1397 error = EACCES;
1398 goto reply;
1400 m_label_free(slabel);
1405 * Now get the filehandle.
1407 * NFS V2 clients get a 32 byte filehandle.
1408 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1409 * the embedded FIDs.
1411 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1413 /* LINTED pointer alignment */
1414 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1415 if (errno == EINVAL &&
1416 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1417 errno = 0;
1418 continue;
1420 error = errno == EINVAL ? EACCES : errno;
1421 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1422 path);
1423 break;
1426 if (version == MOUNTVERS3) {
1427 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1428 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1429 } else {
1430 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1433 reply:
1434 if (uc != NULL)
1435 ucred_free(uc);
1437 switch (version) {
1438 case MOUNTVERS:
1439 case MOUNTVERS_POSIX:
1440 if (error == EINVAL)
1441 fhs.fhs_status = NFSERR_ACCES;
1442 else if (error == EREMOTE)
1443 fhs.fhs_status = NFSERR_REMOTE;
1444 else
1445 fhs.fhs_status = error;
1447 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1448 log_cant_reply(transp);
1450 audit_status = fhs.fhs_status;
1451 break;
1453 case MOUNTVERS3:
1454 if (!error) {
1455 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1456 flavor_list;
1457 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1458 flavor_count;
1460 } else if (error == ENAMETOOLONG)
1461 error = MNT3ERR_NAMETOOLONG;
1463 mountres3.fhs_status = error;
1464 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1465 log_cant_reply(transp);
1467 audit_status = mountres3.fhs_status;
1468 break;
1471 if (verbose)
1472 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1473 (host == NULL) ? "unknown host" : host,
1474 error ? "denied" : "mounted", path);
1477 * If we can not create a queue entry, go ahead and do it
1478 * in the context of this thread.
1480 enqueued = enqueue_logging_data(host, transp, path, rpath,
1481 audit_status, error);
1482 if (enqueued == FALSE) {
1483 if (host == NULL) {
1484 DTRACE_PROBE(mountd, name_by_in_thread);
1485 if (getclientsnames(transp, &nb, &clnames) == 0)
1486 host = clnames->h_hostservs[0].h_host;
1489 DTRACE_PROBE(mountd, logged_in_thread);
1490 audit_mountd_mount(host, path, audit_status); /* BSM */
1491 if (!error)
1492 mntlist_new(host, rpath); /* add entry to mount list */
1495 if (path != NULL)
1496 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1498 done:
1499 if (sh)
1500 sharefree(sh);
1501 netdir_free(clnames, ND_HOSTSERVLIST);
1503 return (error);
1507 * Determine whether two paths are within the same file system.
1508 * Returns nonzero (true) if paths are the same, zero (false) if
1509 * they are different. If an error occurs, return false.
1511 * Use the actual FSID if it's available (via getattrat()); otherwise,
1512 * fall back on st_dev.
1514 * With ZFS snapshots, st_dev differs from the regular file system
1515 * versus the snapshot. But the fsid is the same throughout. Thus
1516 * the fsid is a better test.
1518 static int
1519 same_file_system(const char *path1, const char *path2)
1521 uint64_t fsid1, fsid2;
1522 struct stat64 st1, st2;
1523 nvlist_t *nvl1 = NULL;
1524 nvlist_t *nvl2 = NULL;
1526 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1527 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1528 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1529 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1530 nvlist_free(nvl1);
1531 nvlist_free(nvl2);
1533 * We have found fsid's for both paths.
1536 if (fsid1 == fsid2)
1537 return (B_TRUE);
1539 return (B_FALSE);
1542 if (nvl1 != NULL)
1543 nvlist_free(nvl1);
1544 if (nvl2 != NULL)
1545 nvlist_free(nvl2);
1548 * We were unable to find fsid's for at least one of the paths.
1549 * fall back on st_dev.
1552 if (stat64(path1, &st1) < 0) {
1553 syslog(LOG_NOTICE, "%s: %m", path1);
1554 return (B_FALSE);
1556 if (stat64(path2, &st2) < 0) {
1557 syslog(LOG_NOTICE, "%s: %m", path2);
1558 return (B_FALSE);
1561 if (st1.st_dev == st2.st_dev)
1562 return (B_TRUE);
1564 return (B_FALSE);
1567 share_t *
1568 findentry(char *path)
1570 share_t *sh = NULL;
1571 struct sh_list *shp;
1572 register char *p1, *p2;
1574 check_sharetab();
1576 (void) rw_rdlock(&sharetab_lock);
1578 for (shp = share_list; shp; shp = shp->shl_next) {
1579 sh = shp->shl_sh;
1580 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1581 if (*p1 == '\0')
1582 goto done; /* exact match */
1585 * Now compare the pathnames for three cases:
1587 * Parent: /export/foo (no trailing slash on parent)
1588 * Child: /export/foo/bar
1590 * Parent: /export/foo/ (trailing slash on parent)
1591 * Child: /export/foo/bar
1593 * Parent: /export/foo/ (no trailing slash on child)
1594 * Child: /export/foo
1596 if ((*p1 == '\0' && *p2 == '/') ||
1597 (*p1 == '\0' && *(p1-1) == '/') ||
1598 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1600 * We have a subdirectory. Test whether the
1601 * subdirectory is in the same file system.
1603 if (same_file_system(path, sh->sh_path))
1604 goto done;
1607 done:
1608 sh = shp ? sharedup(sh) : NULL;
1610 (void) rw_unlock(&sharetab_lock);
1612 return (sh);
1616 static int
1617 is_substring(char **mntp, char **path)
1619 char *p1 = *mntp, *p2 = *path;
1621 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1622 return (1);
1623 else if (*p1 == '\0' && *p2 == '/')
1624 return (1);
1625 else if (*p1 == '\0' && *(p1-1) == '/') {
1626 *path = --p2; /* we need the slash in p2 */
1627 return (1);
1628 } else if (*p2 == '\0') {
1629 while (*p1 == '/')
1630 p1++;
1631 if (*p1 == '\0') /* exact match */
1632 return (1);
1634 return (0);
1638 * find_lofsentry() searches for the real path which this requested LOFS path
1639 * (rpath) shadows. If found, it will return the sharetab entry of
1640 * the real path that corresponds to the LOFS path.
1641 * We first search mnttab to see if the requested path is an automounted
1642 * path. If it is an automounted path, it will trigger the mount by stat()ing
1643 * the requested path. Note that it is important to check that this path is
1644 * actually an automounted path, otherwise we would stat() a path which may
1645 * turn out to be NFS and block indefinitely on a dead server. The automounter
1646 * times-out if the server is dead, so there's no risk of hanging this
1647 * thread waiting for stat().
1648 * After the mount has been triggered (if necessary), we look for a
1649 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1650 * is a substring of the rpath. If found, we construct a new path by
1651 * concatenating the mnt_special and the remaining of rpath, call findentry()
1652 * to make sure the 'real path' is shared.
1654 static share_t *
1655 find_lofsentry(char *rpath, int *done_flag)
1657 struct stat r_stbuf;
1658 mntlist_t *ml, *mntl, *mntpnt = NULL;
1659 share_t *retcode = NULL;
1660 char tmp_path[MAXPATHLEN];
1661 int mntpnt_len = 0, tmp;
1662 char *p1, *p2;
1664 if ((*done_flag)++)
1665 return (retcode);
1668 * While fsgetmntlist() uses lockf() to
1669 * lock the mnttab before reading it in,
1670 * the lock ignores threads in the same process.
1671 * Read in the mnttab with the protection of a mutex.
1673 (void) mutex_lock(&mnttab_lock);
1674 mntl = fsgetmntlist();
1675 (void) mutex_unlock(&mnttab_lock);
1678 * Obtain the mountpoint for the requested path.
1680 for (ml = mntl; ml; ml = ml->mntl_next) {
1681 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1682 *p1 == *p2 && *p1; p1++, p2++)
1684 if (is_substring(&p1, &p2) &&
1685 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1686 mntpnt = ml;
1687 mntpnt_len = tmp;
1692 * If the path needs to be autoFS mounted, trigger the mount by
1693 * stat()ing it. This is determined by checking whether the
1694 * mountpoint we just found is of type autofs.
1696 if (mntpnt != NULL &&
1697 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1699 * The requested path is a substring of an autoFS filesystem.
1700 * Trigger the mount.
1702 if (stat(rpath, &r_stbuf) < 0) {
1703 if (verbose)
1704 syslog(LOG_NOTICE, "%s: %m", rpath);
1705 goto done;
1707 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1709 * The requested path is a directory, stat(2) it
1710 * again with a trailing '.' to force the autoFS
1711 * module to trigger the mount of indirect
1712 * automount entries, such as /net/jurassic/.
1714 if (strlen(rpath) + 2 > MAXPATHLEN) {
1715 if (verbose) {
1716 syslog(LOG_NOTICE,
1717 "%s/.: exceeds MAXPATHLEN %d",
1718 rpath, MAXPATHLEN);
1720 goto done;
1722 (void) strcpy(tmp_path, rpath);
1723 (void) strcat(tmp_path, "/.");
1725 if (stat(tmp_path, &r_stbuf) < 0) {
1726 if (verbose)
1727 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1728 goto done;
1733 * The mount has been triggered, re-read mnttab to pick up
1734 * the changes made by autoFS.
1736 fsfreemntlist(mntl);
1737 (void) mutex_lock(&mnttab_lock);
1738 mntl = fsgetmntlist();
1739 (void) mutex_unlock(&mnttab_lock);
1743 * The autoFS mountpoint has been triggered if necessary,
1744 * now search mnttab again to determine if the requested path
1745 * is an LOFS mount of a shared path.
1747 mntpnt_len = 0;
1748 for (ml = mntl; ml; ml = ml->mntl_next) {
1749 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1750 continue;
1752 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1753 *p1 == *p2 && *p1; p1++, p2++)
1756 if (is_substring(&p1, &p2) &&
1757 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1758 mntpnt_len = tmp;
1760 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1761 MAXPATHLEN) {
1762 if (verbose) {
1763 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1764 ml->mntl_mnt->mnt_special, p2,
1765 MAXPATHLEN);
1767 if (retcode)
1768 sharefree(retcode);
1769 retcode = NULL;
1770 goto done;
1773 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1774 (void) strcat(tmp_path, p2);
1775 if (retcode)
1776 sharefree(retcode);
1777 retcode = findentry(tmp_path);
1781 if (retcode) {
1782 assert(strlen(tmp_path) > 0);
1783 (void) strcpy(rpath, tmp_path);
1786 done:
1787 fsfreemntlist(mntl);
1788 return (retcode);
1792 * Determine whether an access list grants rights to a particular host.
1793 * We match on aliases of the hostname as well as on the canonical name.
1794 * Names in the access list may be either hosts or netgroups; they're
1795 * not distinguished syntactically. We check for hosts first because
1796 * it's cheaper, then try netgroups.
1798 * If pnb and pclnames are NULL, it means that we have to use transp
1799 * to resolve client IP address to hostname. If they aren't NULL
1800 * then transp argument won't be used and can be NULL.
1803 in_access_list(SVCXPRT *transp, struct netbuf **pnb,
1804 struct nd_hostservlist **pclnames,
1805 char *access_list) /* N.B. we clobber this "input" parameter */
1807 char addr[INET_ADDRSTRLEN];
1808 char buff[256];
1809 int nentries = 0;
1810 char *cstr = access_list;
1811 char *gr = access_list;
1812 char *host;
1813 int off;
1814 int i;
1815 int response;
1816 int sbr = 0;
1817 struct nd_hostservlist *clnames;
1818 struct netent n, *np;
1820 /* If no access list - then it's unrestricted */
1821 if (access_list == NULL || *access_list == '\0')
1822 return (1);
1824 assert(transp != NULL || (*pnb != NULL && *pclnames != NULL));
1826 /* Get client address if it wasn't provided */
1827 if (*pnb == NULL)
1828 /* Don't grant access if client address isn't known */
1829 if ((*pnb = svc_getrpccaller(transp)) == NULL)
1830 return (0);
1832 /* Try to lookup client hostname if it wasn't provided */
1833 if (*pclnames == NULL)
1834 getclientsnames(transp, pnb, pclnames);
1835 clnames = *pclnames;
1837 for (;;) {
1838 if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
1839 switch (*cstr) {
1840 case '[':
1841 case ']':
1842 sbr = !sbr;
1843 cstr++;
1844 continue;
1845 case ':':
1846 if (sbr) {
1847 cstr++;
1848 continue;
1850 *cstr = '\0';
1855 * If the list name has a '-' prepended then a match of
1856 * the following name implies failure instead of success.
1858 if (*gr == '-') {
1859 response = 0;
1860 gr++;
1861 } else {
1862 response = 1;
1866 * First check if we have '@' entry, as it doesn't
1867 * require client hostname.
1869 if (*gr == '@') {
1870 gr++;
1872 /* Netname support */
1873 if (!isdigit(*gr) && *gr != '[') {
1874 if ((np = getnetbyname_r(gr, &n, buff,
1875 sizeof (buff))) != NULL &&
1876 np->n_net != 0) {
1877 while ((np->n_net & 0xFF000000u) == 0)
1878 np->n_net <<= 8;
1879 np->n_net = htonl(np->n_net);
1880 if (inet_ntop(AF_INET, &np->n_net, addr,
1881 INET_ADDRSTRLEN) == NULL)
1882 break;
1883 if (inet_matchaddr((*pnb)->buf, addr))
1884 return (response);
1886 } else {
1887 if (inet_matchaddr((*pnb)->buf, gr))
1888 return (response);
1891 if (cstr == NULL)
1892 break;
1894 gr = ++cstr;
1896 continue;
1900 * No other checks can be performed if client address
1901 * can't be resolved.
1903 if (clnames == NULL) {
1904 if (cstr == NULL)
1905 break;
1907 gr = ++cstr;
1909 continue;
1912 /* Otherwise loop through all client hostname aliases */
1913 for (i = 0; i < clnames->h_cnt; i++) {
1914 host = clnames->h_hostservs[i].h_host;
1917 * If the list name begins with a dot then
1918 * do a domain name suffix comparison.
1919 * A single dot matches any name with no
1920 * suffix.
1922 if (*gr == '.') {
1923 if (*(gr + 1) == '\0') { /* single dot */
1924 if (strchr(host, '.') == NULL)
1925 return (response);
1926 } else {
1927 off = strlen(host) - strlen(gr);
1928 if (off > 0 &&
1929 strcasecmp(host + off, gr) == 0) {
1930 return (response);
1933 } else {
1934 /* Just do a hostname match */
1935 if (strcasecmp(gr, host) == 0)
1936 return (response);
1940 nentries++;
1942 if (cstr == NULL)
1943 break;
1945 gr = ++cstr;
1948 if (clnames == NULL)
1949 return (0);
1951 return (netgroup_check(clnames, access_list, nentries));
1955 static char *optlist[] = {
1956 #define OPT_RO 0
1957 SHOPT_RO,
1958 #define OPT_RW 1
1959 SHOPT_RW,
1960 #define OPT_ROOT 2
1961 SHOPT_ROOT,
1962 #define OPT_SECURE 3
1963 SHOPT_SECURE,
1964 #define OPT_ANON 4
1965 SHOPT_ANON,
1966 #define OPT_WINDOW 5
1967 SHOPT_WINDOW,
1968 #define OPT_NOSUID 6
1969 SHOPT_NOSUID,
1970 #define OPT_ACLOK 7
1971 SHOPT_ACLOK,
1972 #define OPT_SEC 8
1973 SHOPT_SEC,
1974 #define OPT_NONE 9
1975 SHOPT_NONE,
1976 #define OPT_UIDMAP 10
1977 SHOPT_UIDMAP,
1978 #define OPT_GIDMAP 11
1979 SHOPT_GIDMAP,
1980 NULL
1983 static int
1984 map_flavor(char *str)
1986 seconfig_t sec;
1988 if (nfs_getseconfig_byname(str, &sec))
1989 return (-1);
1991 return (sec.sc_nfsnum);
1995 * If the option string contains a "sec="
1996 * option, then use new option syntax.
1998 static int
1999 newopts(char *opts)
2001 char *head, *p, *val;
2003 if (!opts || *opts == '\0')
2004 return (0);
2006 head = strdup(opts);
2007 if (head == NULL) {
2008 syslog(LOG_ERR, "opts: no memory");
2009 return (0);
2012 p = head;
2013 while (*p) {
2014 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
2015 free(head);
2016 return (1);
2020 free(head);
2021 return (0);
2025 * Given an export and the clients hostname(s)
2026 * determine the security flavors that this
2027 * client is permitted to use.
2029 * This routine is called only for "old" syntax, i.e.
2030 * only one security flavor is allowed. So we need
2031 * to determine two things: the particular flavor,
2032 * and whether the client is allowed to use this
2033 * flavor, i.e. is in the access list.
2035 * Note that if there is no access list, then the
2036 * default is that access is granted.
2038 static int
2039 getclientsflavors_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2040 struct nd_hostservlist **clnames, int *flavors)
2042 char *opts, *p, *val;
2043 boolean_t ok = B_FALSE;
2044 int defaultaccess = 1;
2045 boolean_t reject = B_FALSE;
2047 opts = strdup(sh->sh_opts);
2048 if (opts == NULL) {
2049 syslog(LOG_ERR, "getclientsflavors: no memory");
2050 return (0);
2053 flavors[0] = AUTH_SYS;
2054 p = opts;
2056 while (*p) {
2058 switch (getsubopt(&p, optlist, &val)) {
2059 case OPT_SECURE:
2060 flavors[0] = AUTH_DES;
2061 break;
2063 case OPT_RO:
2064 case OPT_RW:
2065 defaultaccess = 0;
2066 if (in_access_list(transp, nb, clnames, val))
2067 ok++;
2068 break;
2070 case OPT_NONE:
2071 defaultaccess = 0;
2072 if (in_access_list(transp, nb, clnames, val))
2073 reject = B_TRUE;
2077 free(opts);
2079 /* none takes precedence over everything else */
2080 if (reject)
2081 ok = B_TRUE;
2083 return (defaultaccess || ok);
2087 * Given an export and the clients hostname(s)
2088 * determine the security flavors that this
2089 * client is permitted to use.
2091 * This is somewhat more complicated than the "old"
2092 * routine because the options may contain multiple
2093 * security flavors (sec=) each with its own access
2094 * lists. So a client could be granted access based
2095 * on a number of security flavors. Note that the
2096 * type of access might not always be the same, the
2097 * client may get readonly access with one flavor
2098 * and readwrite with another, however the client
2099 * is not told this detail, it gets only the list
2100 * of flavors, and only if the client is using
2101 * version 3 of the mount protocol.
2103 static int
2104 getclientsflavors_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2105 struct nd_hostservlist **clnames, int *flavors)
2107 char *opts, *p, *val;
2108 char *lasts;
2109 char *f;
2110 boolean_t access_ok;
2111 int count, c;
2112 boolean_t reject = B_FALSE;
2114 opts = strdup(sh->sh_opts);
2115 if (opts == NULL) {
2116 syslog(LOG_ERR, "getclientsflavors: no memory");
2117 return (0);
2120 p = opts;
2121 count = c = 0;
2122 /* default access is rw */
2123 access_ok = B_TRUE;
2125 while (*p) {
2126 switch (getsubopt(&p, optlist, &val)) {
2127 case OPT_SEC:
2129 * Before a new sec=xxx option, check if we need
2130 * to move the c index back to the previous count.
2132 if (!access_ok) {
2133 c = count;
2136 /* get all the sec=f1[:f2] flavors */
2137 while ((f = strtok_r(val, ":", &lasts))
2138 != NULL) {
2139 flavors[c++] = map_flavor(f);
2140 val = NULL;
2143 /* for a new sec=xxx option, default is rw access */
2144 access_ok = B_TRUE;
2145 break;
2147 case OPT_RO:
2148 case OPT_RW:
2149 if (in_access_list(transp, nb, clnames, val)) {
2150 count = c;
2151 access_ok = B_TRUE;
2152 } else {
2153 access_ok = B_FALSE;
2155 break;
2157 case OPT_NONE:
2158 if (in_access_list(transp, nb, clnames, val))
2159 reject = B_TRUE; /* none overides rw/ro */
2160 break;
2164 if (reject)
2165 access_ok = B_FALSE;
2167 if (!access_ok)
2168 c = count;
2170 free(opts);
2172 return (c);
2176 * This is a tricky piece of code that parses the
2177 * share options looking for a match on the auth
2178 * flavor that the client is using. If it finds
2179 * a match, then the client is given ro, rw, or
2180 * no access depending whether it is in the access
2181 * list. There is a special case for "secure"
2182 * flavor. Other flavors are values of the new "sec=" option.
2185 check_client(share_t *sh, struct netbuf *nb,
2186 struct nd_hostservlist *clnames, int flavor, uid_t clnt_uid, gid_t clnt_gid,
2187 uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid, gid_t *srv_gid,
2188 uint_t *srv_ngids, gid_t **srv_gids)
2190 if (newopts(sh->sh_opts))
2191 return (check_client_new(sh, NULL, &nb, &clnames, flavor,
2192 clnt_uid, clnt_gid, clnt_ngids, clnt_gids, srv_uid, srv_gid,
2193 srv_ngids, srv_gids));
2194 else
2195 return (check_client_old(sh, NULL, &nb, &clnames, flavor,
2196 clnt_uid, clnt_gid, clnt_ngids, clnt_gids, srv_uid, srv_gid,
2197 srv_ngids, srv_gids));
2200 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2203 * Get supplemental groups for uid
2205 static int
2206 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2208 struct passwd pwd;
2209 char *pwbuf = alloca(pw_size);
2210 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2211 int tmpngrps;
2213 if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
2214 return (-1);
2216 tmpgrps[0] = pwd.pw_gid;
2218 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2219 if (tmpngrps <= 0) {
2220 syslog(LOG_WARNING,
2221 "getusergroups(): Unable to get groups for user %s",
2222 pwd.pw_name);
2224 return (-1);
2227 *grps = malloc(tmpngrps * sizeof (gid_t));
2228 if (*grps == NULL) {
2229 syslog(LOG_ERR,
2230 "getusergroups(): Memory allocation failed: %m");
2232 return (-1);
2235 *ngrps = tmpngrps;
2236 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2238 return (0);
2242 * is_a_number(number)
2244 * is the string a number in one of the forms we want to use?
2247 static int
2248 is_a_number(char *number)
2250 int ret = 1;
2251 int hex = 0;
2253 if (strncmp(number, "0x", 2) == 0) {
2254 number += 2;
2255 hex = 1;
2256 } else if (*number == '-') {
2257 number++; /* skip the minus */
2259 while (ret == 1 && *number != '\0') {
2260 if (hex) {
2261 ret = isxdigit(*number++);
2262 } else {
2263 ret = isdigit(*number++);
2266 return (ret);
2269 static boolean_t
2270 get_uid(char *value, uid_t *uid)
2272 if (!is_a_number(value)) {
2273 struct passwd *pw;
2275 * in this case it would have to be a
2276 * user name
2278 pw = getpwnam(value);
2279 if (pw == NULL)
2280 return (B_FALSE);
2281 *uid = pw->pw_uid;
2282 endpwent();
2283 } else {
2284 uint64_t intval;
2285 intval = strtoull(value, NULL, 0);
2286 if (intval > UID_MAX && intval != -1)
2287 return (B_FALSE);
2288 *uid = (uid_t)intval;
2291 return (B_TRUE);
2294 static boolean_t
2295 get_gid(char *value, gid_t *gid)
2297 if (!is_a_number(value)) {
2298 struct group *gr;
2300 * in this case it would have to be a
2301 * group name
2303 gr = getgrnam(value);
2304 if (gr == NULL)
2305 return (B_FALSE);
2306 *gid = gr->gr_gid;
2307 endgrent();
2308 } else {
2309 uint64_t intval;
2310 intval = strtoull(value, NULL, 0);
2311 if (intval > UID_MAX && intval != -1)
2312 return (B_FALSE);
2313 *gid = (gid_t)intval;
2316 return (B_TRUE);
2319 static int
2320 check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2321 struct nd_hostservlist **clnames, int flavor, uid_t clnt_uid,
2322 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2323 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2325 char *opts, *p, *val;
2326 int match; /* Set when a flavor is matched */
2327 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2328 int list = 0; /* Set when "ro", "rw" is found */
2329 int ro_val = 0; /* Set if ro option is 'ro=' */
2330 int rw_val = 0; /* Set if rw option is 'rw=' */
2332 boolean_t map_deny = B_FALSE;
2334 opts = strdup(sh->sh_opts);
2335 if (opts == NULL) {
2336 syslog(LOG_ERR, "check_client: no memory");
2337 return (0);
2341 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2342 * locally for all of them
2344 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2345 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2346 perm |= NFSAUTH_GROUPS;
2348 p = opts;
2349 match = AUTH_UNIX;
2351 while (*p) {
2352 switch (getsubopt(&p, optlist, &val)) {
2354 case OPT_SECURE:
2355 match = AUTH_DES;
2357 if (perm & NFSAUTH_GROUPS) {
2358 free(*srv_gids);
2359 *srv_ngids = 0;
2360 *srv_gids = NULL;
2361 perm &= ~NFSAUTH_GROUPS;
2364 break;
2366 case OPT_RO:
2367 list++;
2368 if (val != NULL)
2369 ro_val++;
2370 if (in_access_list(transp, nb, clnames, val))
2371 perm |= NFSAUTH_RO;
2372 break;
2374 case OPT_RW:
2375 list++;
2376 if (val != NULL)
2377 rw_val++;
2378 if (in_access_list(transp, nb, clnames, val))
2379 perm |= NFSAUTH_RW;
2380 break;
2382 case OPT_ROOT:
2384 * Check if the client is in
2385 * the root list. Only valid
2386 * for AUTH_SYS.
2388 if (flavor != AUTH_SYS)
2389 break;
2391 if (val == NULL || *val == '\0')
2392 break;
2394 if (clnt_uid != 0)
2395 break;
2397 if (in_access_list(transp, nb, clnames, val)) {
2398 perm |= NFSAUTH_ROOT;
2399 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2400 map_deny = B_FALSE;
2402 if (perm & NFSAUTH_GROUPS) {
2403 free(*srv_gids);
2404 *srv_ngids = 0;
2405 *srv_gids = NULL;
2406 perm &= ~NFSAUTH_GROUPS;
2409 break;
2411 case OPT_NONE:
2413 * Check if the client should have no access
2414 * to this share at all. This option behaves
2415 * more like "root" than either "rw" or "ro".
2417 if (in_access_list(transp, nb, clnames, val))
2418 perm |= NFSAUTH_DENIED;
2419 break;
2421 case OPT_UIDMAP: {
2422 char *c;
2423 char *n;
2426 * The uidmap is supported for AUTH_SYS only.
2428 if (flavor != AUTH_SYS)
2429 break;
2431 if (perm & NFSAUTH_UIDMAP || map_deny)
2432 break;
2434 for (c = val; c != NULL; c = n) {
2435 char *s;
2436 char *al;
2437 uid_t srv;
2439 n = strchr(c, '~');
2440 if (n != NULL)
2441 *n++ = '\0';
2443 s = strchr(c, ':');
2444 if (s != NULL) {
2445 *s++ = '\0';
2446 al = strchr(s, ':');
2447 if (al != NULL)
2448 *al++ = '\0';
2451 if (s == NULL || al == NULL)
2452 continue;
2454 if (*c == '\0') {
2455 if (clnt_uid != (uid_t)-1)
2456 continue;
2457 } else if (strcmp(c, "*") != 0) {
2458 uid_t clnt;
2460 if (!get_uid(c, &clnt))
2461 continue;
2463 if (clnt_uid != clnt)
2464 continue;
2467 if (*s == '\0')
2468 srv = UID_NOBODY;
2469 else if (!get_uid(s, &srv))
2470 continue;
2471 else if (srv == (uid_t)-1) {
2472 map_deny = B_TRUE;
2473 break;
2476 if (in_access_list(transp, nb, clnames, al)) {
2477 *srv_uid = srv;
2478 perm |= NFSAUTH_UIDMAP;
2480 if (perm & NFSAUTH_GROUPS) {
2481 free(*srv_gids);
2482 *srv_ngids = 0;
2483 *srv_gids = NULL;
2484 perm &= ~NFSAUTH_GROUPS;
2487 break;
2491 break;
2494 case OPT_GIDMAP: {
2495 char *c;
2496 char *n;
2499 * The gidmap is supported for AUTH_SYS only.
2501 if (flavor != AUTH_SYS)
2502 break;
2504 if (perm & NFSAUTH_GIDMAP || map_deny)
2505 break;
2507 for (c = val; c != NULL; c = n) {
2508 char *s;
2509 char *al;
2510 gid_t srv;
2512 n = strchr(c, '~');
2513 if (n != NULL)
2514 *n++ = '\0';
2516 s = strchr(c, ':');
2517 if (s != NULL) {
2518 *s++ = '\0';
2519 al = strchr(s, ':');
2520 if (al != NULL)
2521 *al++ = '\0';
2524 if (s == NULL || al == NULL)
2525 break;
2527 if (*c == '\0') {
2528 if (clnt_gid != (gid_t)-1)
2529 continue;
2530 } else if (strcmp(c, "*") != 0) {
2531 gid_t clnt;
2533 if (!get_gid(c, &clnt))
2534 continue;
2536 if (clnt_gid != clnt)
2537 continue;
2540 if (*s == '\0')
2541 srv = UID_NOBODY;
2542 else if (!get_gid(s, &srv))
2543 continue;
2544 else if (srv == (gid_t)-1) {
2545 map_deny = B_TRUE;
2546 break;
2549 if (in_access_list(transp, nb, clnames, al)) {
2550 *srv_gid = srv;
2551 perm |= NFSAUTH_GIDMAP;
2553 if (perm & NFSAUTH_GROUPS) {
2554 free(*srv_gids);
2555 *srv_ngids = 0;
2556 *srv_gids = NULL;
2557 perm &= ~NFSAUTH_GROUPS;
2560 break;
2564 break;
2567 default:
2568 break;
2572 free(opts);
2574 if (perm & NFSAUTH_ROOT) {
2575 *srv_uid = 0;
2576 *srv_gid = 0;
2579 if (map_deny)
2580 perm |= NFSAUTH_DENIED;
2582 if (!(perm & NFSAUTH_UIDMAP))
2583 *srv_uid = clnt_uid;
2584 if (!(perm & NFSAUTH_GIDMAP))
2585 *srv_gid = clnt_gid;
2587 if (flavor != match || perm & NFSAUTH_DENIED)
2588 return (NFSAUTH_DENIED);
2590 if (list) {
2592 * If the client doesn't match an "ro" or "rw"
2593 * list then set no access.
2595 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2596 perm |= NFSAUTH_DENIED;
2597 } else {
2599 * The client matched a flavor entry that
2600 * has no explicit "rw" or "ro" determination.
2601 * Default it to "rw".
2603 perm |= NFSAUTH_RW;
2607 * The client may show up in both ro= and rw=
2608 * lists. If so, then turn off the RO access
2609 * bit leaving RW access.
2611 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2613 * Logically cover all permutations of rw=,ro=.
2614 * In the case where, rw,ro=<host> we would like
2615 * to remove RW access for the host. In all other cases
2616 * RW wins the precedence battle.
2618 if (!rw_val && ro_val) {
2619 perm &= ~(NFSAUTH_RW);
2620 } else {
2621 perm &= ~(NFSAUTH_RO);
2625 return (perm);
2629 * Check if the client has access by using a flavor different from
2630 * the given "flavor". If "flavor" is not in the flavor list,
2631 * return TRUE to indicate that this "flavor" is a wrong sec.
2633 static bool_t
2634 is_wrongsec(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2635 struct nd_hostservlist **clnames, int flavor)
2637 int flavor_list[MAX_FLAVORS];
2638 int flavor_count, i;
2640 /* get the flavor list that the client has access with */
2641 flavor_count = getclientsflavors_new(sh, transp, nb,
2642 clnames, flavor_list);
2644 if (flavor_count == 0)
2645 return (FALSE);
2648 * Check if the given "flavor" is in the flavor_list.
2650 for (i = 0; i < flavor_count; i++) {
2651 if (flavor == flavor_list[i])
2652 return (FALSE);
2656 * If "flavor" is not in the flavor_list, return TRUE to indicate
2657 * that the client should have access by using a security flavor
2658 * different from this "flavor".
2660 return (TRUE);
2664 * Given an export and the client's hostname, we
2665 * check the security options to see whether the
2666 * client is allowed to use the given security flavor.
2668 * The strategy is to proceed through the options looking
2669 * for a flavor match, then pay attention to the ro, rw,
2670 * and root options.
2672 * Note that an entry may list several flavors in a
2673 * single entry, e.g.
2675 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2679 static int
2680 check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2681 struct nd_hostservlist **clnames, int flavor, uid_t clnt_uid,
2682 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2683 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2685 char *opts, *p, *val;
2686 char *lasts;
2687 char *f;
2688 int match = 0; /* Set when a flavor is matched */
2689 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2690 int list = 0; /* Set when "ro", "rw" is found */
2691 int ro_val = 0; /* Set if ro option is 'ro=' */
2692 int rw_val = 0; /* Set if rw option is 'rw=' */
2694 boolean_t map_deny = B_FALSE;
2696 opts = strdup(sh->sh_opts);
2697 if (opts == NULL) {
2698 syslog(LOG_ERR, "check_client: no memory");
2699 return (0);
2703 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2704 * locally for all of them
2706 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2707 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2708 perm |= NFSAUTH_GROUPS;
2710 p = opts;
2712 while (*p) {
2713 switch (getsubopt(&p, optlist, &val)) {
2715 case OPT_SEC:
2716 if (match)
2717 goto done;
2719 while ((f = strtok_r(val, ":", &lasts))
2720 != NULL) {
2721 if (flavor == map_flavor(f)) {
2722 match = 1;
2723 break;
2725 val = NULL;
2727 break;
2729 case OPT_RO:
2730 if (!match)
2731 break;
2733 list++;
2734 if (val != NULL)
2735 ro_val++;
2736 if (in_access_list(transp, nb, clnames, val))
2737 perm |= NFSAUTH_RO;
2738 break;
2740 case OPT_RW:
2741 if (!match)
2742 break;
2744 list++;
2745 if (val != NULL)
2746 rw_val++;
2747 if (in_access_list(transp, nb, clnames, val))
2748 perm |= NFSAUTH_RW;
2749 break;
2751 case OPT_ROOT:
2753 * Check if the client is in
2754 * the root list. Only valid
2755 * for AUTH_SYS.
2757 if (flavor != AUTH_SYS)
2758 break;
2760 if (!match)
2761 break;
2763 if (val == NULL || *val == '\0')
2764 break;
2766 if (clnt_uid != 0)
2767 break;
2769 if (in_access_list(transp, nb, clnames, val)) {
2770 perm |= NFSAUTH_ROOT;
2771 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2772 map_deny = B_FALSE;
2774 if (perm & NFSAUTH_GROUPS) {
2775 free(*srv_gids);
2776 *srv_gids = NULL;
2777 *srv_ngids = 0;
2778 perm &= ~NFSAUTH_GROUPS;
2781 break;
2783 case OPT_NONE:
2785 * Check if the client should have no access
2786 * to this share at all. This option behaves
2787 * more like "root" than either "rw" or "ro".
2789 if (in_access_list(transp, nb, clnames, val))
2790 perm |= NFSAUTH_DENIED;
2791 break;
2793 case OPT_UIDMAP: {
2794 char *c;
2795 char *n;
2798 * The uidmap is supported for AUTH_SYS only.
2800 if (flavor != AUTH_SYS)
2801 break;
2803 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2804 break;
2806 for (c = val; c != NULL; c = n) {
2807 char *s;
2808 char *al;
2809 uid_t srv;
2811 n = strchr(c, '~');
2812 if (n != NULL)
2813 *n++ = '\0';
2815 s = strchr(c, ':');
2816 if (s != NULL) {
2817 *s++ = '\0';
2818 al = strchr(s, ':');
2819 if (al != NULL)
2820 *al++ = '\0';
2823 if (s == NULL || al == NULL)
2824 continue;
2826 if (*c == '\0') {
2827 if (clnt_uid != (uid_t)-1)
2828 continue;
2829 } else if (strcmp(c, "*") != 0) {
2830 uid_t clnt;
2832 if (!get_uid(c, &clnt))
2833 continue;
2835 if (clnt_uid != clnt)
2836 continue;
2839 if (*s == '\0')
2840 srv = UID_NOBODY;
2841 else if (!get_uid(s, &srv))
2842 continue;
2843 else if (srv == (uid_t)-1) {
2844 map_deny = B_TRUE;
2845 break;
2848 if (in_access_list(transp, nb, clnames, al)) {
2849 *srv_uid = srv;
2850 perm |= NFSAUTH_UIDMAP;
2852 if (perm & NFSAUTH_GROUPS) {
2853 free(*srv_gids);
2854 *srv_gids = NULL;
2855 *srv_ngids = 0;
2856 perm &= ~NFSAUTH_GROUPS;
2859 break;
2863 break;
2866 case OPT_GIDMAP: {
2867 char *c;
2868 char *n;
2871 * The gidmap is supported for AUTH_SYS only.
2873 if (flavor != AUTH_SYS)
2874 break;
2876 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2877 break;
2879 for (c = val; c != NULL; c = n) {
2880 char *s;
2881 char *al;
2882 gid_t srv;
2884 n = strchr(c, '~');
2885 if (n != NULL)
2886 *n++ = '\0';
2888 s = strchr(c, ':');
2889 if (s != NULL) {
2890 *s++ = '\0';
2891 al = strchr(s, ':');
2892 if (al != NULL)
2893 *al++ = '\0';
2896 if (s == NULL || al == NULL)
2897 break;
2899 if (*c == '\0') {
2900 if (clnt_gid != (gid_t)-1)
2901 continue;
2902 } else if (strcmp(c, "*") != 0) {
2903 gid_t clnt;
2905 if (!get_gid(c, &clnt))
2906 continue;
2908 if (clnt_gid != clnt)
2909 continue;
2912 if (*s == '\0')
2913 srv = UID_NOBODY;
2914 else if (!get_gid(s, &srv))
2915 continue;
2916 else if (srv == (gid_t)-1) {
2917 map_deny = B_TRUE;
2918 break;
2921 if (in_access_list(transp, nb, clnames, al)) {
2922 *srv_gid = srv;
2923 perm |= NFSAUTH_GIDMAP;
2925 if (perm & NFSAUTH_GROUPS) {
2926 free(*srv_gids);
2927 *srv_gids = NULL;
2928 *srv_ngids = 0;
2929 perm &= ~NFSAUTH_GROUPS;
2932 break;
2936 break;
2939 default:
2940 break;
2944 done:
2945 if (perm & NFSAUTH_ROOT) {
2946 *srv_uid = 0;
2947 *srv_gid = 0;
2950 if (map_deny)
2951 perm |= NFSAUTH_DENIED;
2953 if (!(perm & NFSAUTH_UIDMAP))
2954 *srv_uid = clnt_uid;
2955 if (!(perm & NFSAUTH_GIDMAP))
2956 *srv_gid = clnt_gid;
2959 * If no match then set the perm accordingly
2961 if (!match || perm & NFSAUTH_DENIED) {
2962 free(opts);
2963 return (NFSAUTH_DENIED);
2966 if (list) {
2968 * If the client doesn't match an "ro" or "rw" list then
2969 * check if it may have access by using a different flavor.
2970 * If so, return NFSAUTH_WRONGSEC.
2971 * If not, return NFSAUTH_DENIED.
2973 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2974 if (is_wrongsec(sh, transp, nb, clnames, flavor))
2975 perm |= NFSAUTH_WRONGSEC;
2976 else
2977 perm |= NFSAUTH_DENIED;
2979 } else {
2981 * The client matched a flavor entry that
2982 * has no explicit "rw" or "ro" determination.
2983 * Make sure it defaults to "rw".
2985 perm |= NFSAUTH_RW;
2989 * The client may show up in both ro= and rw=
2990 * lists. If so, then turn off the RO access
2991 * bit leaving RW access.
2993 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2995 * Logically cover all permutations of rw=,ro=.
2996 * In the case where, rw,ro=<host> we would like
2997 * to remove RW access for the host. In all other cases
2998 * RW wins the precedence battle.
3000 if (!rw_val && ro_val) {
3001 perm &= ~(NFSAUTH_RW);
3002 } else {
3003 perm &= ~(NFSAUTH_RO);
3007 free(opts);
3009 return (perm);
3012 void
3013 check_sharetab()
3015 FILE *f;
3016 struct stat st;
3017 static timestruc_t last_sharetab_time;
3018 timestruc_t prev_sharetab_time;
3019 share_t *sh;
3020 struct sh_list *shp, *shp_prev;
3021 int res, c = 0;
3024 * read in /etc/dfs/sharetab if it has changed
3026 if (stat(SHARETAB, &st) != 0) {
3027 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3028 return;
3031 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
3032 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
3034 * No change.
3036 return;
3040 * Remember the mod time, then after getting the
3041 * write lock check again. If another thread
3042 * already did the update, then there's no
3043 * work to do.
3045 prev_sharetab_time = last_sharetab_time;
3047 (void) rw_wrlock(&sharetab_lock);
3049 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3050 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3051 (void) rw_unlock(&sharetab_lock);
3052 return;
3056 * Note that since the sharetab is now in memory
3057 * and a snapshot is taken, we no longer have to
3058 * lock the file.
3060 f = fopen(SHARETAB, "r");
3061 if (f == NULL) {
3062 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3063 (void) rw_unlock(&sharetab_lock);
3064 return;
3068 * Once we are sure /etc/dfs/sharetab has been
3069 * modified, flush netgroup cache entries.
3071 netgrp_cache_flush();
3073 sh_free(share_list); /* free old list */
3074 share_list = NULL;
3076 while ((res = getshare(f, &sh)) > 0) {
3077 c++;
3078 if (strcmp(sh->sh_fstype, "nfs") != 0)
3079 continue;
3081 shp = malloc(sizeof (*shp));
3082 if (shp == NULL)
3083 goto alloc_failed;
3084 if (share_list == NULL)
3085 share_list = shp;
3086 else
3087 /* LINTED not used before set */
3088 shp_prev->shl_next = shp;
3089 shp_prev = shp;
3090 shp->shl_next = NULL;
3091 shp->shl_sh = sharedup(sh);
3092 if (shp->shl_sh == NULL)
3093 goto alloc_failed;
3096 if (res < 0)
3097 syslog(LOG_ERR, "%s: invalid at line %d\n",
3098 SHARETAB, c + 1);
3100 if (stat(SHARETAB, &st) != 0) {
3101 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3102 (void) fclose(f);
3103 (void) rw_unlock(&sharetab_lock);
3104 return;
3107 last_sharetab_time = st.st_mtim;
3108 (void) fclose(f);
3109 (void) rw_unlock(&sharetab_lock);
3111 return;
3113 alloc_failed:
3115 syslog(LOG_ERR, "check_sharetab: no memory");
3116 sh_free(share_list);
3117 share_list = NULL;
3118 (void) fclose(f);
3119 (void) rw_unlock(&sharetab_lock);
3122 static void
3123 sh_free(struct sh_list *shp)
3125 register struct sh_list *next;
3127 while (shp) {
3128 sharefree(shp->shl_sh);
3129 next = shp->shl_next;
3130 free(shp);
3131 shp = next;
3137 * Remove an entry from mounted list
3139 static void
3140 umount(struct svc_req *rqstp)
3142 char *host, *path, *remove_path;
3143 char rpath[MAXPATHLEN];
3144 struct nd_hostservlist *clnames = NULL;
3145 SVCXPRT *transp;
3146 struct netbuf *nb;
3148 transp = rqstp->rq_xprt;
3149 path = NULL;
3150 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3151 svcerr_decode(transp);
3152 return;
3154 errno = 0;
3155 if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3156 log_cant_reply(transp);
3158 if (getclientsnames(transp, &nb, &clnames) != 0) {
3160 * Without the hostname we can't do audit or delete
3161 * this host from the mount entries.
3163 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3164 return;
3166 host = clnames->h_hostservs[0].h_host;
3168 if (verbose)
3169 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3171 audit_mountd_umount(host, path);
3173 remove_path = rpath; /* assume we will use the cannonical path */
3174 if (realpath(path, rpath) == NULL) {
3175 if (verbose)
3176 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3177 remove_path = path; /* use path provided instead */
3180 mntlist_delete(host, remove_path); /* remove from mount list */
3182 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3183 netdir_free(clnames, ND_HOSTSERVLIST);
3187 * Remove all entries for one machine from mounted list
3189 static void
3190 umountall(struct svc_req *rqstp)
3192 struct nd_hostservlist *clnames = NULL;
3193 SVCXPRT *transp;
3194 char *host;
3195 struct netbuf *nb;
3197 transp = rqstp->rq_xprt;
3198 if (!svc_getargs(transp, xdr_void, NULL)) {
3199 svcerr_decode(transp);
3200 return;
3203 * We assume that this call is asynchronous and made via rpcbind
3204 * callit routine. Therefore return control immediately. The error
3205 * causes rpcbind to remain silent, as opposed to every machine
3206 * on the net blasting the requester with a response.
3208 svcerr_systemerr(transp);
3209 if (getclientsnames(transp, &nb, &clnames) != 0) {
3210 /* Can't do anything without the name of the client */
3211 return;
3214 host = clnames->h_hostservs[0].h_host;
3217 * Remove all hosts entries from mount list
3219 mntlist_delete_all(host);
3221 if (verbose)
3222 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3224 netdir_free(clnames, ND_HOSTSERVLIST);
3227 void *
3228 exmalloc(size_t size)
3230 void *ret;
3232 if ((ret = malloc(size)) == NULL) {
3233 syslog(LOG_ERR, "Out of memory");
3234 exit(1);
3236 return (ret);
3239 static void
3240 sigexit(int signum)
3243 if (signum == SIGHUP)
3244 _exit(0);
3245 _exit(1);
3248 static tsol_tpent_t *
3249 get_client_template(struct sockaddr *sock)
3251 in_addr_t v4client;
3252 in6_addr_t v6client;
3253 char v4_addr[INET_ADDRSTRLEN];
3254 char v6_addr[INET6_ADDRSTRLEN];
3255 tsol_rhent_t *rh;
3256 tsol_tpent_t *tp;
3258 switch (sock->sa_family) {
3259 case AF_INET:
3260 v4client = ((struct sockaddr_in *)(void *)sock)->
3261 sin_addr.s_addr;
3262 if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
3263 NULL)
3264 return (NULL);
3265 rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
3266 if (rh == NULL)
3267 return (NULL);
3268 tp = tsol_gettpbyname(rh->rh_template);
3269 tsol_freerhent(rh);
3270 return (tp);
3271 break;
3272 case AF_INET6:
3273 v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
3274 if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
3275 NULL)
3276 return (NULL);
3277 rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
3278 if (rh == NULL)
3279 return (NULL);
3280 tp = tsol_gettpbyname(rh->rh_template);
3281 tsol_freerhent(rh);
3282 return (tp);
3283 break;
3284 default:
3285 return (NULL);