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]
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.
36 #include <stdio_ext.h>
39 #include <sys/types.h>
42 #include <sys/param.h>
45 #include <netconfig.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>
55 #include <sys/resource.h>
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
66 #include <priv_utils.h>
68 #include <nfs/nfssys.h>
70 #include <nfs/nfs_sec.h>
71 #include <rpcsvc/daemon_utils.h>
73 #include "../../fslib.h"
74 #include <sharefs/share.h>
75 #include <sharefs/sharetab.h>
76 #include "../lib/sharetab.h"
78 #include <tsol/label.h>
79 #include <sys/tsol/label_macro.h>
84 #include <sys/nvpair.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
*);
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
;
140 thread_t logging_thread
;
142 typedef struct logging_data
{
148 struct netbuf
*ld_nb
;
149 struct logging_data
*ld_next
;
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 */
163 nfsauth_svc(void *arg
)
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");
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
);
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");
199 (void) close(doorfd
);
206 * Must pass the doorfd down to the kernel.
209 (void) _nfssys(MOUNTD_ARGS
, &darg
);
212 * Wait for incoming calls
219 syslog(LOG_ERR
, gettext("Door server exited"));
224 * NFS command service thread code for setup and handling of the
225 * nfs_cmd requests for character set conversion and other future
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");
242 * Must pass the doorfd down to the kernel.
245 (void) _nfssys(NFSCMD_ARGS
, &darg
);
248 * Wait for incoming calls
255 syslog(LOG_ERR
, gettext("Cmd door server exited"));
260 free_logging_data(logging_data
*lq
)
266 if (lq
->ld_nb
!= NULL
) {
267 free(lq
->ld_nb
->buf
);
278 static logging_data
*
279 remove_head_of_queue(void)
284 * Pull it off the queue.
288 logging_head
= lq
->ld_next
;
293 if (logging_head
== NULL
) {
302 do_logging_queue(logging_data
*lq
)
307 struct nd_hostservlist
*clnames
;
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)
316 host
= clnames
->h_hostservs
[0].h_host
;
320 audit_mountd_mount(host
, lq
->ld_path
, lq
->ld_status
); /* BSM */
322 /* add entry to mount list */
324 mntlist_new(host
, lq
->ld_rpath
);
326 if (lq
->ld_host
!= host
)
327 netdir_free(clnames
, ND_HOSTSERVLIST
);
329 free_logging_data(lq
);
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
);
341 logging_svc(void *arg
)
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
);
359 syslog(LOG_ERR
, gettext("Logging server exited"));
364 convert_int(int *val
, char *str
)
368 if (str
== NULL
|| !isdigit(*str
))
371 lval
= strtol(str
, &str
, 10);
372 if (*str
!= '\0' || lval
> INT_MAX
)
380 main(int argc
, char *argv
[])
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
;
389 long thr_flags
= (THR_NEW_LWP
|THR_DAEMON
);
391 int defvers
, ret
, bufsz
;
393 int listen_backlog
= 0;
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)
408 * file dac search (so it can stat all files)
409 * Optional privileges:
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",
422 if (getrlimit(RLIMIT_NOFILE
, &rl
) != 0) {
423 syslog(LOG_ERR
, "getrlimit failed");
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
);
435 syslog(LOG_ERR
, "Reading of mountd_max_threads from SMF "
436 "failed, using default value");
439 while ((c
= getopt(argc
, argv
, "vrm:")) != EOF
) {
448 if (convert_int(&tmp
, optarg
) != 0 || tmp
< 1) {
449 (void) fprintf(stderr
, "%s: invalid "
450 "max_threads option, using defaults\n",
457 fprintf(stderr
, "usage: mountd [-v] [-r]\n");
463 * Read in the NFS version values from config file.
466 ret
= nfs_smf_get_prop("server_versmin", defval
, DEFAULT_INSTANCE
,
467 SCF_TYPE_INTEGER
, NFSD
, &bufsz
);
470 defvers
= strtol(defval
, (char **)NULL
, 10);
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
;
483 ret
= nfs_smf_get_prop("server_versmax", defval
, DEFAULT_INSTANCE
,
484 SCF_TYPE_INTEGER
, NFSD
, &bufsz
);
487 defvers
= strtol(defval
, (char **)NULL
, 10);
489 mount_vers_max
= defvers
;
493 ret
= nfs_smf_get_iprop("mountd_listen_backlog", &listen_backlog
,
494 DEFAULT_INSTANCE
, SCF_TYPE_INTEGER
, NFSD
);
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
);
517 #if !defined(TEXT_DOMAIN)
518 #define TEXT_DOMAIN "SYS_TEST"
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
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
);
545 fprintf(stderr
, "error locking for %s: %s\n", MOUNTD
,
549 /* daemon was already running */
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");
562 if ((pw_size
= sysconf(_SC_GETPW_R_SIZE_MAX
)) == -1) {
563 syslog(LOG_ERR
, "Unable to get _SC_GETPW_R_SIZE_MAX");
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");
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
,
605 fprintf(stderr
, "unable to set listen backlog\n");
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");
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
)) {
634 gettext("Failed to create NFSAUTH svc thread\n"));
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"));
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"));
662 * Create datagram and connection oriented services
664 if (mount_vers_max
>= MOUNTVERS
) {
665 if (svc_create(mnt
, MOUNTPROG
, MOUNTVERS
, "datagram_v") == 0) {
667 "couldn't register datagram_v MOUNTVERS\n");
670 if (svc_create(mnt
, MOUNTPROG
, MOUNTVERS
, "circuit_v") == 0) {
672 "couldn't register circuit_v MOUNTVERS\n");
677 if (mount_vers_max
>= MOUNTVERS_POSIX
) {
678 if (svc_create(mnt
, MOUNTPROG
, MOUNTVERS_POSIX
,
679 "datagram_v") == 0) {
681 "couldn't register datagram_v MOUNTVERS_POSIX\n");
684 if (svc_create(mnt
, MOUNTPROG
, MOUNTVERS_POSIX
,
687 "couldn't register circuit_v MOUNTVERS_POSIX\n");
692 if (mount_vers_max
>= MOUNTVERS3
) {
693 if (svc_create(mnt
, MOUNTPROG
, MOUNTVERS3
, "datagram_v") == 0) {
695 "couldn't register datagram_v MOUNTVERS3\n");
698 if (svc_create(mnt
, MOUNTPROG
, MOUNTVERS3
, "circuit_v") == 0) {
700 "couldn't register circuit_v MOUNTVERS3\n");
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
,
717 syslog(LOG_ERR
, "Error: svc_run shouldn't have returned");
725 * Server procedure switch routine
728 mnt(struct svc_req
*rqstp
, SVCXPRT
*transp
)
730 switch (rqstp
->rq_proc
) {
733 if (!svc_sendreply(transp
, xdr_void
, (char *)0))
734 log_cant_reply(transp
);
742 mntlist_send(transp
);
749 case MOUNTPROC_UMNTALL
:
753 case MOUNTPROC_EXPORT
:
754 case MOUNTPROC_EXPORTALL
:
758 case MOUNTPROC_PATHCONF
:
759 if (rqstp
->rq_vers
== MOUNTVERS_POSIX
)
762 svcerr_noproc(transp
);
766 svcerr_noproc(transp
);
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
)
783 anon_hs
= malloc(sizeof (*anon_hs
));
784 if (anon_hs
== NULL
) {
790 anon_hs
->h_host
= strdup("(anon)");
792 anon_hs
->h_host
= strdup(host
);
794 if (anon_hs
->h_host
== NULL
) {
799 anon_hs
->h_serv
= '\0';
802 anon_hsl
->h_hostservs
= anon_hs
;
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
);
834 syslog(LOG_ERR
, gettext(
835 "Client's address is neither IPv4 nor IPv6"));
839 *serv
= anon_client(host
);
844 assert(*serv
!= NULL
);
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)".
854 getclientsnames_lazy(char *netid
, struct netbuf
**nbuf
,
855 struct nd_hostservlist
**serv
)
857 struct netconfig
*nconf
;
860 nconf
= getnetconfigent(netid
);
862 syslog(LOG_ERR
, "%s: getnetconfigent failed", netid
);
863 *serv
= anon_client(NULL
);
869 rc
= getclientsnames_common(nconf
, nbuf
, serv
);
870 freenetconfigent(nconf
);
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
;
885 nconf
= getnetconfigent(transp
->xp_netid
);
887 syslog(LOG_ERR
, "%s: getnetconfigent failed",
889 *serv
= anon_client(NULL
);
895 *nbuf
= svc_getrpccaller(transp
);
897 freenetconfigent(nconf
);
898 *serv
= anon_client(NULL
);
904 rc
= getclientsnames_common(nconf
, nbuf
, serv
);
905 freenetconfigent(nconf
);
910 log_cant_reply(SVCXPRT
*transp
)
913 struct nd_hostservlist
*clnames
= NULL
;
917 saverrno
= errno
; /* save error code */
918 if (getclientsnames(transp
, &nb
, &clnames
) != 0)
920 host
= clnames
->h_hostservs
->h_host
;
924 syslog(LOG_ERR
, "couldn't send reply to %s", host
);
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
935 mnt_pathconf(struct svc_req
*rqstp
)
939 char *path
, rpath
[MAXPATHLEN
];
942 transp
= rqstp
->rq_xprt
;
944 (void) memset((caddr_t
)&p
, 0, sizeof (p
));
946 if (!svc_getargs(transp
, xdr_dirpath
, (caddr_t
)&path
)) {
947 svcerr_decode(transp
);
950 if (lstat(path
, &st
) < 0) {
951 _PC_SET(_PC_ERROR
, p
.pc_mask
);
955 * Get a path without symbolic links.
957 if (realpath(path
, rpath
) == NULL
) {
959 "mount request: realpath failed on %s: %m",
961 _PC_SET(_PC_ERROR
, p
.pc_mask
);
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
);
974 p
.pc_link_max
= pathconf(rpath
, _PC_LINK_MAX
);
976 _PC_SET(_PC_LINK_MAX
, p
.pc_mask
);
977 p
.pc_name_max
= pathconf(rpath
, _PC_NAME_MAX
);
979 _PC_SET(_PC_NAME_MAX
, p
.pc_mask
);
980 p
.pc_path_max
= pathconf(rpath
, _PC_PATH_MAX
);
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
);
990 if (!svc_sendreply(transp
, xdr_ppathcnf
, (char *)&p
))
991 log_cant_reply(transp
);
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.
1001 checkrootmount(share_t
*sh
, char *rpath
)
1005 if ((val
= getshareopt(sh
->sh_opts
, SHOPT_NOSUB
)) != NULL
) {
1007 if (strcmp(sh
->sh_path
, rpath
) != 0)
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,
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
;
1041 int realpath_error
= ENOENT
, reply_error
= EACCES
, lofs_tried
= 0;
1044 checkpath
= strdup(path
);
1045 if (checkpath
== NULL
) {
1046 syslog(LOG_ERR
, "mount_enoent: no memory");
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)
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)
1077 * Check permissions in mount table.
1079 if (newopts(sh
->sh_opts
))
1080 flavor_count
= getclientsflavors_new(sh
,
1081 transp
, nb
, clnames
, flavor_list
);
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
;
1096 * Check all parent directories.
1098 dp
= strrchr(checkpath
, '/');
1102 if (strlen(checkpath
) == 0)
1105 * Get the real path (no symbolic links in it)
1107 if (realpath(checkpath
, rpath
) == NULL
) {
1108 if (errno
!= ENOENT
)
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.
1127 enqueue_logging_data(char *host
, SVCXPRT
*transp
, char *path
,
1128 char *rpath
, int status
, int error
)
1133 lq
= (logging_data
*)calloc(1, sizeof (logging_data
));
1138 * We might not yet have the host...
1141 DTRACE_PROBE1(mountd
, log_host
, host
);
1142 lq
->ld_host
= strdup(host
);
1143 if (lq
->ld_host
== NULL
)
1146 DTRACE_PROBE(mountd
, log_no_host
);
1148 lq
->ld_netid
= strdup(transp
->xp_netid
);
1149 if (lq
->ld_netid
== NULL
)
1152 lq
->ld_nb
= calloc(1, sizeof (struct netbuf
));
1153 if (lq
->ld_nb
== NULL
)
1156 nb
= svc_getrpccaller(transp
);
1158 DTRACE_PROBE(mountd
, e__nb__enqueue
);
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
)
1171 bcopy(nb
->buf
, lq
->ld_nb
->buf
, lq
->ld_nb
->len
);
1174 lq
->ld_path
= strdup(path
);
1175 if (lq
->ld_path
== NULL
)
1179 lq
->ld_rpath
= strdup(rpath
);
1180 if (lq
->ld_rpath
== NULL
)
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
;
1193 logging_tail
->ld_next
= lq
;
1196 (void) cond_signal(&logging_queue_cv
);
1197 (void) mutex_unlock(&logging_queue_lock
);
1203 free_logging_data(lq
);
1209 * Check mount requests, add to mounted list if ok
1212 mount(struct svc_req
*rqstp
)
1216 struct fhstatus fhs
;
1217 struct mountres3 mountres3
;
1220 char *path
, rpath
[MAXPATHLEN
];
1222 struct nd_hostservlist
*clnames
= NULL
;
1224 int error
= 0, lofs_tried
= 0, enqueued
;
1225 int flavor_list
[MAX_FLAVORS
];
1227 struct netbuf
*nb
= NULL
;
1232 transp
= rqstp
->rq_xprt
;
1233 version
= rqstp
->rq_vers
;
1236 if (!svc_getargs(transp
, xdr_dirpath
, (caddr_t
)&path
)) {
1237 svcerr_decode(transp
);
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
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.
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
1268 if (rejecting
|| version
< mount_vers_min
) {
1270 syslog(LOG_NOTICE
, "Rejected mount: %s for %s",
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()) {
1286 "Rejected mount: TX doesn't support NFSv2");
1293 * Get the real path (no symbolic links in it)
1295 if (realpath(path
, rpath
) == NULL
) {
1299 "mount request: realpath: %s: %m", path
);
1300 if (error
== ENOENT
)
1301 error
= mount_enoent_error(transp
, path
, rpath
,
1302 &clnames
, &nb
, flavor_list
);
1306 if ((sh
= findentry(rpath
)) == NULL
&&
1307 (sh
= find_lofsentry(rpath
, &lofs_tried
)) == NULL
) {
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) {
1321 if (newopts(sh
->sh_opts
))
1322 flavor_count
= getclientsflavors_new(sh
, transp
, &nb
, &clnames
,
1325 flavor_count
= getclientsflavors_old(sh
, transp
, &nb
, &clnames
,
1329 host
= clnames
->h_hostservs
[0].h_host
;
1331 if (flavor_count
== 0) {
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) {
1349 "mount request: Failed to get caller's ucred : %m");
1353 if ((clabel
= ucred_getlabel(uc
)) == NULL
) {
1355 "mount request: can't get client label from ucred");
1361 if (blequal(&admin_low
, clabel
)) {
1362 struct sockaddr
*ca
;
1365 ca
= (struct sockaddr
*)(void *)svc_getrpccaller(
1366 rqstp
->rq_xprt
)->buf
;
1372 * get trusted network template associated
1375 tp
= get_client_template(ca
);
1376 if (tp
== NULL
|| tp
->host_type
!= SUN_CIPSO
) {
1384 if ((slabel
= m_label_alloc(MAC_LABEL
)) == NULL
) {
1389 if (getlabel(rpath
, slabel
) != 0) {
1390 m_label_free(slabel
);
1395 if (!bldominates(clabel
, slabel
)) {
1396 m_label_free(slabel
);
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
) {
1420 error
= errno
== EINVAL
? EACCES
: errno
;
1421 syslog(LOG_DEBUG
, "mount request: getfh failed on %s: %m",
1426 if (version
== MOUNTVERS3
) {
1427 mountres3
.mountres3_u
.mountinfo
.fhandle
.fhandle3_len
= len
;
1428 mountres3
.mountres3_u
.mountinfo
.fhandle
.fhandle3_val
= fh
;
1430 bcopy(fh
, &fhs
.fhstatus_u
.fhs_fhandle
, NFS_FHSIZE
);
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
;
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
;
1455 mountres3
.mountres3_u
.mountinfo
.auth_flavors
.auth_flavors_val
=
1457 mountres3
.mountres3_u
.mountinfo
.auth_flavors
.auth_flavors_len
=
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
;
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
) {
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 */
1492 mntlist_new(host
, rpath
); /* add entry to mount list */
1496 svc_freeargs(transp
, xdr_dirpath
, (caddr_t
)&path
);
1501 netdir_free(clnames
, ND_HOSTSERVLIST
);
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.
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)) {
1533 * We have found fsid's for both paths.
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
);
1556 if (stat64(path2
, &st2
) < 0) {
1557 syslog(LOG_NOTICE
, "%s: %m", path2
);
1561 if (st1
.st_dev
== st2
.st_dev
)
1568 findentry(char *path
)
1571 struct sh_list
*shp
;
1572 register char *p1
, *p2
;
1576 (void) rw_rdlock(&sharetab_lock
);
1578 for (shp
= share_list
; shp
; shp
= shp
->shl_next
) {
1580 for (p1
= sh
->sh_path
, p2
= path
; *p1
== *p2
; p1
++, p2
++)
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
))
1608 sh
= shp
? sharedup(sh
) : NULL
;
1610 (void) rw_unlock(&sharetab_lock
);
1617 is_substring(char **mntp
, char **path
)
1619 char *p1
= *mntp
, *p2
= *path
;
1621 if (*p1
== '\0' && *p2
== '\0') /* exact match */
1623 else if (*p1
== '\0' && *p2
== '/')
1625 else if (*p1
== '\0' && *(p1
-1) == '/') {
1626 *path
= --p2
; /* we need the slash in p2 */
1628 } else if (*p2
== '\0') {
1631 if (*p1
== '\0') /* exact match */
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.
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
;
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
) {
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) {
1704 syslog(LOG_NOTICE
, "%s: %m", rpath
);
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
) {
1717 "%s/.: exceeds MAXPATHLEN %d",
1722 (void) strcpy(tmp_path
, rpath
);
1723 (void) strcat(tmp_path
, "/.");
1725 if (stat(tmp_path
, &r_stbuf
) < 0) {
1727 syslog(LOG_NOTICE
, "%s: %m", tmp_path
);
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.
1748 for (ml
= mntl
; ml
; ml
= ml
->mntl_next
) {
1749 if (strcmp(ml
->mntl_mnt
->mnt_fstype
, "lofs"))
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
)) {
1760 if ((strlen(ml
->mntl_mnt
->mnt_special
) + strlen(p2
)) >
1763 syslog(LOG_NOTICE
, "%s%s: exceeds %d",
1764 ml
->mntl_mnt
->mnt_special
, p2
,
1773 (void) strcpy(tmp_path
, ml
->mntl_mnt
->mnt_special
);
1774 (void) strcat(tmp_path
, p2
);
1777 retcode
= findentry(tmp_path
);
1782 assert(strlen(tmp_path
) > 0);
1783 (void) strcpy(rpath
, tmp_path
);
1787 fsfreemntlist(mntl
);
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
];
1810 char *cstr
= access_list
;
1811 char *gr
= access_list
;
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')
1824 assert(transp
!= NULL
|| (*pnb
!= NULL
&& *pclnames
!= NULL
));
1826 /* Get client address if it wasn't provided */
1828 /* Don't grant access if client address isn't known */
1829 if ((*pnb
= svc_getrpccaller(transp
)) == NULL
)
1832 /* Try to lookup client hostname if it wasn't provided */
1833 if (*pclnames
== NULL
)
1834 getclientsnames(transp
, pnb
, pclnames
);
1835 clnames
= *pclnames
;
1838 if ((cstr
= strpbrk(cstr
, "[]:")) != NULL
) {
1855 * If the list name has a '-' prepended then a match of
1856 * the following name implies failure instead of success.
1866 * First check if we have '@' entry, as it doesn't
1867 * require client hostname.
1872 /* Netname support */
1873 if (!isdigit(*gr
) && *gr
!= '[') {
1874 if ((np
= getnetbyname_r(gr
, &n
, buff
,
1875 sizeof (buff
))) != NULL
&&
1877 while ((np
->n_net
& 0xFF000000u
) == 0)
1879 np
->n_net
= htonl(np
->n_net
);
1880 if (inet_ntop(AF_INET
, &np
->n_net
, addr
,
1881 INET_ADDRSTRLEN
) == NULL
)
1883 if (inet_matchaddr((*pnb
)->buf
, addr
))
1887 if (inet_matchaddr((*pnb
)->buf
, gr
))
1900 * No other checks can be performed if client address
1901 * can't be resolved.
1903 if (clnames
== NULL
) {
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
1923 if (*(gr
+ 1) == '\0') { /* single dot */
1924 if (strchr(host
, '.') == NULL
)
1927 off
= strlen(host
) - strlen(gr
);
1929 strcasecmp(host
+ off
, gr
) == 0) {
1934 /* Just do a hostname match */
1935 if (strcasecmp(gr
, host
) == 0)
1948 if (clnames
== NULL
)
1951 return (netgroup_check(clnames
, access_list
, nentries
));
1955 static char *optlist
[] = {
1962 #define OPT_SECURE 3
1966 #define OPT_WINDOW 5
1968 #define OPT_NOSUID 6
1976 #define OPT_UIDMAP 10
1978 #define OPT_GIDMAP 11
1984 map_flavor(char *str
)
1988 if (nfs_getseconfig_byname(str
, &sec
))
1991 return (sec
.sc_nfsnum
);
1995 * If the option string contains a "sec="
1996 * option, then use new option syntax.
2001 char *head
, *p
, *val
;
2003 if (!opts
|| *opts
== '\0')
2006 head
= strdup(opts
);
2008 syslog(LOG_ERR
, "opts: no memory");
2014 if (getsubopt(&p
, optlist
, &val
) == OPT_SEC
) {
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.
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
);
2049 syslog(LOG_ERR
, "getclientsflavors: no memory");
2053 flavors
[0] = AUTH_SYS
;
2058 switch (getsubopt(&p
, optlist
, &val
)) {
2060 flavors
[0] = AUTH_DES
;
2066 if (in_access_list(transp
, nb
, clnames
, val
))
2072 if (in_access_list(transp
, nb
, clnames
, val
))
2079 /* none takes precedence over everything else */
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.
2104 getclientsflavors_new(share_t
*sh
, SVCXPRT
*transp
, struct netbuf
**nb
,
2105 struct nd_hostservlist
**clnames
, int *flavors
)
2107 char *opts
, *p
, *val
;
2110 boolean_t access_ok
;
2112 boolean_t reject
= B_FALSE
;
2114 opts
= strdup(sh
->sh_opts
);
2116 syslog(LOG_ERR
, "getclientsflavors: no memory");
2122 /* default access is rw */
2126 switch (getsubopt(&p
, optlist
, &val
)) {
2129 * Before a new sec=xxx option, check if we need
2130 * to move the c index back to the previous count.
2136 /* get all the sec=f1[:f2] flavors */
2137 while ((f
= strtok_r(val
, ":", &lasts
))
2139 flavors
[c
++] = map_flavor(f
);
2143 /* for a new sec=xxx option, default is rw access */
2149 if (in_access_list(transp
, nb
, clnames
, val
)) {
2153 access_ok
= B_FALSE
;
2158 if (in_access_list(transp
, nb
, clnames
, val
))
2159 reject
= B_TRUE
; /* none overides rw/ro */
2165 access_ok
= B_FALSE
;
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
));
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
2206 getusergroups(uid_t uid
, uint_t
*ngrps
, gid_t
**grps
)
2209 char *pwbuf
= alloca(pw_size
);
2210 gid_t
*tmpgrps
= alloca(ngroups_max
* sizeof (gid_t
));
2213 if (getpwuid_r(uid
, &pwd
, pwbuf
, pw_size
) == NULL
)
2216 tmpgrps
[0] = pwd
.pw_gid
;
2218 tmpngrps
= _getgroupsbymember(pwd
.pw_name
, tmpgrps
, ngroups_max
, 1);
2219 if (tmpngrps
<= 0) {
2221 "getusergroups(): Unable to get groups for user %s",
2227 *grps
= malloc(tmpngrps
* sizeof (gid_t
));
2228 if (*grps
== NULL
) {
2230 "getusergroups(): Memory allocation failed: %m");
2236 (void) memcpy(*grps
, tmpgrps
, tmpngrps
* sizeof (gid_t
));
2242 * is_a_number(number)
2244 * is the string a number in one of the forms we want to use?
2248 is_a_number(char *number
)
2253 if (strncmp(number
, "0x", 2) == 0) {
2256 } else if (*number
== '-') {
2257 number
++; /* skip the minus */
2259 while (ret
== 1 && *number
!= '\0') {
2261 ret
= isxdigit(*number
++);
2263 ret
= isdigit(*number
++);
2270 get_uid(char *value
, uid_t
*uid
)
2272 if (!is_a_number(value
)) {
2275 * in this case it would have to be a
2278 pw
= getpwnam(value
);
2285 intval
= strtoull(value
, NULL
, 0);
2286 if (intval
> UID_MAX
&& intval
!= -1)
2288 *uid
= (uid_t
)intval
;
2295 get_gid(char *value
, gid_t
*gid
)
2297 if (!is_a_number(value
)) {
2300 * in this case it would have to be a
2303 gr
= getgrnam(value
);
2310 intval
= strtoull(value
, NULL
, 0);
2311 if (intval
> UID_MAX
&& intval
!= -1)
2313 *gid
= (gid_t
)intval
;
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
);
2336 syslog(LOG_ERR
, "check_client: no memory");
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
;
2352 switch (getsubopt(&p
, optlist
, &val
)) {
2357 if (perm
& NFSAUTH_GROUPS
) {
2361 perm
&= ~NFSAUTH_GROUPS
;
2370 if (in_access_list(transp
, nb
, clnames
, val
))
2378 if (in_access_list(transp
, nb
, clnames
, val
))
2384 * Check if the client is in
2385 * the root list. Only valid
2388 if (flavor
!= AUTH_SYS
)
2391 if (val
== NULL
|| *val
== '\0')
2397 if (in_access_list(transp
, nb
, clnames
, val
)) {
2398 perm
|= NFSAUTH_ROOT
;
2399 perm
|= NFSAUTH_UIDMAP
| NFSAUTH_GIDMAP
;
2402 if (perm
& NFSAUTH_GROUPS
) {
2406 perm
&= ~NFSAUTH_GROUPS
;
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
;
2426 * The uidmap is supported for AUTH_SYS only.
2428 if (flavor
!= AUTH_SYS
)
2431 if (perm
& NFSAUTH_UIDMAP
|| map_deny
)
2434 for (c
= val
; c
!= NULL
; c
= n
) {
2446 al
= strchr(s
, ':');
2451 if (s
== NULL
|| al
== NULL
)
2455 if (clnt_uid
!= (uid_t
)-1)
2457 } else if (strcmp(c
, "*") != 0) {
2460 if (!get_uid(c
, &clnt
))
2463 if (clnt_uid
!= clnt
)
2469 else if (!get_uid(s
, &srv
))
2471 else if (srv
== (uid_t
)-1) {
2476 if (in_access_list(transp
, nb
, clnames
, al
)) {
2478 perm
|= NFSAUTH_UIDMAP
;
2480 if (perm
& NFSAUTH_GROUPS
) {
2484 perm
&= ~NFSAUTH_GROUPS
;
2499 * The gidmap is supported for AUTH_SYS only.
2501 if (flavor
!= AUTH_SYS
)
2504 if (perm
& NFSAUTH_GIDMAP
|| map_deny
)
2507 for (c
= val
; c
!= NULL
; c
= n
) {
2519 al
= strchr(s
, ':');
2524 if (s
== NULL
|| al
== NULL
)
2528 if (clnt_gid
!= (gid_t
)-1)
2530 } else if (strcmp(c
, "*") != 0) {
2533 if (!get_gid(c
, &clnt
))
2536 if (clnt_gid
!= clnt
)
2542 else if (!get_gid(s
, &srv
))
2544 else if (srv
== (gid_t
)-1) {
2549 if (in_access_list(transp
, nb
, clnames
, al
)) {
2551 perm
|= NFSAUTH_GIDMAP
;
2553 if (perm
& NFSAUTH_GROUPS
) {
2557 perm
&= ~NFSAUTH_GROUPS
;
2574 if (perm
& NFSAUTH_ROOT
) {
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
);
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
;
2599 * The client matched a flavor entry that
2600 * has no explicit "rw" or "ro" determination.
2601 * Default it to "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
);
2621 perm
&= ~(NFSAUTH_RO
);
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.
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)
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
])
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".
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,
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
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
;
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
);
2698 syslog(LOG_ERR
, "check_client: no memory");
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
;
2713 switch (getsubopt(&p
, optlist
, &val
)) {
2719 while ((f
= strtok_r(val
, ":", &lasts
))
2721 if (flavor
== map_flavor(f
)) {
2736 if (in_access_list(transp
, nb
, clnames
, val
))
2747 if (in_access_list(transp
, nb
, clnames
, val
))
2753 * Check if the client is in
2754 * the root list. Only valid
2757 if (flavor
!= AUTH_SYS
)
2763 if (val
== NULL
|| *val
== '\0')
2769 if (in_access_list(transp
, nb
, clnames
, val
)) {
2770 perm
|= NFSAUTH_ROOT
;
2771 perm
|= NFSAUTH_UIDMAP
| NFSAUTH_GIDMAP
;
2774 if (perm
& NFSAUTH_GROUPS
) {
2778 perm
&= ~NFSAUTH_GROUPS
;
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
;
2798 * The uidmap is supported for AUTH_SYS only.
2800 if (flavor
!= AUTH_SYS
)
2803 if (!match
|| perm
& NFSAUTH_UIDMAP
|| map_deny
)
2806 for (c
= val
; c
!= NULL
; c
= n
) {
2818 al
= strchr(s
, ':');
2823 if (s
== NULL
|| al
== NULL
)
2827 if (clnt_uid
!= (uid_t
)-1)
2829 } else if (strcmp(c
, "*") != 0) {
2832 if (!get_uid(c
, &clnt
))
2835 if (clnt_uid
!= clnt
)
2841 else if (!get_uid(s
, &srv
))
2843 else if (srv
== (uid_t
)-1) {
2848 if (in_access_list(transp
, nb
, clnames
, al
)) {
2850 perm
|= NFSAUTH_UIDMAP
;
2852 if (perm
& NFSAUTH_GROUPS
) {
2856 perm
&= ~NFSAUTH_GROUPS
;
2871 * The gidmap is supported for AUTH_SYS only.
2873 if (flavor
!= AUTH_SYS
)
2876 if (!match
|| perm
& NFSAUTH_GIDMAP
|| map_deny
)
2879 for (c
= val
; c
!= NULL
; c
= n
) {
2891 al
= strchr(s
, ':');
2896 if (s
== NULL
|| al
== NULL
)
2900 if (clnt_gid
!= (gid_t
)-1)
2902 } else if (strcmp(c
, "*") != 0) {
2905 if (!get_gid(c
, &clnt
))
2908 if (clnt_gid
!= clnt
)
2914 else if (!get_gid(s
, &srv
))
2916 else if (srv
== (gid_t
)-1) {
2921 if (in_access_list(transp
, nb
, clnames
, al
)) {
2923 perm
|= NFSAUTH_GIDMAP
;
2925 if (perm
& NFSAUTH_GROUPS
) {
2929 perm
&= ~NFSAUTH_GROUPS
;
2945 if (perm
& NFSAUTH_ROOT
) {
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
) {
2963 return (NFSAUTH_DENIED
);
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
;
2977 perm
|= NFSAUTH_DENIED
;
2981 * The client matched a flavor entry that
2982 * has no explicit "rw" or "ro" determination.
2983 * Make sure it defaults to "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
);
3003 perm
&= ~(NFSAUTH_RO
);
3017 static timestruc_t last_sharetab_time
;
3018 timestruc_t prev_sharetab_time
;
3020 struct sh_list
*shp
, *shp_prev
;
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
);
3031 if (st
.st_mtim
.tv_sec
== last_sharetab_time
.tv_sec
&&
3032 st
.st_mtim
.tv_nsec
== last_sharetab_time
.tv_nsec
) {
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
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
);
3056 * Note that since the sharetab is now in memory
3057 * and a snapshot is taken, we no longer have to
3060 f
= fopen(SHARETAB
, "r");
3062 syslog(LOG_ERR
, "Cannot open %s: %m", SHARETAB
);
3063 (void) rw_unlock(&sharetab_lock
);
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 */
3076 while ((res
= getshare(f
, &sh
)) > 0) {
3078 if (strcmp(sh
->sh_fstype
, "nfs") != 0)
3081 shp
= malloc(sizeof (*shp
));
3084 if (share_list
== NULL
)
3087 /* LINTED not used before set */
3088 shp_prev
->shl_next
= shp
;
3090 shp
->shl_next
= NULL
;
3091 shp
->shl_sh
= sharedup(sh
);
3092 if (shp
->shl_sh
== NULL
)
3097 syslog(LOG_ERR
, "%s: invalid at line %d\n",
3100 if (stat(SHARETAB
, &st
) != 0) {
3101 syslog(LOG_ERR
, "Cannot stat %s: %m", SHARETAB
);
3103 (void) rw_unlock(&sharetab_lock
);
3107 last_sharetab_time
= st
.st_mtim
;
3109 (void) rw_unlock(&sharetab_lock
);
3115 syslog(LOG_ERR
, "check_sharetab: no memory");
3116 sh_free(share_list
);
3119 (void) rw_unlock(&sharetab_lock
);
3123 sh_free(struct sh_list
*shp
)
3125 register struct sh_list
*next
;
3128 sharefree(shp
->shl_sh
);
3129 next
= shp
->shl_next
;
3137 * Remove an entry from mounted list
3140 umount(struct svc_req
*rqstp
)
3142 char *host
, *path
, *remove_path
;
3143 char rpath
[MAXPATHLEN
];
3144 struct nd_hostservlist
*clnames
= NULL
;
3148 transp
= rqstp
->rq_xprt
;
3150 if (!svc_getargs(transp
, xdr_dirpath
, (caddr_t
)&path
)) {
3151 svcerr_decode(transp
);
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
);
3166 host
= clnames
->h_hostservs
[0].h_host
;
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
) {
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
3190 umountall(struct svc_req
*rqstp
)
3192 struct nd_hostservlist
*clnames
= NULL
;
3197 transp
= rqstp
->rq_xprt
;
3198 if (!svc_getargs(transp
, xdr_void
, NULL
)) {
3199 svcerr_decode(transp
);
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 */
3214 host
= clnames
->h_hostservs
[0].h_host
;
3217 * Remove all hosts entries from mount list
3219 mntlist_delete_all(host
);
3222 syslog(LOG_NOTICE
, "UNMOUNTALL: from %s", host
);
3224 netdir_free(clnames
, ND_HOSTSERVLIST
);
3228 exmalloc(size_t size
)
3232 if ((ret
= malloc(size
)) == NULL
) {
3233 syslog(LOG_ERR
, "Out of memory");
3243 if (signum
== SIGHUP
)
3248 static tsol_tpent_t
*
3249 get_client_template(struct sockaddr
*sock
)
3252 in6_addr_t v6client
;
3253 char v4_addr
[INET_ADDRSTRLEN
];
3254 char v6_addr
[INET6_ADDRSTRLEN
];
3258 switch (sock
->sa_family
) {
3260 v4client
= ((struct sockaddr_in
*)(void *)sock
)->
3262 if (inet_ntop(AF_INET
, &v4client
, v4_addr
, INET_ADDRSTRLEN
) ==
3265 rh
= tsol_getrhbyaddr(v4_addr
, sizeof (v4_addr
), AF_INET
);
3268 tp
= tsol_gettpbyname(rh
->rh_template
);
3273 v6client
= ((struct sockaddr_in6
*)(void *)sock
)->sin6_addr
;
3274 if (inet_ntop(AF_INET6
, &v6client
, v6_addr
, INET6_ADDRSTRLEN
) ==
3277 rh
= tsol_getrhbyaddr(v6_addr
, sizeof (v6_addr
), AF_INET6
);
3280 tp
= tsol_gettpbyname(rh
->rh_template
);