6332 mountd: Compile warnings cleanup
[illumos-gate.git] / usr / src / cmd / fs.d / nfs / mountd / mountd.c
blob50180d38151874eb190622338ae0c0e1cd2a70e5
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 getclientsnames_lazy(char *, struct netbuf **,
106 struct nd_hostservlist **);
107 static int getclientsnames(SVCXPRT *, struct netbuf **,
108 struct nd_hostservlist **);
109 static int getclientsflavors_old(share_t *, SVCXPRT *, struct netbuf **,
110 struct nd_hostservlist **, int *);
111 static int getclientsflavors_new(share_t *, SVCXPRT *, struct netbuf **,
112 struct nd_hostservlist **, int *);
113 static int check_client_old(share_t *, SVCXPRT *, struct netbuf **,
114 struct nd_hostservlist **, int, uid_t, gid_t, uint_t, gid_t *, uid_t *,
115 gid_t *, uint_t *, gid_t **);
116 static int check_client_new(share_t *, SVCXPRT *, struct netbuf **,
117 struct nd_hostservlist **, int, uid_t, gid_t, uint_t, gid_t *, uid_t *,
118 gid_t *i, uint_t *, gid_t **);
119 static void mnt(struct svc_req *, SVCXPRT *);
120 static void mnt_pathconf(struct svc_req *);
121 static int mount(struct svc_req *r);
122 static void sh_free(struct sh_list *);
123 static void umount(struct svc_req *);
124 static void umountall(struct svc_req *);
125 static int newopts(char *);
126 static tsol_tpent_t *get_client_template(struct sockaddr *);
128 static int verbose;
129 static int rejecting;
130 static int mount_vers_min = MOUNTVERS;
131 static int mount_vers_max = MOUNTVERS3;
133 extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
135 thread_t nfsauth_thread;
136 thread_t cmd_thread;
137 thread_t logging_thread;
139 typedef struct logging_data {
140 char *ld_host;
141 char *ld_path;
142 char *ld_rpath;
143 int ld_status;
144 char *ld_netid;
145 struct netbuf *ld_nb;
146 struct logging_data *ld_next;
147 } logging_data;
149 static logging_data *logging_head = NULL;
150 static logging_data *logging_tail = NULL;
153 * Our copy of some system variables obtained using sysconf(3c)
155 static long ngroups_max; /* _SC_NGROUPS_MAX */
156 static long pw_size; /* _SC_GETPW_R_SIZE_MAX */
158 /* ARGSUSED */
159 static void *
160 nfsauth_svc(void *arg)
162 int doorfd = -1;
163 uint_t darg;
164 #ifdef DEBUG
165 int dfd;
166 #endif
168 if ((doorfd = door_create(nfsauth_func, NULL,
169 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
170 syslog(LOG_ERR, "Unable to create door: %m\n");
171 exit(10);
174 #ifdef DEBUG
176 * Create a file system path for the door
178 if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
179 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
180 syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
181 (void) close(doorfd);
182 exit(11);
186 * Clean up any stale namespace associations
188 (void) fdetach(MOUNTD_DOOR);
191 * Register in namespace to pass to the kernel to door_ki_open
193 if (fattach(doorfd, MOUNTD_DOOR) == -1) {
194 syslog(LOG_ERR, "Unable to fattach door: %m\n");
195 (void) close(dfd);
196 (void) close(doorfd);
197 exit(12);
199 (void) close(dfd);
200 #endif
203 * Must pass the doorfd down to the kernel.
205 darg = doorfd;
206 (void) _nfssys(MOUNTD_ARGS, &darg);
209 * Wait for incoming calls
211 /*CONSTCOND*/
212 for (;;)
213 (void) pause();
215 /*NOTREACHED*/
216 syslog(LOG_ERR, gettext("Door server exited"));
217 return (NULL);
221 * NFS command service thread code for setup and handling of the
222 * nfs_cmd requests for character set conversion and other future
223 * events.
226 static void *
227 cmd_svc(void *arg)
229 int doorfd = -1;
230 uint_t darg;
232 if ((doorfd = door_create(nfscmd_func, NULL,
233 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
234 syslog(LOG_ERR, "Unable to create cmd door: %m\n");
235 exit(10);
239 * Must pass the doorfd down to the kernel.
241 darg = doorfd;
242 (void) _nfssys(NFSCMD_ARGS, &darg);
245 * Wait for incoming calls
247 /*CONSTCOND*/
248 for (;;)
249 (void) pause();
251 /*NOTREACHED*/
252 syslog(LOG_ERR, gettext("Cmd door server exited"));
253 return (NULL);
256 static void
257 free_logging_data(logging_data *lq)
259 if (lq != NULL) {
260 free(lq->ld_host);
261 free(lq->ld_netid);
263 if (lq->ld_nb != NULL) {
264 free(lq->ld_nb->buf);
265 free(lq->ld_nb);
268 free(lq->ld_path);
269 free(lq->ld_rpath);
271 free(lq);
275 static logging_data *
276 remove_head_of_queue(void)
278 logging_data *lq;
281 * Pull it off the queue.
283 lq = logging_head;
284 if (lq) {
285 logging_head = lq->ld_next;
288 * Drained it.
290 if (logging_head == NULL) {
291 logging_tail = NULL;
295 return (lq);
298 static void
299 do_logging_queue(logging_data *lq)
301 int cleared = 0;
302 char *host;
304 struct nd_hostservlist *clnames;
306 while (lq) {
307 if (lq->ld_host == NULL) {
308 DTRACE_PROBE(mountd, name_by_lazy);
309 if (getclientsnames_lazy(lq->ld_netid,
310 &lq->ld_nb, &clnames) != 0)
311 host = NULL;
312 else
313 host = clnames->h_hostservs[0].h_host;
314 } else
315 host = lq->ld_host;
317 audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
319 /* add entry to mount list */
320 if (lq->ld_rpath)
321 mntlist_new(host, lq->ld_rpath);
323 if (lq->ld_host != host)
324 netdir_free(clnames, ND_HOSTSERVLIST);
326 free_logging_data(lq);
327 cleared++;
329 (void) mutex_lock(&logging_queue_lock);
330 lq = remove_head_of_queue();
331 (void) mutex_unlock(&logging_queue_lock);
334 DTRACE_PROBE1(mountd, logging_cleared, cleared);
337 static void *
338 logging_svc(void *arg)
340 logging_data *lq;
342 for (;;) {
343 (void) mutex_lock(&logging_queue_lock);
344 while (logging_head == NULL) {
345 (void) cond_wait(&logging_queue_cv,
346 &logging_queue_lock);
349 lq = remove_head_of_queue();
350 (void) mutex_unlock(&logging_queue_lock);
352 do_logging_queue(lq);
355 /*NOTREACHED*/
356 syslog(LOG_ERR, gettext("Logging server exited"));
357 return (NULL);
360 static int
361 convert_int(int *val, char *str)
363 long lval;
365 if (str == NULL || !isdigit(*str))
366 return (-1);
368 lval = strtol(str, &str, 10);
369 if (*str != '\0' || lval > INT_MAX)
370 return (-2);
372 *val = (int)lval;
373 return (0);
377 main(int argc, char *argv[])
379 int pid;
380 int c;
381 int rpc_svc_fdunlim = 1;
382 int rpc_svc_mode = RPC_SVC_MT_AUTO;
383 int maxrecsz = RPC_MAXDATASIZE;
384 bool_t exclbind = TRUE;
385 bool_t can_do_mlp;
386 long thr_flags = (THR_NEW_LWP|THR_DAEMON);
387 char defval[4];
388 int defvers, ret, bufsz;
389 struct rlimit rl;
390 int listen_backlog = 0;
391 int max_threads = 0;
392 int tmp;
394 int pipe_fd = -1;
397 * Mountd requires uid 0 for:
398 * /etc/rmtab updates (we could chown it to daemon)
399 * /etc/dfs/dfstab reading (it wants to lock out share which
400 * doesn't do any locking before first truncate;
401 * NFS share does; should use fcntl locking instead)
402 * Needed privileges:
403 * auditing
404 * nfs syscall
405 * file dac search (so it can stat all files)
406 * Optional privileges:
407 * MLP
409 can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
410 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
411 PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
412 can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
413 (void) fprintf(stderr,
414 "%s: must be run with sufficient privileges\n",
415 argv[0]);
416 exit(1);
419 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
420 syslog(LOG_ERR, "getrlimit failed");
421 } else {
422 rl.rlim_cur = rl.rlim_max;
423 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
424 syslog(LOG_ERR, "setrlimit failed");
427 (void) enable_extended_FILE_stdio(-1, -1);
429 ret = nfs_smf_get_iprop("mountd_max_threads", &max_threads,
430 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
431 if (ret != SA_OK) {
432 syslog(LOG_ERR, "Reading of mountd_max_threads from SMF "
433 "failed, using default value");
436 while ((c = getopt(argc, argv, "vrm:")) != EOF) {
437 switch (c) {
438 case 'v':
439 verbose++;
440 break;
441 case 'r':
442 rejecting = 1;
443 break;
444 case 'm':
445 if (convert_int(&tmp, optarg) != 0 || tmp < 1) {
446 (void) fprintf(stderr, "%s: invalid "
447 "max_threads option, using defaults\n",
448 argv[0]);
449 break;
451 max_threads = tmp;
452 break;
453 default:
454 fprintf(stderr, "usage: mountd [-v] [-r]\n");
455 exit(1);
460 * Read in the NFS version values from config file.
462 bufsz = 4;
463 ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
464 SCF_TYPE_INTEGER, NFSD, &bufsz);
465 if (ret == SA_OK) {
466 errno = 0;
467 defvers = strtol(defval, (char **)NULL, 10);
468 if (errno == 0) {
469 mount_vers_min = defvers;
471 * special because NFSv2 is
472 * supported by mount v1 & v2
474 if (defvers == NFS_VERSION)
475 mount_vers_min = MOUNTVERS;
479 bufsz = 4;
480 ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
481 SCF_TYPE_INTEGER, NFSD, &bufsz);
482 if (ret == SA_OK) {
483 errno = 0;
484 defvers = strtol(defval, (char **)NULL, 10);
485 if (errno == 0) {
486 mount_vers_max = defvers;
490 ret = nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog,
491 DEFAULT_INSTANCE, SCF_TYPE_INTEGER, NFSD);
492 if (ret != SA_OK) {
493 syslog(LOG_ERR, "Reading of mountd_listen_backlog from SMF "
494 "failed, using default value");
498 * Sanity check versions,
499 * even though we may get versions > MOUNTVERS3, we still need
500 * to start nfsauth service, so continue on regardless of values.
502 if (mount_vers_min > mount_vers_max) {
503 fprintf(stderr, "server_versmin > server_versmax\n");
504 mount_vers_max = mount_vers_min;
506 (void) setlocale(LC_ALL, "");
507 (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
508 (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
509 (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
510 (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
512 netgroup_init();
514 #if !defined(TEXT_DOMAIN)
515 #define TEXT_DOMAIN "SYS_TEST"
516 #endif
517 (void) textdomain(TEXT_DOMAIN);
519 /* Don't drop core if the NFS module isn't loaded. */
520 (void) signal(SIGSYS, SIG_IGN);
522 pipe_fd = daemonize_init();
525 * If we coredump it'll be in /core
527 if (chdir("/") < 0)
528 fprintf(stderr, "chdir /: %s\n", strerror(errno));
530 openlog("mountd", LOG_PID, LOG_DAEMON);
533 * establish our lock on the lock file and write our pid to it.
534 * exit if some other process holds the lock, or if there's any
535 * error in writing/locking the file.
537 pid = _enter_daemon_lock(MOUNTD);
538 switch (pid) {
539 case 0:
540 break;
541 case -1:
542 fprintf(stderr, "error locking for %s: %s\n", MOUNTD,
543 strerror(errno));
544 exit(2);
545 default:
546 /* daemon was already running */
547 exit(0);
550 audit_mountd_setup(); /* BSM */
553 * Get required system variables
555 if ((ngroups_max = sysconf(_SC_NGROUPS_MAX)) == -1) {
556 syslog(LOG_ERR, "Unable to get _SC_NGROUPS_MAX");
557 exit(1);
559 if ((pw_size = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
560 syslog(LOG_ERR, "Unable to get _SC_GETPW_R_SIZE_MAX");
561 exit(1);
565 * Set number of file descriptors to unlimited
567 if (!rpc_control(RPC_SVC_USE_POLLFD, &rpc_svc_fdunlim)) {
568 syslog(LOG_INFO, "unable to set number of FDs to unlimited");
572 * Tell RPC that we want automatic thread mode.
573 * A new thread will be spawned for each request.
575 if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
576 fprintf(stderr, "unable to set automatic MT mode\n");
577 exit(1);
581 * Enable non-blocking mode and maximum record size checks for
582 * connection oriented transports.
584 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
585 fprintf(stderr, "unable to set RPC max record size\n");
589 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
590 * from being hijacked by a bind to a more specific addr.
592 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
593 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
597 * Set the maximum number of outstanding connection
598 * indications (listen backlog) to the value specified.
600 if (listen_backlog > 0 && !rpc_control(__RPC_SVC_LSTNBKLOG_SET,
601 &listen_backlog)) {
602 fprintf(stderr, "unable to set listen backlog\n");
603 exit(1);
607 * If max_threads was specified, then set the
608 * maximum number of threads to the value specified.
610 if (max_threads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &max_threads)) {
611 fprintf(stderr, "unable to set max_threads\n");
612 exit(1);
616 * Make sure to unregister any previous versions in case the
617 * user is reconfiguring the server in interesting ways.
619 svc_unreg(MOUNTPROG, MOUNTVERS);
620 svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
621 svc_unreg(MOUNTPROG, MOUNTVERS3);
624 * Create the nfsauth thread with same signal disposition
625 * as the main thread. We need to create a separate thread
626 * since mountd() will be both an RPC server (for remote
627 * traffic) _and_ a doors server (for kernel upcalls).
629 if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
630 fprintf(stderr,
631 gettext("Failed to create NFSAUTH svc thread\n"));
632 exit(2);
636 * Create the cmd service thread with same signal disposition
637 * as the main thread. We need to create a separate thread
638 * since mountd() will be both an RPC server (for remote
639 * traffic) _and_ a doors server (for kernel upcalls).
641 if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
642 syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
643 exit(2);
647 * Create an additional thread to service the rmtab and
648 * audit_mountd_mount logging for mount requests. Use the same
649 * signal disposition as the main thread. We create
650 * a separate thread to allow the mount request threads to
651 * clear as soon as possible.
653 if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
654 syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
655 exit(2);
659 * Create datagram and connection oriented services
661 if (mount_vers_max >= MOUNTVERS) {
662 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
663 fprintf(stderr,
664 "couldn't register datagram_v MOUNTVERS\n");
665 exit(1);
667 if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
668 fprintf(stderr,
669 "couldn't register circuit_v MOUNTVERS\n");
670 exit(1);
674 if (mount_vers_max >= MOUNTVERS_POSIX) {
675 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
676 "datagram_v") == 0) {
677 fprintf(stderr,
678 "couldn't register datagram_v MOUNTVERS_POSIX\n");
679 exit(1);
681 if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
682 "circuit_v") == 0) {
683 fprintf(stderr,
684 "couldn't register circuit_v MOUNTVERS_POSIX\n");
685 exit(1);
689 if (mount_vers_max >= MOUNTVERS3) {
690 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
691 fprintf(stderr,
692 "couldn't register datagram_v MOUNTVERS3\n");
693 exit(1);
695 if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
696 fprintf(stderr,
697 "couldn't register circuit_v MOUNTVERS3\n");
698 exit(1);
703 * Start serving
705 rmtab_load();
707 daemonize_fini(pipe_fd);
709 /* Get rid of the most dangerous basic privileges. */
710 __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
711 (char *)NULL);
713 svc_run();
714 syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
715 abort();
717 /* NOTREACHED */
718 return (0);
722 * Server procedure switch routine
724 void
725 mnt(struct svc_req *rqstp, SVCXPRT *transp)
727 switch (rqstp->rq_proc) {
728 case NULLPROC:
729 errno = 0;
730 if (!svc_sendreply(transp, xdr_void, (char *)0))
731 log_cant_reply(transp);
732 return;
734 case MOUNTPROC_MNT:
735 (void) mount(rqstp);
736 return;
738 case MOUNTPROC_DUMP:
739 mntlist_send(transp);
740 return;
742 case MOUNTPROC_UMNT:
743 umount(rqstp);
744 return;
746 case MOUNTPROC_UMNTALL:
747 umountall(rqstp);
748 return;
750 case MOUNTPROC_EXPORT:
751 case MOUNTPROC_EXPORTALL:
752 export(rqstp);
753 return;
755 case MOUNTPROC_PATHCONF:
756 if (rqstp->rq_vers == MOUNTVERS_POSIX)
757 mnt_pathconf(rqstp);
758 else
759 svcerr_noproc(transp);
760 return;
762 default:
763 svcerr_noproc(transp);
764 return;
768 /* Set up anonymous client */
770 struct nd_hostservlist *
771 anon_client(char *host)
773 struct nd_hostservlist *anon_hsl;
774 struct nd_hostserv *anon_hs;
776 anon_hsl = malloc(sizeof (*anon_hsl));
777 if (anon_hsl == NULL)
778 return (NULL);
780 anon_hs = malloc(sizeof (*anon_hs));
781 if (anon_hs == NULL) {
782 free(anon_hsl);
783 return (NULL);
786 if (host == NULL)
787 anon_hs->h_host = strdup("(anon)");
788 else
789 anon_hs->h_host = strdup(host);
791 if (anon_hs->h_host == NULL) {
792 free(anon_hs);
793 free(anon_hsl);
794 return (NULL);
796 anon_hs->h_serv = '\0';
798 anon_hsl->h_cnt = 1;
799 anon_hsl->h_hostservs = anon_hs;
801 return (anon_hsl);
804 static int
805 getclientsnames_common(struct netconfig *nconf, struct netbuf **nbuf,
806 struct nd_hostservlist **serv)
808 char host[MAXIPADDRLEN];
810 assert(*nbuf != NULL);
813 * Use the this API instead of the netdir_getbyaddr()
814 * to avoid service lookup.
816 if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf) != 0) {
817 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
818 struct sockaddr_in *sa;
820 /* LINTED pointer alignment */
821 sa = (struct sockaddr_in *)((*nbuf)->buf);
822 (void) inet_ntoa_r(sa->sin_addr, host);
823 } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
824 struct sockaddr_in6 *sa;
826 /* LINTED pointer alignment */
827 sa = (struct sockaddr_in6 *)((*nbuf)->buf);
828 (void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr,
829 host, INET6_ADDRSTRLEN);
830 } else {
831 syslog(LOG_ERR, gettext(
832 "Client's address is neither IPv4 nor IPv6"));
833 return (EINVAL);
836 *serv = anon_client(host);
837 if (*serv == NULL)
838 return (ENOMEM);
841 assert(*serv != NULL);
842 return (0);
846 * Get the client's hostname from the copy of the
847 * relevant transport handle parts.
848 * If the name is not available then return "(anon)".
850 static int
851 getclientsnames_lazy(char *netid, struct netbuf **nbuf,
852 struct nd_hostservlist **serv)
854 struct netconfig *nconf;
855 int rc;
857 nconf = getnetconfigent(netid);
858 if (nconf == NULL) {
859 syslog(LOG_ERR, "%s: getnetconfigent failed", netid);
860 *serv = anon_client(NULL);
861 if (*serv == NULL)
862 return (ENOMEM);
863 return (0);
866 rc = getclientsnames_common(nconf, nbuf, serv);
867 freenetconfigent(nconf);
868 return (rc);
872 * Get the client's hostname from the transport handle.
873 * If the name is not available then return "(anon)".
876 getclientsnames(SVCXPRT *transp, struct netbuf **nbuf,
877 struct nd_hostservlist **serv)
879 struct netconfig *nconf;
880 int rc;
882 nconf = getnetconfigent(transp->xp_netid);
883 if (nconf == NULL) {
884 syslog(LOG_ERR, "%s: getnetconfigent failed",
885 transp->xp_netid);
886 *serv = anon_client(NULL);
887 if (*serv == NULL)
888 return (ENOMEM);
889 return (0);
892 *nbuf = svc_getrpccaller(transp);
893 if (*nbuf == NULL) {
894 freenetconfigent(nconf);
895 *serv = anon_client(NULL);
896 if (*serv == NULL)
897 return (ENOMEM);
898 return (0);
901 rc = getclientsnames_common(nconf, nbuf, serv);
902 freenetconfigent(nconf);
903 return (rc);
906 void
907 log_cant_reply(SVCXPRT *transp)
909 int saverrno;
910 struct nd_hostservlist *clnames = NULL;
911 char *host;
912 struct netbuf *nb;
914 saverrno = errno; /* save error code */
915 if (getclientsnames(transp, &nb, &clnames) != 0)
916 return;
917 host = clnames->h_hostservs->h_host;
919 errno = saverrno;
920 if (errno == 0)
921 syslog(LOG_ERR, "couldn't send reply to %s", host);
922 else
923 syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
925 netdir_free(clnames, ND_HOSTSERVLIST);
929 * Answer pathconf questions for the mount point fs
931 static void
932 mnt_pathconf(struct svc_req *rqstp)
934 SVCXPRT *transp;
935 struct pathcnf p;
936 char *path, rpath[MAXPATHLEN];
937 struct stat st;
939 transp = rqstp->rq_xprt;
940 path = NULL;
941 (void) memset((caddr_t)&p, 0, sizeof (p));
943 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
944 svcerr_decode(transp);
945 return;
947 if (lstat(path, &st) < 0) {
948 _PC_SET(_PC_ERROR, p.pc_mask);
949 goto done;
952 * Get a path without symbolic links.
954 if (realpath(path, rpath) == NULL) {
955 syslog(LOG_DEBUG,
956 "mount request: realpath failed on %s: %m",
957 path);
958 _PC_SET(_PC_ERROR, p.pc_mask);
959 goto done;
961 (void) memset((caddr_t)&p, 0, sizeof (p));
963 * can't ask about devices over NFS
965 _PC_SET(_PC_MAX_CANON, p.pc_mask);
966 _PC_SET(_PC_MAX_INPUT, p.pc_mask);
967 _PC_SET(_PC_PIPE_BUF, p.pc_mask);
968 _PC_SET(_PC_VDISABLE, p.pc_mask);
970 errno = 0;
971 p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
972 if (errno)
973 _PC_SET(_PC_LINK_MAX, p.pc_mask);
974 p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
975 if (errno)
976 _PC_SET(_PC_NAME_MAX, p.pc_mask);
977 p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
978 if (errno)
979 _PC_SET(_PC_PATH_MAX, p.pc_mask);
980 if (pathconf(rpath, _PC_NO_TRUNC) == 1)
981 _PC_SET(_PC_NO_TRUNC, p.pc_mask);
982 if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
983 _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
985 done:
986 errno = 0;
987 if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
988 log_cant_reply(transp);
989 if (path != NULL)
990 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
994 * If the rootmount (export) option is specified, the all mount requests for
995 * subdirectories return EACCES.
997 static int
998 checkrootmount(share_t *sh, char *rpath)
1000 char *val;
1002 if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
1003 free(val);
1004 if (strcmp(sh->sh_path, rpath) != 0)
1005 return (0);
1006 else
1007 return (1);
1008 } else
1009 return (1);
1012 #define MAX_FLAVORS 128
1015 * Return only EACCES if client does not have access
1016 * to this directory.
1017 * "If the server exports only /a/b, an attempt to
1018 * mount a/b/c will fail with ENOENT if the directory
1019 * does not exist"... However, if the client
1020 * does not have access to /a/b, an attacker can
1021 * determine whether the directory exists.
1022 * This routine checks either existence of the file or
1023 * existence of the file name entry in the mount table.
1024 * If the file exists and there is no file name entry,
1025 * the error returned should be EACCES.
1026 * If the file does not exist, it must be determined
1027 * whether the client has access to a parent
1028 * directory. If the client has access to a parent
1029 * directory, the error returned should be ENOENT,
1030 * otherwise EACCES.
1032 static int
1033 mount_enoent_error(SVCXPRT *transp, char *path, char *rpath,
1034 struct nd_hostservlist **clnames, struct netbuf **nb, int *flavor_list)
1036 char *checkpath, *dp;
1037 share_t *sh = NULL;
1038 int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
1039 int flavor_count;
1041 checkpath = strdup(path);
1042 if (checkpath == NULL) {
1043 syslog(LOG_ERR, "mount_enoent: no memory");
1044 return (EACCES);
1047 /* CONSTCOND */
1048 while (1) {
1049 if (sh) {
1050 sharefree(sh);
1051 sh = NULL;
1054 if ((sh = findentry(rpath)) == NULL &&
1055 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1057 * There is no file name entry.
1058 * If the file (with symbolic links resolved) exists,
1059 * the error returned should be EACCES.
1061 if (realpath_error == 0)
1062 break;
1063 } else if (checkrootmount(sh, rpath) == 0) {
1065 * This is a "nosub" only export, in which case,
1066 * mounting subdirectories isn't allowed.
1067 * If the file (with symbolic links resolved) exists,
1068 * the error returned should be EACCES.
1070 if (realpath_error == 0)
1071 break;
1072 } else {
1074 * Check permissions in mount table.
1076 if (newopts(sh->sh_opts))
1077 flavor_count = getclientsflavors_new(sh,
1078 transp, nb, clnames, flavor_list);
1079 else
1080 flavor_count = getclientsflavors_old(sh,
1081 transp, nb, clnames, flavor_list);
1082 if (flavor_count != 0) {
1084 * Found entry in table and
1085 * client has correct permissions.
1087 reply_error = ENOENT;
1088 break;
1093 * Check all parent directories.
1095 dp = strrchr(checkpath, '/');
1096 if (dp == NULL)
1097 break;
1098 *dp = '\0';
1099 if (strlen(checkpath) == 0)
1100 break;
1102 * Get the real path (no symbolic links in it)
1104 if (realpath(checkpath, rpath) == NULL) {
1105 if (errno != ENOENT)
1106 break;
1107 } else {
1108 realpath_error = 0;
1112 if (sh)
1113 sharefree(sh);
1114 free(checkpath);
1115 return (reply_error);
1119 * We need to inform the caller whether or not we were
1120 * able to add a node to the queue. If we are not, then
1121 * it is up to the caller to go ahead and log the data.
1123 static int
1124 enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
1125 char *rpath, int status, int error)
1127 logging_data *lq;
1128 struct netbuf *nb;
1130 lq = (logging_data *)calloc(1, sizeof (logging_data));
1131 if (lq == NULL)
1132 goto cleanup;
1135 * We might not yet have the host...
1137 if (host) {
1138 DTRACE_PROBE1(mountd, log_host, host);
1139 lq->ld_host = strdup(host);
1140 if (lq->ld_host == NULL)
1141 goto cleanup;
1142 } else {
1143 DTRACE_PROBE(mountd, log_no_host);
1145 lq->ld_netid = strdup(transp->xp_netid);
1146 if (lq->ld_netid == NULL)
1147 goto cleanup;
1149 lq->ld_nb = calloc(1, sizeof (struct netbuf));
1150 if (lq->ld_nb == NULL)
1151 goto cleanup;
1153 nb = svc_getrpccaller(transp);
1154 if (nb == NULL) {
1155 DTRACE_PROBE(mountd, e__nb__enqueue);
1156 goto cleanup;
1159 DTRACE_PROBE(mountd, nb_set_enqueue);
1161 lq->ld_nb->maxlen = nb->maxlen;
1162 lq->ld_nb->len = nb->len;
1164 lq->ld_nb->buf = malloc(lq->ld_nb->len);
1165 if (lq->ld_nb->buf == NULL)
1166 goto cleanup;
1168 bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
1171 lq->ld_path = strdup(path);
1172 if (lq->ld_path == NULL)
1173 goto cleanup;
1175 if (!error) {
1176 lq->ld_rpath = strdup(rpath);
1177 if (lq->ld_rpath == NULL)
1178 goto cleanup;
1181 lq->ld_status = status;
1184 * Add to the tail of the logging queue.
1186 (void) mutex_lock(&logging_queue_lock);
1187 if (logging_tail == NULL) {
1188 logging_tail = logging_head = lq;
1189 } else {
1190 logging_tail->ld_next = lq;
1191 logging_tail = lq;
1193 (void) cond_signal(&logging_queue_cv);
1194 (void) mutex_unlock(&logging_queue_lock);
1196 return (TRUE);
1198 cleanup:
1200 free_logging_data(lq);
1202 return (FALSE);
1206 * Check mount requests, add to mounted list if ok
1208 static int
1209 mount(struct svc_req *rqstp)
1211 SVCXPRT *transp;
1212 int version, vers;
1213 struct fhstatus fhs;
1214 struct mountres3 mountres3;
1215 char fh[FHSIZE3];
1216 int len = FHSIZE3;
1217 char *path, rpath[MAXPATHLEN];
1218 share_t *sh = NULL;
1219 struct nd_hostservlist *clnames = NULL;
1220 char *host = NULL;
1221 int error = 0, lofs_tried = 0, enqueued;
1222 int flavor_list[MAX_FLAVORS];
1223 int flavor_count;
1224 struct netbuf *nb = NULL;
1225 ucred_t *uc = NULL;
1227 int audit_status;
1229 transp = rqstp->rq_xprt;
1230 version = rqstp->rq_vers;
1231 path = NULL;
1233 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
1234 svcerr_decode(transp);
1235 return (EACCES);
1239 * Put off getting the name for the client until we
1240 * need it. This is a performance gain. If we are logging,
1241 * then we don't care about performance and might as well
1242 * get the host name now in case we need to spit out an
1243 * error message.
1245 if (verbose) {
1246 DTRACE_PROBE(mountd, name_by_verbose);
1247 if (getclientsnames(transp, &nb, &clnames) != 0) {
1249 * We failed to get a name for the client, even
1250 * 'anon', probably because we ran out of memory.
1251 * In this situation it doesn't make sense to
1252 * allow the mount to succeed.
1254 error = EACCES;
1255 goto reply;
1257 host = clnames->h_hostservs[0].h_host;
1261 * If the version being used is less than the minimum version,
1262 * the filehandle translation should not be provided to the
1263 * client.
1265 if (rejecting || version < mount_vers_min) {
1266 if (verbose)
1267 syslog(LOG_NOTICE, "Rejected mount: %s for %s",
1268 host, path);
1269 error = EACCES;
1270 goto reply;
1274 * Trusted Extension doesn't support nfsv2. nfsv2 client
1275 * uses MOUNT protocol v1 and v2. To prevent circumventing
1276 * TX label policy via using nfsv2 client, reject a mount
1277 * request with version less than 3 and log an error.
1279 if (is_system_labeled()) {
1280 if (version < 3) {
1281 if (verbose)
1282 syslog(LOG_ERR,
1283 "Rejected mount: TX doesn't support NFSv2");
1284 error = EACCES;
1285 goto reply;
1290 * Get the real path (no symbolic links in it)
1292 if (realpath(path, rpath) == NULL) {
1293 error = errno;
1294 if (verbose)
1295 syslog(LOG_ERR,
1296 "mount request: realpath: %s: %m", path);
1297 if (error == ENOENT)
1298 error = mount_enoent_error(transp, path, rpath,
1299 &clnames, &nb, flavor_list);
1300 goto reply;
1303 if ((sh = findentry(rpath)) == NULL &&
1304 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
1305 error = EACCES;
1306 goto reply;
1310 * Check if this is a "nosub" only export, in which case, mounting
1311 * subdirectories isn't allowed. Bug 1184573.
1313 if (checkrootmount(sh, rpath) == 0) {
1314 error = EACCES;
1315 goto reply;
1318 if (newopts(sh->sh_opts))
1319 flavor_count = getclientsflavors_new(sh, transp, &nb, &clnames,
1320 flavor_list);
1321 else
1322 flavor_count = getclientsflavors_old(sh, transp, &nb, &clnames,
1323 flavor_list);
1325 if (clnames)
1326 host = clnames->h_hostservs[0].h_host;
1328 if (flavor_count == 0) {
1329 error = EACCES;
1330 goto reply;
1334 * Check MAC policy here. The server side policy should be
1335 * consistent with client side mount policy, i.e.
1336 * - we disallow an admin_low unlabeled client to mount
1337 * - we disallow mount from a lower labeled client.
1339 if (is_system_labeled()) {
1340 m_label_t *clabel = NULL;
1341 m_label_t *slabel = NULL;
1342 m_label_t admin_low;
1344 if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
1345 syslog(LOG_ERR,
1346 "mount request: Failed to get caller's ucred : %m");
1347 error = EACCES;
1348 goto reply;
1350 if ((clabel = ucred_getlabel(uc)) == NULL) {
1351 syslog(LOG_ERR,
1352 "mount request: can't get client label from ucred");
1353 error = EACCES;
1354 goto reply;
1357 bsllow(&admin_low);
1358 if (blequal(&admin_low, clabel)) {
1359 struct sockaddr *ca;
1360 tsol_tpent_t *tp;
1362 ca = (struct sockaddr *)(void *)svc_getrpccaller(
1363 rqstp->rq_xprt)->buf;
1364 if (ca == NULL) {
1365 error = EACCES;
1366 goto reply;
1369 * get trusted network template associated
1370 * with the client.
1372 tp = get_client_template(ca);
1373 if (tp == NULL || tp->host_type != SUN_CIPSO) {
1374 if (tp != NULL)
1375 tsol_freetpent(tp);
1376 error = EACCES;
1377 goto reply;
1379 tsol_freetpent(tp);
1380 } else {
1381 if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
1382 error = EACCES;
1383 goto reply;
1386 if (getlabel(rpath, slabel) != 0) {
1387 m_label_free(slabel);
1388 error = EACCES;
1389 goto reply;
1392 if (!bldominates(clabel, slabel)) {
1393 m_label_free(slabel);
1394 error = EACCES;
1395 goto reply;
1397 m_label_free(slabel);
1402 * Now get the filehandle.
1404 * NFS V2 clients get a 32 byte filehandle.
1405 * NFS V3 clients get a 32 or 64 byte filehandle, depending on
1406 * the embedded FIDs.
1408 vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
1410 /* LINTED pointer alignment */
1411 while (nfs_getfh(rpath, vers, &len, fh) < 0) {
1412 if (errno == EINVAL &&
1413 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
1414 errno = 0;
1415 continue;
1417 error = errno == EINVAL ? EACCES : errno;
1418 syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
1419 path);
1420 break;
1423 if (version == MOUNTVERS3) {
1424 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
1425 mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
1426 } else {
1427 bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
1430 reply:
1431 if (uc != NULL)
1432 ucred_free(uc);
1434 switch (version) {
1435 case MOUNTVERS:
1436 case MOUNTVERS_POSIX:
1437 if (error == EINVAL)
1438 fhs.fhs_status = NFSERR_ACCES;
1439 else if (error == EREMOTE)
1440 fhs.fhs_status = NFSERR_REMOTE;
1441 else
1442 fhs.fhs_status = error;
1444 if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
1445 log_cant_reply(transp);
1447 audit_status = fhs.fhs_status;
1448 break;
1450 case MOUNTVERS3:
1451 if (!error) {
1452 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
1453 flavor_list;
1454 mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
1455 flavor_count;
1457 } else if (error == ENAMETOOLONG)
1458 error = MNT3ERR_NAMETOOLONG;
1460 mountres3.fhs_status = error;
1461 if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
1462 log_cant_reply(transp);
1464 audit_status = mountres3.fhs_status;
1465 break;
1468 if (verbose)
1469 syslog(LOG_NOTICE, "MOUNT: %s %s %s",
1470 (host == NULL) ? "unknown host" : host,
1471 error ? "denied" : "mounted", path);
1474 * If we can not create a queue entry, go ahead and do it
1475 * in the context of this thread.
1477 enqueued = enqueue_logging_data(host, transp, path, rpath,
1478 audit_status, error);
1479 if (enqueued == FALSE) {
1480 if (host == NULL) {
1481 DTRACE_PROBE(mountd, name_by_in_thread);
1482 if (getclientsnames(transp, &nb, &clnames) == 0)
1483 host = clnames->h_hostservs[0].h_host;
1486 DTRACE_PROBE(mountd, logged_in_thread);
1487 audit_mountd_mount(host, path, audit_status); /* BSM */
1488 if (!error)
1489 mntlist_new(host, rpath); /* add entry to mount list */
1492 if (path != NULL)
1493 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
1495 if (sh)
1496 sharefree(sh);
1497 netdir_free(clnames, ND_HOSTSERVLIST);
1499 return (error);
1503 * Determine whether two paths are within the same file system.
1504 * Returns nonzero (true) if paths are the same, zero (false) if
1505 * they are different. If an error occurs, return false.
1507 * Use the actual FSID if it's available (via getattrat()); otherwise,
1508 * fall back on st_dev.
1510 * With ZFS snapshots, st_dev differs from the regular file system
1511 * versus the snapshot. But the fsid is the same throughout. Thus
1512 * the fsid is a better test.
1514 static int
1515 same_file_system(const char *path1, const char *path2)
1517 uint64_t fsid1, fsid2;
1518 struct stat64 st1, st2;
1519 nvlist_t *nvl1 = NULL;
1520 nvlist_t *nvl2 = NULL;
1522 if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
1523 (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
1524 (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
1525 (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
1526 nvlist_free(nvl1);
1527 nvlist_free(nvl2);
1529 * We have found fsid's for both paths.
1532 if (fsid1 == fsid2)
1533 return (B_TRUE);
1535 return (B_FALSE);
1538 if (nvl1 != NULL)
1539 nvlist_free(nvl1);
1540 if (nvl2 != NULL)
1541 nvlist_free(nvl2);
1544 * We were unable to find fsid's for at least one of the paths.
1545 * fall back on st_dev.
1548 if (stat64(path1, &st1) < 0) {
1549 syslog(LOG_NOTICE, "%s: %m", path1);
1550 return (B_FALSE);
1552 if (stat64(path2, &st2) < 0) {
1553 syslog(LOG_NOTICE, "%s: %m", path2);
1554 return (B_FALSE);
1557 if (st1.st_dev == st2.st_dev)
1558 return (B_TRUE);
1560 return (B_FALSE);
1563 share_t *
1564 findentry(char *path)
1566 share_t *sh = NULL;
1567 struct sh_list *shp;
1568 char *p1, *p2;
1570 check_sharetab();
1572 (void) rw_rdlock(&sharetab_lock);
1574 for (shp = share_list; shp; shp = shp->shl_next) {
1575 sh = shp->shl_sh;
1576 for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
1577 if (*p1 == '\0')
1578 goto done; /* exact match */
1581 * Now compare the pathnames for three cases:
1583 * Parent: /export/foo (no trailing slash on parent)
1584 * Child: /export/foo/bar
1586 * Parent: /export/foo/ (trailing slash on parent)
1587 * Child: /export/foo/bar
1589 * Parent: /export/foo/ (no trailing slash on child)
1590 * Child: /export/foo
1592 if ((*p1 == '\0' && *p2 == '/') ||
1593 (*p1 == '\0' && *(p1-1) == '/') ||
1594 (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
1596 * We have a subdirectory. Test whether the
1597 * subdirectory is in the same file system.
1599 if (same_file_system(path, sh->sh_path))
1600 goto done;
1603 done:
1604 sh = shp ? sharedup(sh) : NULL;
1606 (void) rw_unlock(&sharetab_lock);
1608 return (sh);
1612 static int
1613 is_substring(char **mntp, char **path)
1615 char *p1 = *mntp, *p2 = *path;
1617 if (*p1 == '\0' && *p2 == '\0') /* exact match */
1618 return (1);
1619 else if (*p1 == '\0' && *p2 == '/')
1620 return (1);
1621 else if (*p1 == '\0' && *(p1-1) == '/') {
1622 *path = --p2; /* we need the slash in p2 */
1623 return (1);
1624 } else if (*p2 == '\0') {
1625 while (*p1 == '/')
1626 p1++;
1627 if (*p1 == '\0') /* exact match */
1628 return (1);
1630 return (0);
1634 * find_lofsentry() searches for the real path which this requested LOFS path
1635 * (rpath) shadows. If found, it will return the sharetab entry of
1636 * the real path that corresponds to the LOFS path.
1637 * We first search mnttab to see if the requested path is an automounted
1638 * path. If it is an automounted path, it will trigger the mount by stat()ing
1639 * the requested path. Note that it is important to check that this path is
1640 * actually an automounted path, otherwise we would stat() a path which may
1641 * turn out to be NFS and block indefinitely on a dead server. The automounter
1642 * times-out if the server is dead, so there's no risk of hanging this
1643 * thread waiting for stat().
1644 * After the mount has been triggered (if necessary), we look for a
1645 * mountpoint of type LOFS (by searching /etc/mnttab again) which
1646 * is a substring of the rpath. If found, we construct a new path by
1647 * concatenating the mnt_special and the remaining of rpath, call findentry()
1648 * to make sure the 'real path' is shared.
1650 static share_t *
1651 find_lofsentry(char *rpath, int *done_flag)
1653 struct stat r_stbuf;
1654 mntlist_t *ml, *mntl, *mntpnt = NULL;
1655 share_t *retcode = NULL;
1656 char tmp_path[MAXPATHLEN];
1657 int mntpnt_len = 0, tmp;
1658 char *p1, *p2;
1660 if ((*done_flag)++)
1661 return (retcode);
1664 * While fsgetmntlist() uses lockf() to
1665 * lock the mnttab before reading it in,
1666 * the lock ignores threads in the same process.
1667 * Read in the mnttab with the protection of a mutex.
1669 (void) mutex_lock(&mnttab_lock);
1670 mntl = fsgetmntlist();
1671 (void) mutex_unlock(&mnttab_lock);
1674 * Obtain the mountpoint for the requested path.
1676 for (ml = mntl; ml; ml = ml->mntl_next) {
1677 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1678 *p1 == *p2 && *p1; p1++, p2++)
1680 if (is_substring(&p1, &p2) &&
1681 (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
1682 mntpnt = ml;
1683 mntpnt_len = tmp;
1688 * If the path needs to be autoFS mounted, trigger the mount by
1689 * stat()ing it. This is determined by checking whether the
1690 * mountpoint we just found is of type autofs.
1692 if (mntpnt != NULL &&
1693 strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
1695 * The requested path is a substring of an autoFS filesystem.
1696 * Trigger the mount.
1698 if (stat(rpath, &r_stbuf) < 0) {
1699 if (verbose)
1700 syslog(LOG_NOTICE, "%s: %m", rpath);
1701 goto done;
1703 if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
1705 * The requested path is a directory, stat(2) it
1706 * again with a trailing '.' to force the autoFS
1707 * module to trigger the mount of indirect
1708 * automount entries, such as /net/jurassic/.
1710 if (strlen(rpath) + 2 > MAXPATHLEN) {
1711 if (verbose) {
1712 syslog(LOG_NOTICE,
1713 "%s/.: exceeds MAXPATHLEN %d",
1714 rpath, MAXPATHLEN);
1716 goto done;
1718 (void) strcpy(tmp_path, rpath);
1719 (void) strcat(tmp_path, "/.");
1721 if (stat(tmp_path, &r_stbuf) < 0) {
1722 if (verbose)
1723 syslog(LOG_NOTICE, "%s: %m", tmp_path);
1724 goto done;
1729 * The mount has been triggered, re-read mnttab to pick up
1730 * the changes made by autoFS.
1732 fsfreemntlist(mntl);
1733 (void) mutex_lock(&mnttab_lock);
1734 mntl = fsgetmntlist();
1735 (void) mutex_unlock(&mnttab_lock);
1739 * The autoFS mountpoint has been triggered if necessary,
1740 * now search mnttab again to determine if the requested path
1741 * is an LOFS mount of a shared path.
1743 mntpnt_len = 0;
1744 for (ml = mntl; ml; ml = ml->mntl_next) {
1745 if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
1746 continue;
1748 for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
1749 *p1 == *p2 && *p1; p1++, p2++)
1752 if (is_substring(&p1, &p2) &&
1753 ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
1754 mntpnt_len = tmp;
1756 if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
1757 MAXPATHLEN) {
1758 if (verbose) {
1759 syslog(LOG_NOTICE, "%s%s: exceeds %d",
1760 ml->mntl_mnt->mnt_special, p2,
1761 MAXPATHLEN);
1763 if (retcode)
1764 sharefree(retcode);
1765 retcode = NULL;
1766 goto done;
1769 (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
1770 (void) strcat(tmp_path, p2);
1771 if (retcode)
1772 sharefree(retcode);
1773 retcode = findentry(tmp_path);
1777 if (retcode) {
1778 assert(strlen(tmp_path) > 0);
1779 (void) strcpy(rpath, tmp_path);
1782 done:
1783 fsfreemntlist(mntl);
1784 return (retcode);
1788 * Determine whether an access list grants rights to a particular host.
1789 * We match on aliases of the hostname as well as on the canonical name.
1790 * Names in the access list may be either hosts or netgroups; they're
1791 * not distinguished syntactically. We check for hosts first because
1792 * it's cheaper, then try netgroups.
1794 * If pnb and pclnames are NULL, it means that we have to use transp
1795 * to resolve client IP address to hostname. If they aren't NULL
1796 * then transp argument won't be used and can be NULL.
1799 in_access_list(SVCXPRT *transp, struct netbuf **pnb,
1800 struct nd_hostservlist **pclnames,
1801 char *access_list) /* N.B. we clobber this "input" parameter */
1803 char addr[INET_ADDRSTRLEN];
1804 char buff[256];
1805 int nentries = 0;
1806 char *cstr = access_list;
1807 char *gr = access_list;
1808 char *host;
1809 int off;
1810 int i;
1811 int response;
1812 int sbr = 0;
1813 struct nd_hostservlist *clnames;
1814 struct netent n, *np;
1816 /* If no access list - then it's unrestricted */
1817 if (access_list == NULL || *access_list == '\0')
1818 return (1);
1820 assert(transp != NULL || (*pnb != NULL && *pclnames != NULL));
1822 /* Get client address if it wasn't provided */
1823 if (*pnb == NULL)
1824 /* Don't grant access if client address isn't known */
1825 if ((*pnb = svc_getrpccaller(transp)) == NULL)
1826 return (0);
1828 /* Try to lookup client hostname if it wasn't provided */
1829 if (*pclnames == NULL)
1830 getclientsnames(transp, pnb, pclnames);
1831 clnames = *pclnames;
1833 for (;;) {
1834 if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
1835 switch (*cstr) {
1836 case '[':
1837 case ']':
1838 sbr = !sbr;
1839 cstr++;
1840 continue;
1841 case ':':
1842 if (sbr) {
1843 cstr++;
1844 continue;
1846 *cstr = '\0';
1851 * If the list name has a '-' prepended then a match of
1852 * the following name implies failure instead of success.
1854 if (*gr == '-') {
1855 response = 0;
1856 gr++;
1857 } else {
1858 response = 1;
1862 * First check if we have '@' entry, as it doesn't
1863 * require client hostname.
1865 if (*gr == '@') {
1866 gr++;
1868 /* Netname support */
1869 if (!isdigit(*gr) && *gr != '[') {
1870 if ((np = getnetbyname_r(gr, &n, buff,
1871 sizeof (buff))) != NULL &&
1872 np->n_net != 0) {
1873 while ((np->n_net & 0xFF000000u) == 0)
1874 np->n_net <<= 8;
1875 np->n_net = htonl(np->n_net);
1876 if (inet_ntop(AF_INET, &np->n_net, addr,
1877 INET_ADDRSTRLEN) == NULL)
1878 break;
1879 if (inet_matchaddr((*pnb)->buf, addr))
1880 return (response);
1882 } else {
1883 if (inet_matchaddr((*pnb)->buf, gr))
1884 return (response);
1887 if (cstr == NULL)
1888 break;
1890 gr = ++cstr;
1892 continue;
1896 * No other checks can be performed if client address
1897 * can't be resolved.
1899 if (clnames == NULL) {
1900 if (cstr == NULL)
1901 break;
1903 gr = ++cstr;
1905 continue;
1908 /* Otherwise loop through all client hostname aliases */
1909 for (i = 0; i < clnames->h_cnt; i++) {
1910 host = clnames->h_hostservs[i].h_host;
1913 * If the list name begins with a dot then
1914 * do a domain name suffix comparison.
1915 * A single dot matches any name with no
1916 * suffix.
1918 if (*gr == '.') {
1919 if (*(gr + 1) == '\0') { /* single dot */
1920 if (strchr(host, '.') == NULL)
1921 return (response);
1922 } else {
1923 off = strlen(host) - strlen(gr);
1924 if (off > 0 &&
1925 strcasecmp(host + off, gr) == 0) {
1926 return (response);
1929 } else {
1930 /* Just do a hostname match */
1931 if (strcasecmp(gr, host) == 0)
1932 return (response);
1936 nentries++;
1938 if (cstr == NULL)
1939 break;
1941 gr = ++cstr;
1944 if (clnames == NULL)
1945 return (0);
1947 return (netgroup_check(clnames, access_list, nentries));
1951 static char *optlist[] = {
1952 #define OPT_RO 0
1953 SHOPT_RO,
1954 #define OPT_RW 1
1955 SHOPT_RW,
1956 #define OPT_ROOT 2
1957 SHOPT_ROOT,
1958 #define OPT_SECURE 3
1959 SHOPT_SECURE,
1960 #define OPT_ANON 4
1961 SHOPT_ANON,
1962 #define OPT_WINDOW 5
1963 SHOPT_WINDOW,
1964 #define OPT_NOSUID 6
1965 SHOPT_NOSUID,
1966 #define OPT_ACLOK 7
1967 SHOPT_ACLOK,
1968 #define OPT_SEC 8
1969 SHOPT_SEC,
1970 #define OPT_NONE 9
1971 SHOPT_NONE,
1972 #define OPT_UIDMAP 10
1973 SHOPT_UIDMAP,
1974 #define OPT_GIDMAP 11
1975 SHOPT_GIDMAP,
1976 NULL
1979 static int
1980 map_flavor(char *str)
1982 seconfig_t sec;
1984 if (nfs_getseconfig_byname(str, &sec))
1985 return (-1);
1987 return (sec.sc_nfsnum);
1991 * If the option string contains a "sec="
1992 * option, then use new option syntax.
1994 static int
1995 newopts(char *opts)
1997 char *head, *p, *val;
1999 if (!opts || *opts == '\0')
2000 return (0);
2002 head = strdup(opts);
2003 if (head == NULL) {
2004 syslog(LOG_ERR, "opts: no memory");
2005 return (0);
2008 p = head;
2009 while (*p) {
2010 if (getsubopt(&p, optlist, &val) == OPT_SEC) {
2011 free(head);
2012 return (1);
2016 free(head);
2017 return (0);
2021 * Given an export and the clients hostname(s)
2022 * determine the security flavors that this
2023 * client is permitted to use.
2025 * This routine is called only for "old" syntax, i.e.
2026 * only one security flavor is allowed. So we need
2027 * to determine two things: the particular flavor,
2028 * and whether the client is allowed to use this
2029 * flavor, i.e. is in the access list.
2031 * Note that if there is no access list, then the
2032 * default is that access is granted.
2034 static int
2035 getclientsflavors_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2036 struct nd_hostservlist **clnames, int *flavors)
2038 char *opts, *p, *val;
2039 boolean_t ok = B_FALSE;
2040 int defaultaccess = 1;
2041 boolean_t reject = B_FALSE;
2043 opts = strdup(sh->sh_opts);
2044 if (opts == NULL) {
2045 syslog(LOG_ERR, "getclientsflavors: no memory");
2046 return (0);
2049 flavors[0] = AUTH_SYS;
2050 p = opts;
2052 while (*p) {
2054 switch (getsubopt(&p, optlist, &val)) {
2055 case OPT_SECURE:
2056 flavors[0] = AUTH_DES;
2057 break;
2059 case OPT_RO:
2060 case OPT_RW:
2061 defaultaccess = 0;
2062 if (in_access_list(transp, nb, clnames, val))
2063 ok++;
2064 break;
2066 case OPT_NONE:
2067 defaultaccess = 0;
2068 if (in_access_list(transp, nb, clnames, val))
2069 reject = B_TRUE;
2073 free(opts);
2075 /* none takes precedence over everything else */
2076 if (reject)
2077 ok = B_TRUE;
2079 return (defaultaccess || ok);
2083 * Given an export and the clients hostname(s)
2084 * determine the security flavors that this
2085 * client is permitted to use.
2087 * This is somewhat more complicated than the "old"
2088 * routine because the options may contain multiple
2089 * security flavors (sec=) each with its own access
2090 * lists. So a client could be granted access based
2091 * on a number of security flavors. Note that the
2092 * type of access might not always be the same, the
2093 * client may get readonly access with one flavor
2094 * and readwrite with another, however the client
2095 * is not told this detail, it gets only the list
2096 * of flavors, and only if the client is using
2097 * version 3 of the mount protocol.
2099 static int
2100 getclientsflavors_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2101 struct nd_hostservlist **clnames, int *flavors)
2103 char *opts, *p, *val;
2104 char *lasts;
2105 char *f;
2106 boolean_t access_ok;
2107 int count, c;
2108 boolean_t reject = B_FALSE;
2110 opts = strdup(sh->sh_opts);
2111 if (opts == NULL) {
2112 syslog(LOG_ERR, "getclientsflavors: no memory");
2113 return (0);
2116 p = opts;
2117 count = c = 0;
2118 /* default access is rw */
2119 access_ok = B_TRUE;
2121 while (*p) {
2122 switch (getsubopt(&p, optlist, &val)) {
2123 case OPT_SEC:
2125 * Before a new sec=xxx option, check if we need
2126 * to move the c index back to the previous count.
2128 if (!access_ok) {
2129 c = count;
2132 /* get all the sec=f1[:f2] flavors */
2133 while ((f = strtok_r(val, ":", &lasts))
2134 != NULL) {
2135 flavors[c++] = map_flavor(f);
2136 val = NULL;
2139 /* for a new sec=xxx option, default is rw access */
2140 access_ok = B_TRUE;
2141 break;
2143 case OPT_RO:
2144 case OPT_RW:
2145 if (in_access_list(transp, nb, clnames, val)) {
2146 count = c;
2147 access_ok = B_TRUE;
2148 } else {
2149 access_ok = B_FALSE;
2151 break;
2153 case OPT_NONE:
2154 if (in_access_list(transp, nb, clnames, val))
2155 reject = B_TRUE; /* none overides rw/ro */
2156 break;
2160 if (reject)
2161 access_ok = B_FALSE;
2163 if (!access_ok)
2164 c = count;
2166 free(opts);
2168 return (c);
2172 * This is a tricky piece of code that parses the
2173 * share options looking for a match on the auth
2174 * flavor that the client is using. If it finds
2175 * a match, then the client is given ro, rw, or
2176 * no access depending whether it is in the access
2177 * list. There is a special case for "secure"
2178 * flavor. Other flavors are values of the new "sec=" option.
2181 check_client(share_t *sh, struct netbuf *nb,
2182 struct nd_hostservlist *clnames, int flavor, uid_t clnt_uid, gid_t clnt_gid,
2183 uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid, gid_t *srv_gid,
2184 uint_t *srv_ngids, gid_t **srv_gids)
2186 if (newopts(sh->sh_opts))
2187 return (check_client_new(sh, NULL, &nb, &clnames, flavor,
2188 clnt_uid, clnt_gid, clnt_ngids, clnt_gids, srv_uid, srv_gid,
2189 srv_ngids, srv_gids));
2190 else
2191 return (check_client_old(sh, NULL, &nb, &clnames, flavor,
2192 clnt_uid, clnt_gid, clnt_ngids, clnt_gids, srv_uid, srv_gid,
2193 srv_ngids, srv_gids));
2196 extern int _getgroupsbymember(const char *, gid_t[], int, int);
2199 * Get supplemental groups for uid
2201 static int
2202 getusergroups(uid_t uid, uint_t *ngrps, gid_t **grps)
2204 struct passwd pwd;
2205 char *pwbuf = alloca(pw_size);
2206 gid_t *tmpgrps = alloca(ngroups_max * sizeof (gid_t));
2207 int tmpngrps;
2209 if (getpwuid_r(uid, &pwd, pwbuf, pw_size) == NULL)
2210 return (-1);
2212 tmpgrps[0] = pwd.pw_gid;
2214 tmpngrps = _getgroupsbymember(pwd.pw_name, tmpgrps, ngroups_max, 1);
2215 if (tmpngrps <= 0) {
2216 syslog(LOG_WARNING,
2217 "getusergroups(): Unable to get groups for user %s",
2218 pwd.pw_name);
2220 return (-1);
2223 *grps = malloc(tmpngrps * sizeof (gid_t));
2224 if (*grps == NULL) {
2225 syslog(LOG_ERR,
2226 "getusergroups(): Memory allocation failed: %m");
2228 return (-1);
2231 *ngrps = tmpngrps;
2232 (void) memcpy(*grps, tmpgrps, tmpngrps * sizeof (gid_t));
2234 return (0);
2238 * is_a_number(number)
2240 * is the string a number in one of the forms we want to use?
2243 static int
2244 is_a_number(char *number)
2246 int ret = 1;
2247 int hex = 0;
2249 if (strncmp(number, "0x", 2) == 0) {
2250 number += 2;
2251 hex = 1;
2252 } else if (*number == '-') {
2253 number++; /* skip the minus */
2255 while (ret == 1 && *number != '\0') {
2256 if (hex) {
2257 ret = isxdigit(*number++);
2258 } else {
2259 ret = isdigit(*number++);
2262 return (ret);
2265 static boolean_t
2266 get_uid(char *value, uid_t *uid)
2268 if (!is_a_number(value)) {
2269 struct passwd *pw;
2271 * in this case it would have to be a
2272 * user name
2274 pw = getpwnam(value);
2275 if (pw == NULL)
2276 return (B_FALSE);
2277 *uid = pw->pw_uid;
2278 endpwent();
2279 } else {
2280 uint64_t intval;
2281 intval = strtoull(value, NULL, 0);
2282 if (intval > UID_MAX && intval != -1)
2283 return (B_FALSE);
2284 *uid = (uid_t)intval;
2287 return (B_TRUE);
2290 static boolean_t
2291 get_gid(char *value, gid_t *gid)
2293 if (!is_a_number(value)) {
2294 struct group *gr;
2296 * in this case it would have to be a
2297 * group name
2299 gr = getgrnam(value);
2300 if (gr == NULL)
2301 return (B_FALSE);
2302 *gid = gr->gr_gid;
2303 endgrent();
2304 } else {
2305 uint64_t intval;
2306 intval = strtoull(value, NULL, 0);
2307 if (intval > UID_MAX && intval != -1)
2308 return (B_FALSE);
2309 *gid = (gid_t)intval;
2312 return (B_TRUE);
2315 static int
2316 check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2317 struct nd_hostservlist **clnames, int flavor, uid_t clnt_uid,
2318 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2319 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2321 char *opts, *p, *val;
2322 int match; /* Set when a flavor is matched */
2323 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2324 int list = 0; /* Set when "ro", "rw" is found */
2325 int ro_val = 0; /* Set if ro option is 'ro=' */
2326 int rw_val = 0; /* Set if rw option is 'rw=' */
2328 boolean_t map_deny = B_FALSE;
2330 opts = strdup(sh->sh_opts);
2331 if (opts == NULL) {
2332 syslog(LOG_ERR, "check_client: no memory");
2333 return (0);
2337 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2338 * locally for all of them
2340 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2341 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2342 perm |= NFSAUTH_GROUPS;
2344 p = opts;
2345 match = AUTH_UNIX;
2347 while (*p) {
2348 switch (getsubopt(&p, optlist, &val)) {
2350 case OPT_SECURE:
2351 match = AUTH_DES;
2353 if (perm & NFSAUTH_GROUPS) {
2354 free(*srv_gids);
2355 *srv_ngids = 0;
2356 *srv_gids = NULL;
2357 perm &= ~NFSAUTH_GROUPS;
2360 break;
2362 case OPT_RO:
2363 list++;
2364 if (val != NULL)
2365 ro_val++;
2366 if (in_access_list(transp, nb, clnames, val))
2367 perm |= NFSAUTH_RO;
2368 break;
2370 case OPT_RW:
2371 list++;
2372 if (val != NULL)
2373 rw_val++;
2374 if (in_access_list(transp, nb, clnames, val))
2375 perm |= NFSAUTH_RW;
2376 break;
2378 case OPT_ROOT:
2380 * Check if the client is in
2381 * the root list. Only valid
2382 * for AUTH_SYS.
2384 if (flavor != AUTH_SYS)
2385 break;
2387 if (val == NULL || *val == '\0')
2388 break;
2390 if (clnt_uid != 0)
2391 break;
2393 if (in_access_list(transp, nb, clnames, val)) {
2394 perm |= NFSAUTH_ROOT;
2395 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2396 map_deny = B_FALSE;
2398 if (perm & NFSAUTH_GROUPS) {
2399 free(*srv_gids);
2400 *srv_ngids = 0;
2401 *srv_gids = NULL;
2402 perm &= ~NFSAUTH_GROUPS;
2405 break;
2407 case OPT_NONE:
2409 * Check if the client should have no access
2410 * to this share at all. This option behaves
2411 * more like "root" than either "rw" or "ro".
2413 if (in_access_list(transp, nb, clnames, val))
2414 perm |= NFSAUTH_DENIED;
2415 break;
2417 case OPT_UIDMAP: {
2418 char *c;
2419 char *n;
2422 * The uidmap is supported for AUTH_SYS only.
2424 if (flavor != AUTH_SYS)
2425 break;
2427 if (perm & NFSAUTH_UIDMAP || map_deny)
2428 break;
2430 for (c = val; c != NULL; c = n) {
2431 char *s;
2432 char *al;
2433 uid_t srv;
2435 n = strchr(c, '~');
2436 if (n != NULL)
2437 *n++ = '\0';
2439 s = strchr(c, ':');
2440 if (s != NULL) {
2441 *s++ = '\0';
2442 al = strchr(s, ':');
2443 if (al != NULL)
2444 *al++ = '\0';
2447 if (s == NULL || al == NULL)
2448 continue;
2450 if (*c == '\0') {
2451 if (clnt_uid != (uid_t)-1)
2452 continue;
2453 } else if (strcmp(c, "*") != 0) {
2454 uid_t clnt;
2456 if (!get_uid(c, &clnt))
2457 continue;
2459 if (clnt_uid != clnt)
2460 continue;
2463 if (*s == '\0')
2464 srv = UID_NOBODY;
2465 else if (!get_uid(s, &srv))
2466 continue;
2467 else if (srv == (uid_t)-1) {
2468 map_deny = B_TRUE;
2469 break;
2472 if (in_access_list(transp, nb, clnames, al)) {
2473 *srv_uid = srv;
2474 perm |= NFSAUTH_UIDMAP;
2476 if (perm & NFSAUTH_GROUPS) {
2477 free(*srv_gids);
2478 *srv_ngids = 0;
2479 *srv_gids = NULL;
2480 perm &= ~NFSAUTH_GROUPS;
2483 break;
2487 break;
2490 case OPT_GIDMAP: {
2491 char *c;
2492 char *n;
2495 * The gidmap is supported for AUTH_SYS only.
2497 if (flavor != AUTH_SYS)
2498 break;
2500 if (perm & NFSAUTH_GIDMAP || map_deny)
2501 break;
2503 for (c = val; c != NULL; c = n) {
2504 char *s;
2505 char *al;
2506 gid_t srv;
2508 n = strchr(c, '~');
2509 if (n != NULL)
2510 *n++ = '\0';
2512 s = strchr(c, ':');
2513 if (s != NULL) {
2514 *s++ = '\0';
2515 al = strchr(s, ':');
2516 if (al != NULL)
2517 *al++ = '\0';
2520 if (s == NULL || al == NULL)
2521 break;
2523 if (*c == '\0') {
2524 if (clnt_gid != (gid_t)-1)
2525 continue;
2526 } else if (strcmp(c, "*") != 0) {
2527 gid_t clnt;
2529 if (!get_gid(c, &clnt))
2530 continue;
2532 if (clnt_gid != clnt)
2533 continue;
2536 if (*s == '\0')
2537 srv = UID_NOBODY;
2538 else if (!get_gid(s, &srv))
2539 continue;
2540 else if (srv == (gid_t)-1) {
2541 map_deny = B_TRUE;
2542 break;
2545 if (in_access_list(transp, nb, clnames, al)) {
2546 *srv_gid = srv;
2547 perm |= NFSAUTH_GIDMAP;
2549 if (perm & NFSAUTH_GROUPS) {
2550 free(*srv_gids);
2551 *srv_ngids = 0;
2552 *srv_gids = NULL;
2553 perm &= ~NFSAUTH_GROUPS;
2556 break;
2560 break;
2563 default:
2564 break;
2568 free(opts);
2570 if (perm & NFSAUTH_ROOT) {
2571 *srv_uid = 0;
2572 *srv_gid = 0;
2575 if (map_deny)
2576 perm |= NFSAUTH_DENIED;
2578 if (!(perm & NFSAUTH_UIDMAP))
2579 *srv_uid = clnt_uid;
2580 if (!(perm & NFSAUTH_GIDMAP))
2581 *srv_gid = clnt_gid;
2583 if (flavor != match || perm & NFSAUTH_DENIED)
2584 return (NFSAUTH_DENIED);
2586 if (list) {
2588 * If the client doesn't match an "ro" or "rw"
2589 * list then set no access.
2591 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
2592 perm |= NFSAUTH_DENIED;
2593 } else {
2595 * The client matched a flavor entry that
2596 * has no explicit "rw" or "ro" determination.
2597 * Default it to "rw".
2599 perm |= NFSAUTH_RW;
2603 * The client may show up in both ro= and rw=
2604 * lists. If so, then turn off the RO access
2605 * bit leaving RW access.
2607 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2609 * Logically cover all permutations of rw=,ro=.
2610 * In the case where, rw,ro=<host> we would like
2611 * to remove RW access for the host. In all other cases
2612 * RW wins the precedence battle.
2614 if (!rw_val && ro_val) {
2615 perm &= ~(NFSAUTH_RW);
2616 } else {
2617 perm &= ~(NFSAUTH_RO);
2621 return (perm);
2625 * Check if the client has access by using a flavor different from
2626 * the given "flavor". If "flavor" is not in the flavor list,
2627 * return TRUE to indicate that this "flavor" is a wrong sec.
2629 static bool_t
2630 is_wrongsec(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2631 struct nd_hostservlist **clnames, int flavor)
2633 int flavor_list[MAX_FLAVORS];
2634 int flavor_count, i;
2636 /* get the flavor list that the client has access with */
2637 flavor_count = getclientsflavors_new(sh, transp, nb,
2638 clnames, flavor_list);
2640 if (flavor_count == 0)
2641 return (FALSE);
2644 * Check if the given "flavor" is in the flavor_list.
2646 for (i = 0; i < flavor_count; i++) {
2647 if (flavor == flavor_list[i])
2648 return (FALSE);
2652 * If "flavor" is not in the flavor_list, return TRUE to indicate
2653 * that the client should have access by using a security flavor
2654 * different from this "flavor".
2656 return (TRUE);
2660 * Given an export and the client's hostname, we
2661 * check the security options to see whether the
2662 * client is allowed to use the given security flavor.
2664 * The strategy is to proceed through the options looking
2665 * for a flavor match, then pay attention to the ro, rw,
2666 * and root options.
2668 * Note that an entry may list several flavors in a
2669 * single entry, e.g.
2671 * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
2675 static int
2676 check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
2677 struct nd_hostservlist **clnames, int flavor, uid_t clnt_uid,
2678 gid_t clnt_gid, uint_t clnt_ngids, gid_t *clnt_gids, uid_t *srv_uid,
2679 gid_t *srv_gid, uint_t *srv_ngids, gid_t **srv_gids)
2681 char *opts, *p, *val;
2682 char *lasts;
2683 char *f;
2684 int match = 0; /* Set when a flavor is matched */
2685 int perm = 0; /* Set when "ro", "rw" or "root" is matched */
2686 int list = 0; /* Set when "ro", "rw" is found */
2687 int ro_val = 0; /* Set if ro option is 'ro=' */
2688 int rw_val = 0; /* Set if rw option is 'rw=' */
2690 boolean_t map_deny = B_FALSE;
2692 opts = strdup(sh->sh_opts);
2693 if (opts == NULL) {
2694 syslog(LOG_ERR, "check_client: no memory");
2695 return (0);
2699 * If client provided 16 supplemental groups with AUTH_SYS, lookup
2700 * locally for all of them
2702 if (flavor == AUTH_SYS && clnt_ngids == NGRPS && ngroups_max > NGRPS)
2703 if (getusergroups(clnt_uid, srv_ngids, srv_gids) == 0)
2704 perm |= NFSAUTH_GROUPS;
2706 p = opts;
2708 while (*p) {
2709 switch (getsubopt(&p, optlist, &val)) {
2711 case OPT_SEC:
2712 if (match)
2713 goto done;
2715 while ((f = strtok_r(val, ":", &lasts))
2716 != NULL) {
2717 if (flavor == map_flavor(f)) {
2718 match = 1;
2719 break;
2721 val = NULL;
2723 break;
2725 case OPT_RO:
2726 if (!match)
2727 break;
2729 list++;
2730 if (val != NULL)
2731 ro_val++;
2732 if (in_access_list(transp, nb, clnames, val))
2733 perm |= NFSAUTH_RO;
2734 break;
2736 case OPT_RW:
2737 if (!match)
2738 break;
2740 list++;
2741 if (val != NULL)
2742 rw_val++;
2743 if (in_access_list(transp, nb, clnames, val))
2744 perm |= NFSAUTH_RW;
2745 break;
2747 case OPT_ROOT:
2749 * Check if the client is in
2750 * the root list. Only valid
2751 * for AUTH_SYS.
2753 if (flavor != AUTH_SYS)
2754 break;
2756 if (!match)
2757 break;
2759 if (val == NULL || *val == '\0')
2760 break;
2762 if (clnt_uid != 0)
2763 break;
2765 if (in_access_list(transp, nb, clnames, val)) {
2766 perm |= NFSAUTH_ROOT;
2767 perm |= NFSAUTH_UIDMAP | NFSAUTH_GIDMAP;
2768 map_deny = B_FALSE;
2770 if (perm & NFSAUTH_GROUPS) {
2771 free(*srv_gids);
2772 *srv_gids = NULL;
2773 *srv_ngids = 0;
2774 perm &= ~NFSAUTH_GROUPS;
2777 break;
2779 case OPT_NONE:
2781 * Check if the client should have no access
2782 * to this share at all. This option behaves
2783 * more like "root" than either "rw" or "ro".
2785 if (in_access_list(transp, nb, clnames, val))
2786 perm |= NFSAUTH_DENIED;
2787 break;
2789 case OPT_UIDMAP: {
2790 char *c;
2791 char *n;
2794 * The uidmap is supported for AUTH_SYS only.
2796 if (flavor != AUTH_SYS)
2797 break;
2799 if (!match || perm & NFSAUTH_UIDMAP || map_deny)
2800 break;
2802 for (c = val; c != NULL; c = n) {
2803 char *s;
2804 char *al;
2805 uid_t srv;
2807 n = strchr(c, '~');
2808 if (n != NULL)
2809 *n++ = '\0';
2811 s = strchr(c, ':');
2812 if (s != NULL) {
2813 *s++ = '\0';
2814 al = strchr(s, ':');
2815 if (al != NULL)
2816 *al++ = '\0';
2819 if (s == NULL || al == NULL)
2820 continue;
2822 if (*c == '\0') {
2823 if (clnt_uid != (uid_t)-1)
2824 continue;
2825 } else if (strcmp(c, "*") != 0) {
2826 uid_t clnt;
2828 if (!get_uid(c, &clnt))
2829 continue;
2831 if (clnt_uid != clnt)
2832 continue;
2835 if (*s == '\0')
2836 srv = UID_NOBODY;
2837 else if (!get_uid(s, &srv))
2838 continue;
2839 else if (srv == (uid_t)-1) {
2840 map_deny = B_TRUE;
2841 break;
2844 if (in_access_list(transp, nb, clnames, al)) {
2845 *srv_uid = srv;
2846 perm |= NFSAUTH_UIDMAP;
2848 if (perm & NFSAUTH_GROUPS) {
2849 free(*srv_gids);
2850 *srv_gids = NULL;
2851 *srv_ngids = 0;
2852 perm &= ~NFSAUTH_GROUPS;
2855 break;
2859 break;
2862 case OPT_GIDMAP: {
2863 char *c;
2864 char *n;
2867 * The gidmap is supported for AUTH_SYS only.
2869 if (flavor != AUTH_SYS)
2870 break;
2872 if (!match || perm & NFSAUTH_GIDMAP || map_deny)
2873 break;
2875 for (c = val; c != NULL; c = n) {
2876 char *s;
2877 char *al;
2878 gid_t srv;
2880 n = strchr(c, '~');
2881 if (n != NULL)
2882 *n++ = '\0';
2884 s = strchr(c, ':');
2885 if (s != NULL) {
2886 *s++ = '\0';
2887 al = strchr(s, ':');
2888 if (al != NULL)
2889 *al++ = '\0';
2892 if (s == NULL || al == NULL)
2893 break;
2895 if (*c == '\0') {
2896 if (clnt_gid != (gid_t)-1)
2897 continue;
2898 } else if (strcmp(c, "*") != 0) {
2899 gid_t clnt;
2901 if (!get_gid(c, &clnt))
2902 continue;
2904 if (clnt_gid != clnt)
2905 continue;
2908 if (*s == '\0')
2909 srv = UID_NOBODY;
2910 else if (!get_gid(s, &srv))
2911 continue;
2912 else if (srv == (gid_t)-1) {
2913 map_deny = B_TRUE;
2914 break;
2917 if (in_access_list(transp, nb, clnames, al)) {
2918 *srv_gid = srv;
2919 perm |= NFSAUTH_GIDMAP;
2921 if (perm & NFSAUTH_GROUPS) {
2922 free(*srv_gids);
2923 *srv_gids = NULL;
2924 *srv_ngids = 0;
2925 perm &= ~NFSAUTH_GROUPS;
2928 break;
2932 break;
2935 default:
2936 break;
2940 done:
2941 if (perm & NFSAUTH_ROOT) {
2942 *srv_uid = 0;
2943 *srv_gid = 0;
2946 if (map_deny)
2947 perm |= NFSAUTH_DENIED;
2949 if (!(perm & NFSAUTH_UIDMAP))
2950 *srv_uid = clnt_uid;
2951 if (!(perm & NFSAUTH_GIDMAP))
2952 *srv_gid = clnt_gid;
2955 * If no match then set the perm accordingly
2957 if (!match || perm & NFSAUTH_DENIED) {
2958 free(opts);
2959 return (NFSAUTH_DENIED);
2962 if (list) {
2964 * If the client doesn't match an "ro" or "rw" list then
2965 * check if it may have access by using a different flavor.
2966 * If so, return NFSAUTH_WRONGSEC.
2967 * If not, return NFSAUTH_DENIED.
2969 if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
2970 if (is_wrongsec(sh, transp, nb, clnames, flavor))
2971 perm |= NFSAUTH_WRONGSEC;
2972 else
2973 perm |= NFSAUTH_DENIED;
2975 } else {
2977 * The client matched a flavor entry that
2978 * has no explicit "rw" or "ro" determination.
2979 * Make sure it defaults to "rw".
2981 perm |= NFSAUTH_RW;
2985 * The client may show up in both ro= and rw=
2986 * lists. If so, then turn off the RO access
2987 * bit leaving RW access.
2989 if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
2991 * Logically cover all permutations of rw=,ro=.
2992 * In the case where, rw,ro=<host> we would like
2993 * to remove RW access for the host. In all other cases
2994 * RW wins the precedence battle.
2996 if (!rw_val && ro_val) {
2997 perm &= ~(NFSAUTH_RW);
2998 } else {
2999 perm &= ~(NFSAUTH_RO);
3003 free(opts);
3005 return (perm);
3008 void
3009 check_sharetab()
3011 FILE *f;
3012 struct stat st;
3013 static timestruc_t last_sharetab_time;
3014 timestruc_t prev_sharetab_time;
3015 share_t *sh;
3016 struct sh_list *shp, *shp_prev;
3017 int res, c = 0;
3020 * read in /etc/dfs/sharetab if it has changed
3022 if (stat(SHARETAB, &st) != 0) {
3023 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3024 return;
3027 if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
3028 st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
3030 * No change.
3032 return;
3036 * Remember the mod time, then after getting the
3037 * write lock check again. If another thread
3038 * already did the update, then there's no
3039 * work to do.
3041 prev_sharetab_time = last_sharetab_time;
3043 (void) rw_wrlock(&sharetab_lock);
3045 if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
3046 prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
3047 (void) rw_unlock(&sharetab_lock);
3048 return;
3052 * Note that since the sharetab is now in memory
3053 * and a snapshot is taken, we no longer have to
3054 * lock the file.
3056 f = fopen(SHARETAB, "r");
3057 if (f == NULL) {
3058 syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
3059 (void) rw_unlock(&sharetab_lock);
3060 return;
3064 * Once we are sure /etc/dfs/sharetab has been
3065 * modified, flush netgroup cache entries.
3067 netgrp_cache_flush();
3069 sh_free(share_list); /* free old list */
3070 share_list = NULL;
3072 while ((res = getshare(f, &sh)) > 0) {
3073 c++;
3074 if (strcmp(sh->sh_fstype, "nfs") != 0)
3075 continue;
3077 shp = malloc(sizeof (*shp));
3078 if (shp == NULL)
3079 goto alloc_failed;
3080 if (share_list == NULL)
3081 share_list = shp;
3082 else
3083 /* LINTED not used before set */
3084 shp_prev->shl_next = shp;
3085 shp_prev = shp;
3086 shp->shl_next = NULL;
3087 shp->shl_sh = sharedup(sh);
3088 if (shp->shl_sh == NULL)
3089 goto alloc_failed;
3092 if (res < 0)
3093 syslog(LOG_ERR, "%s: invalid at line %d\n",
3094 SHARETAB, c + 1);
3096 if (stat(SHARETAB, &st) != 0) {
3097 syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
3098 (void) fclose(f);
3099 (void) rw_unlock(&sharetab_lock);
3100 return;
3103 last_sharetab_time = st.st_mtim;
3104 (void) fclose(f);
3105 (void) rw_unlock(&sharetab_lock);
3107 return;
3109 alloc_failed:
3111 syslog(LOG_ERR, "check_sharetab: no memory");
3112 sh_free(share_list);
3113 share_list = NULL;
3114 (void) fclose(f);
3115 (void) rw_unlock(&sharetab_lock);
3118 static void
3119 sh_free(struct sh_list *shp)
3121 struct sh_list *next;
3123 while (shp) {
3124 sharefree(shp->shl_sh);
3125 next = shp->shl_next;
3126 free(shp);
3127 shp = next;
3133 * Remove an entry from mounted list
3135 static void
3136 umount(struct svc_req *rqstp)
3138 char *host, *path, *remove_path;
3139 char rpath[MAXPATHLEN];
3140 struct nd_hostservlist *clnames = NULL;
3141 SVCXPRT *transp;
3142 struct netbuf *nb;
3144 transp = rqstp->rq_xprt;
3145 path = NULL;
3146 if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
3147 svcerr_decode(transp);
3148 return;
3150 errno = 0;
3151 if (!svc_sendreply(transp, xdr_void, (char *)NULL))
3152 log_cant_reply(transp);
3154 if (getclientsnames(transp, &nb, &clnames) != 0) {
3156 * Without the hostname we can't do audit or delete
3157 * this host from the mount entries.
3159 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3160 return;
3162 host = clnames->h_hostservs[0].h_host;
3164 if (verbose)
3165 syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
3167 audit_mountd_umount(host, path);
3169 remove_path = rpath; /* assume we will use the cannonical path */
3170 if (realpath(path, rpath) == NULL) {
3171 if (verbose)
3172 syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
3173 remove_path = path; /* use path provided instead */
3176 mntlist_delete(host, remove_path); /* remove from mount list */
3178 svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
3179 netdir_free(clnames, ND_HOSTSERVLIST);
3183 * Remove all entries for one machine from mounted list
3185 static void
3186 umountall(struct svc_req *rqstp)
3188 struct nd_hostservlist *clnames = NULL;
3189 SVCXPRT *transp;
3190 char *host;
3191 struct netbuf *nb;
3193 transp = rqstp->rq_xprt;
3194 if (!svc_getargs(transp, xdr_void, NULL)) {
3195 svcerr_decode(transp);
3196 return;
3199 * We assume that this call is asynchronous and made via rpcbind
3200 * callit routine. Therefore return control immediately. The error
3201 * causes rpcbind to remain silent, as opposed to every machine
3202 * on the net blasting the requester with a response.
3204 svcerr_systemerr(transp);
3205 if (getclientsnames(transp, &nb, &clnames) != 0) {
3206 /* Can't do anything without the name of the client */
3207 return;
3210 host = clnames->h_hostservs[0].h_host;
3213 * Remove all hosts entries from mount list
3215 mntlist_delete_all(host);
3217 if (verbose)
3218 syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
3220 netdir_free(clnames, ND_HOSTSERVLIST);
3223 void *
3224 exmalloc(size_t size)
3226 void *ret;
3228 if ((ret = malloc(size)) == NULL) {
3229 syslog(LOG_ERR, "Out of memory");
3230 exit(1);
3232 return (ret);
3235 static tsol_tpent_t *
3236 get_client_template(struct sockaddr *sock)
3238 in_addr_t v4client;
3239 in6_addr_t v6client;
3240 char v4_addr[INET_ADDRSTRLEN];
3241 char v6_addr[INET6_ADDRSTRLEN];
3242 tsol_rhent_t *rh;
3243 tsol_tpent_t *tp;
3245 switch (sock->sa_family) {
3246 case AF_INET:
3247 v4client = ((struct sockaddr_in *)(void *)sock)->
3248 sin_addr.s_addr;
3249 if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
3250 NULL)
3251 return (NULL);
3252 rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
3253 if (rh == NULL)
3254 return (NULL);
3255 tp = tsol_gettpbyname(rh->rh_template);
3256 tsol_freerhent(rh);
3257 return (tp);
3258 break;
3259 case AF_INET6:
3260 v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
3261 if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
3262 NULL)
3263 return (NULL);
3264 rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
3265 if (rh == NULL)
3266 return (NULL);
3267 tp = tsol_gettpbyname(rh->rh_template);
3268 tsol_freerhent(rh);
3269 return (tp);
3270 break;
3271 default:
3272 return (NULL);