10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51610Sthurlow * Common Development and Distribution License (the "License").
61610Sthurlow * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
213957Sth199096
220Sstevel@tonic-gate /*
2312157SSam.Falkner@Sun.COM * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
270Sstevel@tonic-gate /* All Rights Reserved */
280Sstevel@tonic-gate
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD
310Sstevel@tonic-gate * under license from the Regents of the University of California.
320Sstevel@tonic-gate */
330Sstevel@tonic-gate
340Sstevel@tonic-gate #include <stdio.h>
350Sstevel@tonic-gate #include <stdlib.h>
360Sstevel@tonic-gate #include <ctype.h>
370Sstevel@tonic-gate #include <sys/types.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate #include <syslog.h>
400Sstevel@tonic-gate #include <sys/param.h>
410Sstevel@tonic-gate #include <rpc/rpc.h>
420Sstevel@tonic-gate #include <sys/stat.h>
430Sstevel@tonic-gate #include <netconfig.h>
440Sstevel@tonic-gate #include <netdir.h>
450Sstevel@tonic-gate #include <sys/file.h>
460Sstevel@tonic-gate #include <sys/time.h>
470Sstevel@tonic-gate #include <sys/errno.h>
480Sstevel@tonic-gate #include <rpcsvc/mount.h>
490Sstevel@tonic-gate #include <sys/pathconf.h>
500Sstevel@tonic-gate #include <sys/systeminfo.h>
510Sstevel@tonic-gate #include <sys/utsname.h>
526859Sth199096 #include <sys/wait.h>
530Sstevel@tonic-gate #include <signal.h>
540Sstevel@tonic-gate #include <locale.h>
550Sstevel@tonic-gate #include <unistd.h>
560Sstevel@tonic-gate #include <errno.h>
570Sstevel@tonic-gate #include <sys/socket.h>
580Sstevel@tonic-gate #include <netinet/in.h>
590Sstevel@tonic-gate #include <arpa/inet.h>
600Sstevel@tonic-gate #include <netdb.h>
610Sstevel@tonic-gate #include <thread.h>
620Sstevel@tonic-gate #include <assert.h>
630Sstevel@tonic-gate #include <priv_utils.h>
642140Srmesta #include <nfs/auth.h>
652140Srmesta #include <nfs/nfssys.h>
660Sstevel@tonic-gate #include <nfs/nfs.h>
670Sstevel@tonic-gate #include <nfs/nfs_sec.h>
680Sstevel@tonic-gate #include <rpcsvc/daemon_utils.h>
690Sstevel@tonic-gate #include <deflt.h>
700Sstevel@tonic-gate #include "../../fslib.h"
713957Sth199096 #include <sharefs/share.h>
723957Sth199096 #include <sharefs/sharetab.h>
730Sstevel@tonic-gate #include "../lib/sharetab.h"
740Sstevel@tonic-gate #include "mountd.h"
754971Sjarrett #include <tsol/label.h>
764971Sjarrett #include <sys/tsol/label_macro.h>
774971Sjarrett #include <libtsnet.h>
7811211SThomas.Haynes@Sun.COM #include <sys/sdt.h>
79*13080SPavan.Mettu@Oracle.COM #include <libscf.h>
80*13080SPavan.Mettu@Oracle.COM #include <limits.h>
8112157SSam.Falkner@Sun.COM #include <sys/nvpair.h>
8212157SSam.Falkner@Sun.COM #include <attr.h>
83*13080SPavan.Mettu@Oracle.COM #include "smfcfg.h"
840Sstevel@tonic-gate
856859Sth199096 extern int daemonize_init(void);
866859Sth199096 extern void daemonize_fini(int fd);
876859Sth199096
880Sstevel@tonic-gate struct sh_list *share_list;
890Sstevel@tonic-gate
900Sstevel@tonic-gate rwlock_t sharetab_lock; /* lock to protect the cached sharetab */
910Sstevel@tonic-gate static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */
920Sstevel@tonic-gate
9311211SThomas.Haynes@Sun.COM static mutex_t logging_queue_lock;
9411211SThomas.Haynes@Sun.COM static cond_t logging_queue_cv;
9511211SThomas.Haynes@Sun.COM
9611211SThomas.Haynes@Sun.COM static share_t *find_lofsentry(char *, int *);
9712393SJan.Kryl@Sun.COM static int getclientsnames_lazy(char *, struct netbuf **,
9811211SThomas.Haynes@Sun.COM struct nd_hostservlist **);
9912393SJan.Kryl@Sun.COM static int getclientsnames(SVCXPRT *, struct netbuf **,
10011211SThomas.Haynes@Sun.COM struct nd_hostservlist **);
10111211SThomas.Haynes@Sun.COM static int getclientsflavors_old(share_t *, SVCXPRT *, struct netbuf **,
10211211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int *);
10311211SThomas.Haynes@Sun.COM static int getclientsflavors_new(share_t *, SVCXPRT *, struct netbuf **,
10411211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int *);
10511211SThomas.Haynes@Sun.COM static int check_client_old(share_t *, SVCXPRT *, struct netbuf **,
10611211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int);
10711211SThomas.Haynes@Sun.COM static int check_client_new(share_t *, SVCXPRT *, struct netbuf **,
10811211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int);
1090Sstevel@tonic-gate static void mnt(struct svc_req *, SVCXPRT *);
1100Sstevel@tonic-gate static void mnt_pathconf(struct svc_req *);
11111211SThomas.Haynes@Sun.COM static int mount(struct svc_req *r);
1120Sstevel@tonic-gate static void sh_free(struct sh_list *);
1130Sstevel@tonic-gate static void umount(struct svc_req *);
1140Sstevel@tonic-gate static void umountall(struct svc_req *);
1150Sstevel@tonic-gate static int netmatch(struct netbuf *, char *);
1160Sstevel@tonic-gate static void sigexit(int);
1170Sstevel@tonic-gate static int newopts(char *);
1184971Sjarrett static tsol_tpent_t *get_client_template(struct sockaddr *);
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate static int verbose;
1210Sstevel@tonic-gate static int rejecting;
1220Sstevel@tonic-gate static int mount_vers_min = MOUNTVERS;
1230Sstevel@tonic-gate static int mount_vers_max = MOUNTVERS3;
1240Sstevel@tonic-gate
1257961SNatalie.Li@Sun.COM /* Needs to be accessed by nfscmd.c */
12611211SThomas.Haynes@Sun.COM int in_access_list(SVCXPRT *, struct netbuf **,
12711211SThomas.Haynes@Sun.COM struct nd_hostservlist **, char *);
1287961SNatalie.Li@Sun.COM
1297961SNatalie.Li@Sun.COM extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t);
1307961SNatalie.Li@Sun.COM
1312140Srmesta thread_t nfsauth_thread;
1327961SNatalie.Li@Sun.COM thread_t cmd_thread;
13311211SThomas.Haynes@Sun.COM thread_t logging_thread;
13411211SThomas.Haynes@Sun.COM
13511211SThomas.Haynes@Sun.COM typedef struct logging_data {
13611211SThomas.Haynes@Sun.COM char *ld_host;
13711211SThomas.Haynes@Sun.COM char *ld_path;
13811211SThomas.Haynes@Sun.COM char *ld_rpath;
13911211SThomas.Haynes@Sun.COM int ld_status;
14011211SThomas.Haynes@Sun.COM char *ld_netid;
14111211SThomas.Haynes@Sun.COM struct netbuf *ld_nb;
14211211SThomas.Haynes@Sun.COM struct logging_data *ld_next;
14311211SThomas.Haynes@Sun.COM } logging_data;
14411211SThomas.Haynes@Sun.COM
14511211SThomas.Haynes@Sun.COM static logging_data *logging_head = NULL;
14611211SThomas.Haynes@Sun.COM static logging_data *logging_tail = NULL;
1472140Srmesta
1482140Srmesta /* ARGSUSED */
1492140Srmesta static void *
nfsauth_svc(void * arg)1502140Srmesta nfsauth_svc(void *arg)
1512140Srmesta {
1522140Srmesta int doorfd = -1;
1532140Srmesta uint_t darg;
1542140Srmesta #ifdef DEBUG
1552140Srmesta int dfd;
1562140Srmesta #endif
1572140Srmesta
1582140Srmesta if ((doorfd = door_create(nfsauth_func, NULL,
1592140Srmesta DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1602140Srmesta syslog(LOG_ERR, "Unable to create door: %m\n");
1612140Srmesta exit(10);
1622140Srmesta }
1632140Srmesta
1642140Srmesta #ifdef DEBUG
1652140Srmesta /*
1662140Srmesta * Create a file system path for the door
1672140Srmesta */
1682140Srmesta if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC,
1692140Srmesta S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
1702140Srmesta syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR);
1712140Srmesta (void) close(doorfd);
1722140Srmesta exit(11);
1732140Srmesta }
1742140Srmesta
1752140Srmesta /*
1762140Srmesta * Clean up any stale namespace associations
1772140Srmesta */
1782140Srmesta (void) fdetach(MOUNTD_DOOR);
1792140Srmesta
1802140Srmesta /*
1812140Srmesta * Register in namespace to pass to the kernel to door_ki_open
1822140Srmesta */
1832140Srmesta if (fattach(doorfd, MOUNTD_DOOR) == -1) {
1842140Srmesta syslog(LOG_ERR, "Unable to fattach door: %m\n");
1852140Srmesta (void) close(dfd);
1862140Srmesta (void) close(doorfd);
1872140Srmesta exit(12);
1882140Srmesta }
1892140Srmesta (void) close(dfd);
1902140Srmesta #endif
1912140Srmesta
1922140Srmesta /*
1932140Srmesta * Must pass the doorfd down to the kernel.
1942140Srmesta */
1952140Srmesta darg = doorfd;
1962140Srmesta (void) _nfssys(MOUNTD_ARGS, &darg);
1972140Srmesta
1982140Srmesta /*
1992140Srmesta * Wait for incoming calls
2002140Srmesta */
2012140Srmesta /*CONSTCOND*/
2022140Srmesta for (;;)
2032140Srmesta (void) pause();
2042140Srmesta
2052140Srmesta /*NOTREACHED*/
2062140Srmesta syslog(LOG_ERR, gettext("Door server exited"));
2072140Srmesta return (NULL);
2082140Srmesta }
2092140Srmesta
2107961SNatalie.Li@Sun.COM /*
2117961SNatalie.Li@Sun.COM * NFS command service thread code for setup and handling of the
2127961SNatalie.Li@Sun.COM * nfs_cmd requests for character set conversion and other future
2137961SNatalie.Li@Sun.COM * events.
2147961SNatalie.Li@Sun.COM */
2157961SNatalie.Li@Sun.COM
2167961SNatalie.Li@Sun.COM static void *
cmd_svc(void * arg)2177961SNatalie.Li@Sun.COM cmd_svc(void *arg)
2187961SNatalie.Li@Sun.COM {
2197961SNatalie.Li@Sun.COM int doorfd = -1;
2207961SNatalie.Li@Sun.COM uint_t darg;
2217961SNatalie.Li@Sun.COM
2227961SNatalie.Li@Sun.COM if ((doorfd = door_create(nfscmd_func, NULL,
2237961SNatalie.Li@Sun.COM DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
2247961SNatalie.Li@Sun.COM syslog(LOG_ERR, "Unable to create cmd door: %m\n");
2257961SNatalie.Li@Sun.COM exit(10);
2267961SNatalie.Li@Sun.COM }
2277961SNatalie.Li@Sun.COM
2287961SNatalie.Li@Sun.COM /*
2297961SNatalie.Li@Sun.COM * Must pass the doorfd down to the kernel.
2307961SNatalie.Li@Sun.COM */
2317961SNatalie.Li@Sun.COM darg = doorfd;
2327961SNatalie.Li@Sun.COM (void) _nfssys(NFSCMD_ARGS, &darg);
2337961SNatalie.Li@Sun.COM
2347961SNatalie.Li@Sun.COM /*
2357961SNatalie.Li@Sun.COM * Wait for incoming calls
2367961SNatalie.Li@Sun.COM */
2377961SNatalie.Li@Sun.COM /*CONSTCOND*/
2387961SNatalie.Li@Sun.COM for (;;)
2397961SNatalie.Li@Sun.COM (void) pause();
2407961SNatalie.Li@Sun.COM
2417961SNatalie.Li@Sun.COM /*NOTREACHED*/
2427961SNatalie.Li@Sun.COM syslog(LOG_ERR, gettext("Cmd door server exited"));
2437961SNatalie.Li@Sun.COM return (NULL);
2447961SNatalie.Li@Sun.COM }
2456859Sth199096
24611211SThomas.Haynes@Sun.COM static void
free_logging_data(logging_data * lq)24711211SThomas.Haynes@Sun.COM free_logging_data(logging_data *lq)
24811211SThomas.Haynes@Sun.COM {
24911211SThomas.Haynes@Sun.COM if (lq != NULL) {
25011211SThomas.Haynes@Sun.COM free(lq->ld_host);
25111211SThomas.Haynes@Sun.COM free(lq->ld_netid);
25211211SThomas.Haynes@Sun.COM
25311211SThomas.Haynes@Sun.COM if (lq->ld_nb != NULL) {
25411211SThomas.Haynes@Sun.COM free(lq->ld_nb->buf);
25511211SThomas.Haynes@Sun.COM free(lq->ld_nb);
25611211SThomas.Haynes@Sun.COM }
25711211SThomas.Haynes@Sun.COM
25811211SThomas.Haynes@Sun.COM free(lq->ld_path);
25911211SThomas.Haynes@Sun.COM free(lq->ld_rpath);
26011211SThomas.Haynes@Sun.COM
26111211SThomas.Haynes@Sun.COM free(lq);
26211211SThomas.Haynes@Sun.COM }
26311211SThomas.Haynes@Sun.COM }
26411211SThomas.Haynes@Sun.COM
26511211SThomas.Haynes@Sun.COM static logging_data *
remove_head_of_queue(void)26611211SThomas.Haynes@Sun.COM remove_head_of_queue(void)
26711211SThomas.Haynes@Sun.COM {
26811211SThomas.Haynes@Sun.COM logging_data *lq;
26911211SThomas.Haynes@Sun.COM
27011211SThomas.Haynes@Sun.COM /*
27111211SThomas.Haynes@Sun.COM * Pull it off the queue.
27211211SThomas.Haynes@Sun.COM */
27311211SThomas.Haynes@Sun.COM lq = logging_head;
27411211SThomas.Haynes@Sun.COM if (lq) {
27511211SThomas.Haynes@Sun.COM logging_head = lq->ld_next;
27611211SThomas.Haynes@Sun.COM
27711211SThomas.Haynes@Sun.COM /*
27811211SThomas.Haynes@Sun.COM * Drained it.
27911211SThomas.Haynes@Sun.COM */
28011211SThomas.Haynes@Sun.COM if (logging_head == NULL) {
28111211SThomas.Haynes@Sun.COM logging_tail = NULL;
28211211SThomas.Haynes@Sun.COM }
28311211SThomas.Haynes@Sun.COM }
28411211SThomas.Haynes@Sun.COM
28511211SThomas.Haynes@Sun.COM return (lq);
28611211SThomas.Haynes@Sun.COM }
28711211SThomas.Haynes@Sun.COM
28811211SThomas.Haynes@Sun.COM static void
do_logging_queue(logging_data * lq)28911211SThomas.Haynes@Sun.COM do_logging_queue(logging_data *lq)
29011211SThomas.Haynes@Sun.COM {
29111211SThomas.Haynes@Sun.COM logging_data *lq_clean = NULL;
29211211SThomas.Haynes@Sun.COM int cleared = 0;
29311211SThomas.Haynes@Sun.COM char *host;
29411211SThomas.Haynes@Sun.COM
29511211SThomas.Haynes@Sun.COM struct nd_hostservlist *clnames;
29611211SThomas.Haynes@Sun.COM
29711211SThomas.Haynes@Sun.COM while (lq) {
29811211SThomas.Haynes@Sun.COM if (lq->ld_host == NULL) {
29911211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_lazy);
30012393SJan.Kryl@Sun.COM if (getclientsnames_lazy(lq->ld_netid,
30112393SJan.Kryl@Sun.COM &lq->ld_nb, &clnames) != 0)
30211211SThomas.Haynes@Sun.COM host = NULL;
30311211SThomas.Haynes@Sun.COM else
30411211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host;
30511211SThomas.Haynes@Sun.COM } else
30611211SThomas.Haynes@Sun.COM host = lq->ld_host;
30711211SThomas.Haynes@Sun.COM
30811211SThomas.Haynes@Sun.COM audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */
30911211SThomas.Haynes@Sun.COM
31011211SThomas.Haynes@Sun.COM /* add entry to mount list */
31111211SThomas.Haynes@Sun.COM if (lq->ld_rpath)
31211211SThomas.Haynes@Sun.COM mntlist_new(host, lq->ld_rpath);
31311211SThomas.Haynes@Sun.COM
31411211SThomas.Haynes@Sun.COM lq->ld_next = lq_clean;
31511211SThomas.Haynes@Sun.COM lq_clean = lq;
31611211SThomas.Haynes@Sun.COM
31711211SThomas.Haynes@Sun.COM (void) mutex_lock(&logging_queue_lock);
31811211SThomas.Haynes@Sun.COM lq = remove_head_of_queue();
31911211SThomas.Haynes@Sun.COM (void) mutex_unlock(&logging_queue_lock);
32011211SThomas.Haynes@Sun.COM }
32111211SThomas.Haynes@Sun.COM
32211211SThomas.Haynes@Sun.COM while (lq_clean) {
32311211SThomas.Haynes@Sun.COM lq = lq_clean;
32411211SThomas.Haynes@Sun.COM lq_clean = lq->ld_next;
32511211SThomas.Haynes@Sun.COM
32611211SThomas.Haynes@Sun.COM free_logging_data(lq);
32711211SThomas.Haynes@Sun.COM cleared++;
32811211SThomas.Haynes@Sun.COM }
32911211SThomas.Haynes@Sun.COM
33011211SThomas.Haynes@Sun.COM DTRACE_PROBE1(mountd, logging_cleared, cleared);
33111211SThomas.Haynes@Sun.COM }
33211211SThomas.Haynes@Sun.COM
33311211SThomas.Haynes@Sun.COM static void *
logging_svc(void * arg)33411211SThomas.Haynes@Sun.COM logging_svc(void *arg)
33511211SThomas.Haynes@Sun.COM {
33611211SThomas.Haynes@Sun.COM logging_data *lq;
33711211SThomas.Haynes@Sun.COM
33811211SThomas.Haynes@Sun.COM for (;;) {
33911211SThomas.Haynes@Sun.COM (void) mutex_lock(&logging_queue_lock);
34011211SThomas.Haynes@Sun.COM while (logging_head == NULL) {
34111211SThomas.Haynes@Sun.COM (void) cond_wait(&logging_queue_cv,
34211211SThomas.Haynes@Sun.COM &logging_queue_lock);
34311211SThomas.Haynes@Sun.COM }
34411211SThomas.Haynes@Sun.COM
34511211SThomas.Haynes@Sun.COM lq = remove_head_of_queue();
34611211SThomas.Haynes@Sun.COM (void) mutex_unlock(&logging_queue_lock);
34711211SThomas.Haynes@Sun.COM
34811211SThomas.Haynes@Sun.COM do_logging_queue(lq);
34911211SThomas.Haynes@Sun.COM }
35011211SThomas.Haynes@Sun.COM
35111211SThomas.Haynes@Sun.COM /*NOTREACHED*/
35211211SThomas.Haynes@Sun.COM syslog(LOG_ERR, gettext("Logging server exited"));
35311211SThomas.Haynes@Sun.COM return (NULL);
35411211SThomas.Haynes@Sun.COM }
35511211SThomas.Haynes@Sun.COM
356249Sjwahlig int
main(int argc,char * argv[])357249Sjwahlig main(int argc, char *argv[])
3580Sstevel@tonic-gate {
3592140Srmesta int pid;
3602140Srmesta int c;
3612140Srmesta int rpc_svc_mode = RPC_SVC_MT_AUTO;
3622140Srmesta int maxthreads;
3632140Srmesta int maxrecsz = RPC_MAXDATASIZE;
3642140Srmesta bool_t exclbind = TRUE;
3652140Srmesta bool_t can_do_mlp;
3662140Srmesta long thr_flags = (THR_NEW_LWP|THR_DAEMON);
367*13080SPavan.Mettu@Oracle.COM char defval[4];
368*13080SPavan.Mettu@Oracle.COM int defvers, ret, bufsz;
3690Sstevel@tonic-gate
3706859Sth199096 int pipe_fd = -1;
3716859Sth199096
3720Sstevel@tonic-gate /*
3730Sstevel@tonic-gate * Mountd requires uid 0 for:
3740Sstevel@tonic-gate * /etc/rmtab updates (we could chown it to daemon)
3750Sstevel@tonic-gate * /etc/dfs/dfstab reading (it wants to lock out share which
3760Sstevel@tonic-gate * doesn't do any locking before first truncate;
3770Sstevel@tonic-gate * NFS share does; should use fcntl locking instead)
3780Sstevel@tonic-gate * Needed privileges:
3790Sstevel@tonic-gate * auditing
3800Sstevel@tonic-gate * nfs syscall
3810Sstevel@tonic-gate * file dac search (so it can stat all files)
3821676Sjpk * Optional privileges:
3831676Sjpk * MLP
3840Sstevel@tonic-gate */
3851676Sjpk can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
3860Sstevel@tonic-gate if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1,
3871676Sjpk PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH,
3881676Sjpk can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
3890Sstevel@tonic-gate (void) fprintf(stderr,
3906859Sth199096 "%s: must be run with sufficient privileges\n",
3916859Sth199096 argv[0]);
3920Sstevel@tonic-gate exit(1);
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate maxthreads = 0;
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate while ((c = getopt(argc, argv, "vrm:")) != EOF) {
3980Sstevel@tonic-gate switch (c) {
3990Sstevel@tonic-gate case 'v':
4000Sstevel@tonic-gate verbose++;
4010Sstevel@tonic-gate break;
4020Sstevel@tonic-gate case 'r':
4030Sstevel@tonic-gate rejecting = 1;
4040Sstevel@tonic-gate break;
4050Sstevel@tonic-gate case 'm':
4060Sstevel@tonic-gate maxthreads = atoi(optarg);
4070Sstevel@tonic-gate if (maxthreads < 1) {
4080Sstevel@tonic-gate (void) fprintf(stderr,
4090Sstevel@tonic-gate "%s: must specify positive maximum threads count, using default\n",
4106859Sth199096 argv[0]);
4110Sstevel@tonic-gate maxthreads = 0;
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate break;
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate /*
4180Sstevel@tonic-gate * Read in the NFS version values from config file.
4190Sstevel@tonic-gate */
420*13080SPavan.Mettu@Oracle.COM bufsz = 4;
421*13080SPavan.Mettu@Oracle.COM ret = nfs_smf_get_prop("server_versmin", defval, DEFAULT_INSTANCE,
422*13080SPavan.Mettu@Oracle.COM SCF_TYPE_INTEGER, NFSD, &bufsz);
423*13080SPavan.Mettu@Oracle.COM if (ret == SA_OK) {
424*13080SPavan.Mettu@Oracle.COM errno = 0;
425*13080SPavan.Mettu@Oracle.COM defvers = strtol(defval, (char **)NULL, 10);
426*13080SPavan.Mettu@Oracle.COM if (errno == 0) {
427*13080SPavan.Mettu@Oracle.COM mount_vers_min = defvers;
428*13080SPavan.Mettu@Oracle.COM /*
429*13080SPavan.Mettu@Oracle.COM * special because NFSv2 is
430*13080SPavan.Mettu@Oracle.COM * supported by mount v1 & v2
431*13080SPavan.Mettu@Oracle.COM */
432*13080SPavan.Mettu@Oracle.COM if (defvers == NFS_VERSION)
433*13080SPavan.Mettu@Oracle.COM mount_vers_min = MOUNTVERS;
434*13080SPavan.Mettu@Oracle.COM }
435*13080SPavan.Mettu@Oracle.COM }
4360Sstevel@tonic-gate
437*13080SPavan.Mettu@Oracle.COM bufsz = 4;
438*13080SPavan.Mettu@Oracle.COM ret = nfs_smf_get_prop("server_versmax", defval, DEFAULT_INSTANCE,
439*13080SPavan.Mettu@Oracle.COM SCF_TYPE_INTEGER, NFSD, &bufsz);
440*13080SPavan.Mettu@Oracle.COM if (ret == SA_OK) {
441*13080SPavan.Mettu@Oracle.COM errno = 0;
442*13080SPavan.Mettu@Oracle.COM defvers = strtol(defval, (char **)NULL, 10);
443*13080SPavan.Mettu@Oracle.COM if (errno == 0) {
444*13080SPavan.Mettu@Oracle.COM mount_vers_max = defvers;
4450Sstevel@tonic-gate }
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate
4480Sstevel@tonic-gate /*
4490Sstevel@tonic-gate * Sanity check versions,
4500Sstevel@tonic-gate * even though we may get versions > MOUNTVERS3, we still need
4510Sstevel@tonic-gate * to start nfsauth service, so continue on regardless of values.
4520Sstevel@tonic-gate */
4530Sstevel@tonic-gate if (mount_vers_min > mount_vers_max) {
454*13080SPavan.Mettu@Oracle.COM fprintf(stderr, "server_versmin > server_versmax");
4550Sstevel@tonic-gate mount_vers_max = mount_vers_min;
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
4580Sstevel@tonic-gate (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL);
4590Sstevel@tonic-gate (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL);
46011211SThomas.Haynes@Sun.COM (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL);
46111211SThomas.Haynes@Sun.COM (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL);
46211211SThomas.Haynes@Sun.COM
4630Sstevel@tonic-gate netgroup_init();
4640Sstevel@tonic-gate
4650Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
4660Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST"
4670Sstevel@tonic-gate #endif
4680Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate /* Don't drop core if the NFS module isn't loaded. */
4710Sstevel@tonic-gate (void) signal(SIGSYS, SIG_IGN);
4720Sstevel@tonic-gate
4736859Sth199096 pipe_fd = daemonize_init();
4740Sstevel@tonic-gate
4750Sstevel@tonic-gate /*
4760Sstevel@tonic-gate * If we coredump it'll be in /core
4770Sstevel@tonic-gate */
4780Sstevel@tonic-gate if (chdir("/") < 0)
4796859Sth199096 fprintf(stderr, "chdir /: %s", strerror(errno));
4800Sstevel@tonic-gate
4810Sstevel@tonic-gate openlog("mountd", LOG_PID, LOG_DAEMON);
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate * establish our lock on the lock file and write our pid to it.
4850Sstevel@tonic-gate * exit if some other process holds the lock, or if there's any
4860Sstevel@tonic-gate * error in writing/locking the file.
4870Sstevel@tonic-gate */
4880Sstevel@tonic-gate pid = _enter_daemon_lock(MOUNTD);
4890Sstevel@tonic-gate switch (pid) {
4900Sstevel@tonic-gate case 0:
4910Sstevel@tonic-gate break;
4920Sstevel@tonic-gate case -1:
4936859Sth199096 fprintf(stderr, "error locking for %s: %s", MOUNTD,
4940Sstevel@tonic-gate strerror(errno));
4950Sstevel@tonic-gate exit(2);
4960Sstevel@tonic-gate default:
4970Sstevel@tonic-gate /* daemon was already running */
4980Sstevel@tonic-gate exit(0);
4990Sstevel@tonic-gate }
5000Sstevel@tonic-gate
5010Sstevel@tonic-gate audit_mountd_setup(); /* BSM */
5020Sstevel@tonic-gate
5030Sstevel@tonic-gate /*
5040Sstevel@tonic-gate * Tell RPC that we want automatic thread mode.
5050Sstevel@tonic-gate * A new thread will be spawned for each request.
5060Sstevel@tonic-gate */
5070Sstevel@tonic-gate if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) {
5086859Sth199096 fprintf(stderr, "unable to set automatic MT mode");
5090Sstevel@tonic-gate exit(1);
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate
5120Sstevel@tonic-gate /*
5130Sstevel@tonic-gate * Enable non-blocking mode and maximum record size checks for
5140Sstevel@tonic-gate * connection oriented transports.
5150Sstevel@tonic-gate */
5160Sstevel@tonic-gate if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
5176859Sth199096 fprintf(stderr, "unable to set RPC max record size");
5180Sstevel@tonic-gate }
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate /*
5210Sstevel@tonic-gate * Prevent our non-priv udp and tcp ports bound w/wildcard addr
5220Sstevel@tonic-gate * from being hijacked by a bind to a more specific addr.
5230Sstevel@tonic-gate */
5240Sstevel@tonic-gate if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
5256859Sth199096 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND");
5260Sstevel@tonic-gate }
5270Sstevel@tonic-gate
5280Sstevel@tonic-gate /*
5290Sstevel@tonic-gate * If the -m argument was specified, then set the
5300Sstevel@tonic-gate * maximum number of threads to the value specified.
5310Sstevel@tonic-gate */
5320Sstevel@tonic-gate if (maxthreads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &maxthreads)) {
5336859Sth199096 fprintf(stderr, "unable to set maxthreads");
5340Sstevel@tonic-gate exit(1);
5350Sstevel@tonic-gate }
5360Sstevel@tonic-gate
5370Sstevel@tonic-gate /*
5380Sstevel@tonic-gate * Make sure to unregister any previous versions in case the
5390Sstevel@tonic-gate * user is reconfiguring the server in interesting ways.
5400Sstevel@tonic-gate */
5410Sstevel@tonic-gate svc_unreg(MOUNTPROG, MOUNTVERS);
5420Sstevel@tonic-gate svc_unreg(MOUNTPROG, MOUNTVERS_POSIX);
5430Sstevel@tonic-gate svc_unreg(MOUNTPROG, MOUNTVERS3);
5440Sstevel@tonic-gate
5450Sstevel@tonic-gate /*
5462140Srmesta * Create the nfsauth thread with same signal disposition
5472140Srmesta * as the main thread. We need to create a separate thread
5482140Srmesta * since mountd() will be both an RPC server (for remote
5492140Srmesta * traffic) _and_ a doors server (for kernel upcalls).
5500Sstevel@tonic-gate */
5512140Srmesta if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) {
5526859Sth199096 fprintf(stderr, gettext("Failed to create NFSAUTH svc thread"));
5532140Srmesta exit(2);
5540Sstevel@tonic-gate }
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate /*
5577961SNatalie.Li@Sun.COM * Create the cmd service thread with same signal disposition
5587961SNatalie.Li@Sun.COM * as the main thread. We need to create a separate thread
5597961SNatalie.Li@Sun.COM * since mountd() will be both an RPC server (for remote
5607961SNatalie.Li@Sun.COM * traffic) _and_ a doors server (for kernel upcalls).
5617961SNatalie.Li@Sun.COM */
5627961SNatalie.Li@Sun.COM if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) {
5637961SNatalie.Li@Sun.COM syslog(LOG_ERR, gettext("Failed to create CMD svc thread"));
5647961SNatalie.Li@Sun.COM exit(2);
5657961SNatalie.Li@Sun.COM }
5667961SNatalie.Li@Sun.COM
5677961SNatalie.Li@Sun.COM /*
56811211SThomas.Haynes@Sun.COM * Create an additional thread to service the rmtab and
56911211SThomas.Haynes@Sun.COM * audit_mountd_mount logging for mount requests. Use the same
57011211SThomas.Haynes@Sun.COM * signal disposition as the main thread. We create
57111211SThomas.Haynes@Sun.COM * a separate thread to allow the mount request threads to
57211211SThomas.Haynes@Sun.COM * clear as soon as possible.
57311211SThomas.Haynes@Sun.COM */
57411211SThomas.Haynes@Sun.COM if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) {
57511211SThomas.Haynes@Sun.COM syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread"));
57611211SThomas.Haynes@Sun.COM exit(2);
57711211SThomas.Haynes@Sun.COM }
57811211SThomas.Haynes@Sun.COM
57911211SThomas.Haynes@Sun.COM /*
5800Sstevel@tonic-gate * Create datagram and connection oriented services
5810Sstevel@tonic-gate */
5820Sstevel@tonic-gate if (mount_vers_max >= MOUNTVERS) {
5830Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) {
5846859Sth199096 fprintf(stderr,
5856859Sth199096 "couldn't register datagram_v MOUNTVERS");
5860Sstevel@tonic-gate exit(1);
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) {
5896859Sth199096 fprintf(stderr,
5906859Sth199096 "couldn't register circuit_v MOUNTVERS");
5910Sstevel@tonic-gate exit(1);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate }
59411211SThomas.Haynes@Sun.COM
5950Sstevel@tonic-gate if (mount_vers_max >= MOUNTVERS_POSIX) {
5960Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
5976859Sth199096 "datagram_v") == 0) {
5986859Sth199096 fprintf(stderr,
5996859Sth199096 "couldn't register datagram_v MOUNTVERS_POSIX");
6000Sstevel@tonic-gate exit(1);
6010Sstevel@tonic-gate }
6020Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX,
6036859Sth199096 "circuit_v") == 0) {
6046859Sth199096 fprintf(stderr,
6056859Sth199096 "couldn't register circuit_v MOUNTVERS_POSIX");
6060Sstevel@tonic-gate exit(1);
6070Sstevel@tonic-gate }
6080Sstevel@tonic-gate }
6090Sstevel@tonic-gate
6100Sstevel@tonic-gate if (mount_vers_max >= MOUNTVERS3) {
6110Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) {
6126859Sth199096 fprintf(stderr,
6136859Sth199096 "couldn't register datagram_v MOUNTVERS3");
6140Sstevel@tonic-gate exit(1);
6150Sstevel@tonic-gate }
6160Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) {
6176859Sth199096 fprintf(stderr,
6186859Sth199096 "couldn't register circuit_v MOUNTVERS3");
6190Sstevel@tonic-gate exit(1);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate
6230Sstevel@tonic-gate /*
6240Sstevel@tonic-gate * Start serving
6250Sstevel@tonic-gate */
6260Sstevel@tonic-gate rmtab_load();
6276859Sth199096
6286859Sth199096 daemonize_fini(pipe_fd);
6290Sstevel@tonic-gate
6300Sstevel@tonic-gate /* Get rid of the most dangerous basic privileges. */
6310Sstevel@tonic-gate __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION,
6320Sstevel@tonic-gate (char *)NULL);
6330Sstevel@tonic-gate
6340Sstevel@tonic-gate svc_run();
6350Sstevel@tonic-gate syslog(LOG_ERR, "Error: svc_run shouldn't have returned");
6360Sstevel@tonic-gate abort();
6376859Sth199096
6380Sstevel@tonic-gate /* NOTREACHED */
639249Sjwahlig return (0);
6400Sstevel@tonic-gate }
6410Sstevel@tonic-gate
6420Sstevel@tonic-gate /*
6430Sstevel@tonic-gate * Server procedure switch routine
6440Sstevel@tonic-gate */
6450Sstevel@tonic-gate void
mnt(struct svc_req * rqstp,SVCXPRT * transp)6460Sstevel@tonic-gate mnt(struct svc_req *rqstp, SVCXPRT *transp)
6470Sstevel@tonic-gate {
6480Sstevel@tonic-gate switch (rqstp->rq_proc) {
6490Sstevel@tonic-gate case NULLPROC:
6500Sstevel@tonic-gate errno = 0;
6510Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_void, (char *)0))
6520Sstevel@tonic-gate log_cant_reply(transp);
6530Sstevel@tonic-gate return;
6540Sstevel@tonic-gate
6550Sstevel@tonic-gate case MOUNTPROC_MNT:
65611211SThomas.Haynes@Sun.COM (void) mount(rqstp);
6570Sstevel@tonic-gate return;
6580Sstevel@tonic-gate
6590Sstevel@tonic-gate case MOUNTPROC_DUMP:
6600Sstevel@tonic-gate mntlist_send(transp);
6610Sstevel@tonic-gate return;
6620Sstevel@tonic-gate
6630Sstevel@tonic-gate case MOUNTPROC_UMNT:
6640Sstevel@tonic-gate umount(rqstp);
6650Sstevel@tonic-gate return;
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate case MOUNTPROC_UMNTALL:
6680Sstevel@tonic-gate umountall(rqstp);
6690Sstevel@tonic-gate return;
6700Sstevel@tonic-gate
6710Sstevel@tonic-gate case MOUNTPROC_EXPORT:
6720Sstevel@tonic-gate case MOUNTPROC_EXPORTALL:
6730Sstevel@tonic-gate export(rqstp);
6740Sstevel@tonic-gate return;
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate case MOUNTPROC_PATHCONF:
6770Sstevel@tonic-gate if (rqstp->rq_vers == MOUNTVERS_POSIX)
6780Sstevel@tonic-gate mnt_pathconf(rqstp);
6790Sstevel@tonic-gate else
6800Sstevel@tonic-gate svcerr_noproc(transp);
6810Sstevel@tonic-gate return;
6820Sstevel@tonic-gate
6830Sstevel@tonic-gate default:
6840Sstevel@tonic-gate svcerr_noproc(transp);
6850Sstevel@tonic-gate return;
6860Sstevel@tonic-gate }
6870Sstevel@tonic-gate }
6880Sstevel@tonic-gate
6890Sstevel@tonic-gate /* Set up anonymous client */
6900Sstevel@tonic-gate
6910Sstevel@tonic-gate struct nd_hostservlist *
anon_client(char * host)6920Sstevel@tonic-gate anon_client(char *host)
6930Sstevel@tonic-gate {
6940Sstevel@tonic-gate struct nd_hostservlist *anon_hsl;
6950Sstevel@tonic-gate struct nd_hostserv *anon_hs;
6960Sstevel@tonic-gate
6970Sstevel@tonic-gate anon_hsl = malloc(sizeof (*anon_hsl));
6980Sstevel@tonic-gate if (anon_hsl == NULL)
6990Sstevel@tonic-gate return (NULL);
7000Sstevel@tonic-gate
7010Sstevel@tonic-gate anon_hs = malloc(sizeof (*anon_hs));
7020Sstevel@tonic-gate if (anon_hs == NULL) {
7030Sstevel@tonic-gate free(anon_hsl);
7040Sstevel@tonic-gate return (NULL);
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate
7070Sstevel@tonic-gate if (host == NULL)
7080Sstevel@tonic-gate anon_hs->h_host = strdup("(anon)");
7090Sstevel@tonic-gate else
7100Sstevel@tonic-gate anon_hs->h_host = strdup(host);
7110Sstevel@tonic-gate
7120Sstevel@tonic-gate if (anon_hs->h_host == NULL) {
7130Sstevel@tonic-gate free(anon_hs);
7140Sstevel@tonic-gate free(anon_hsl);
7150Sstevel@tonic-gate return (NULL);
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate anon_hs->h_serv = '\0';
7180Sstevel@tonic-gate
7190Sstevel@tonic-gate anon_hsl->h_cnt = 1;
7200Sstevel@tonic-gate anon_hsl->h_hostservs = anon_hs;
7210Sstevel@tonic-gate
7220Sstevel@tonic-gate return (anon_hsl);
7230Sstevel@tonic-gate }
7240Sstevel@tonic-gate
72512393SJan.Kryl@Sun.COM static int
getclientsnames_common(struct netconfig * nconf,struct netbuf ** nbuf,struct nd_hostservlist ** serv)72611211SThomas.Haynes@Sun.COM getclientsnames_common(struct netconfig *nconf, struct netbuf **nbuf,
7270Sstevel@tonic-gate struct nd_hostservlist **serv)
7280Sstevel@tonic-gate {
72912393SJan.Kryl@Sun.COM char host[MAXIPADDRLEN];
73012393SJan.Kryl@Sun.COM
73112393SJan.Kryl@Sun.COM assert(*nbuf != NULL);
7320Sstevel@tonic-gate
7330Sstevel@tonic-gate /*
7340Sstevel@tonic-gate * Use the this API instead of the netdir_getbyaddr()
7350Sstevel@tonic-gate * to avoid service lookup.
7360Sstevel@tonic-gate */
73712393SJan.Kryl@Sun.COM if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf) != 0) {
7380Sstevel@tonic-gate if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
7390Sstevel@tonic-gate struct sockaddr_in *sa;
7400Sstevel@tonic-gate
7410Sstevel@tonic-gate /* LINTED pointer alignment */
7420Sstevel@tonic-gate sa = (struct sockaddr_in *)((*nbuf)->buf);
74312393SJan.Kryl@Sun.COM (void) inet_ntoa_r(sa->sin_addr, host);
7440Sstevel@tonic-gate } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
7450Sstevel@tonic-gate struct sockaddr_in6 *sa;
7460Sstevel@tonic-gate
7470Sstevel@tonic-gate /* LINTED pointer alignment */
7480Sstevel@tonic-gate sa = (struct sockaddr_in6 *)((*nbuf)->buf);
7490Sstevel@tonic-gate (void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr,
75012393SJan.Kryl@Sun.COM host, INET6_ADDRSTRLEN);
75112393SJan.Kryl@Sun.COM } else {
75212393SJan.Kryl@Sun.COM syslog(LOG_ERR, gettext(
75312393SJan.Kryl@Sun.COM "Client's address is neither IPv4 nor IPv6"));
75412393SJan.Kryl@Sun.COM return (EINVAL);
7550Sstevel@tonic-gate }
75611211SThomas.Haynes@Sun.COM
7570Sstevel@tonic-gate *serv = anon_client(host);
75812393SJan.Kryl@Sun.COM if (*serv == NULL)
75912393SJan.Kryl@Sun.COM return (ENOMEM);
76011211SThomas.Haynes@Sun.COM }
76112393SJan.Kryl@Sun.COM
76212393SJan.Kryl@Sun.COM assert(*serv != NULL);
76312393SJan.Kryl@Sun.COM return (0);
76411211SThomas.Haynes@Sun.COM }
76511211SThomas.Haynes@Sun.COM
76611211SThomas.Haynes@Sun.COM /*
76711211SThomas.Haynes@Sun.COM * Get the client's hostname from the copy of the
76811211SThomas.Haynes@Sun.COM * relevant transport handle parts.
76911211SThomas.Haynes@Sun.COM * If the name is not available then return "(anon)".
77011211SThomas.Haynes@Sun.COM */
77112393SJan.Kryl@Sun.COM static int
getclientsnames_lazy(char * netid,struct netbuf ** nbuf,struct nd_hostservlist ** serv)77211211SThomas.Haynes@Sun.COM getclientsnames_lazy(char *netid, struct netbuf **nbuf,
77311211SThomas.Haynes@Sun.COM struct nd_hostservlist **serv)
77411211SThomas.Haynes@Sun.COM {
77511211SThomas.Haynes@Sun.COM struct netconfig *nconf;
77612393SJan.Kryl@Sun.COM int rc;
77711211SThomas.Haynes@Sun.COM
77811211SThomas.Haynes@Sun.COM nconf = getnetconfigent(netid);
77911211SThomas.Haynes@Sun.COM if (nconf == NULL) {
78011211SThomas.Haynes@Sun.COM syslog(LOG_ERR, "%s: getnetconfigent failed", netid);
78111211SThomas.Haynes@Sun.COM *serv = anon_client(NULL);
78212393SJan.Kryl@Sun.COM if (*serv == NULL)
78312393SJan.Kryl@Sun.COM return (ENOMEM);
78412393SJan.Kryl@Sun.COM return (0);
7850Sstevel@tonic-gate }
78611211SThomas.Haynes@Sun.COM
78712393SJan.Kryl@Sun.COM rc = getclientsnames_common(nconf, nbuf, serv);
78811211SThomas.Haynes@Sun.COM freenetconfigent(nconf);
78912393SJan.Kryl@Sun.COM return (rc);
79011211SThomas.Haynes@Sun.COM }
79111211SThomas.Haynes@Sun.COM
79211211SThomas.Haynes@Sun.COM /*
79311211SThomas.Haynes@Sun.COM * Get the client's hostname from the transport handle.
79411211SThomas.Haynes@Sun.COM * If the name is not available then return "(anon)".
79511211SThomas.Haynes@Sun.COM */
79612393SJan.Kryl@Sun.COM int
getclientsnames(SVCXPRT * transp,struct netbuf ** nbuf,struct nd_hostservlist ** serv)79711211SThomas.Haynes@Sun.COM getclientsnames(SVCXPRT *transp, struct netbuf **nbuf,
79811211SThomas.Haynes@Sun.COM struct nd_hostservlist **serv)
79911211SThomas.Haynes@Sun.COM {
80011211SThomas.Haynes@Sun.COM struct netconfig *nconf;
80112393SJan.Kryl@Sun.COM int rc;
80211211SThomas.Haynes@Sun.COM
80311211SThomas.Haynes@Sun.COM nconf = getnetconfigent(transp->xp_netid);
80411211SThomas.Haynes@Sun.COM if (nconf == NULL) {
80511211SThomas.Haynes@Sun.COM syslog(LOG_ERR, "%s: getnetconfigent failed",
80611211SThomas.Haynes@Sun.COM transp->xp_netid);
80711211SThomas.Haynes@Sun.COM *serv = anon_client(NULL);
80812393SJan.Kryl@Sun.COM if (*serv == NULL)
80912393SJan.Kryl@Sun.COM return (ENOMEM);
81012393SJan.Kryl@Sun.COM return (0);
81111211SThomas.Haynes@Sun.COM }
81211211SThomas.Haynes@Sun.COM
81311211SThomas.Haynes@Sun.COM *nbuf = svc_getrpccaller(transp);
81411211SThomas.Haynes@Sun.COM if (*nbuf == NULL) {
81511211SThomas.Haynes@Sun.COM freenetconfigent(nconf);
81611211SThomas.Haynes@Sun.COM *serv = anon_client(NULL);
81712393SJan.Kryl@Sun.COM if (*serv == NULL)
81812393SJan.Kryl@Sun.COM return (ENOMEM);
81912393SJan.Kryl@Sun.COM return (0);
82011211SThomas.Haynes@Sun.COM }
82111211SThomas.Haynes@Sun.COM
82212393SJan.Kryl@Sun.COM rc = getclientsnames_common(nconf, nbuf, serv);
8230Sstevel@tonic-gate freenetconfigent(nconf);
82412393SJan.Kryl@Sun.COM return (rc);
8250Sstevel@tonic-gate }
8260Sstevel@tonic-gate
8270Sstevel@tonic-gate void
log_cant_reply(SVCXPRT * transp)8280Sstevel@tonic-gate log_cant_reply(SVCXPRT *transp)
8290Sstevel@tonic-gate {
8300Sstevel@tonic-gate int saverrno;
8310Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL;
8320Sstevel@tonic-gate register char *host;
8330Sstevel@tonic-gate struct netbuf *nb;
8340Sstevel@tonic-gate
8350Sstevel@tonic-gate saverrno = errno; /* save error code */
83612393SJan.Kryl@Sun.COM if (getclientsnames(transp, &nb, &clnames) != 0)
8370Sstevel@tonic-gate return;
8380Sstevel@tonic-gate host = clnames->h_hostservs->h_host;
8390Sstevel@tonic-gate
8400Sstevel@tonic-gate errno = saverrno;
8410Sstevel@tonic-gate if (errno == 0)
8420Sstevel@tonic-gate syslog(LOG_ERR, "couldn't send reply to %s", host);
8430Sstevel@tonic-gate else
8440Sstevel@tonic-gate syslog(LOG_ERR, "couldn't send reply to %s: %m", host);
8450Sstevel@tonic-gate
8460Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST);
8470Sstevel@tonic-gate }
8480Sstevel@tonic-gate
8490Sstevel@tonic-gate /*
8500Sstevel@tonic-gate * Answer pathconf questions for the mount point fs
8510Sstevel@tonic-gate */
8520Sstevel@tonic-gate static void
mnt_pathconf(struct svc_req * rqstp)8530Sstevel@tonic-gate mnt_pathconf(struct svc_req *rqstp)
8540Sstevel@tonic-gate {
8550Sstevel@tonic-gate SVCXPRT *transp;
8560Sstevel@tonic-gate struct pathcnf p;
8570Sstevel@tonic-gate char *path, rpath[MAXPATHLEN];
8580Sstevel@tonic-gate struct stat st;
8590Sstevel@tonic-gate
8600Sstevel@tonic-gate transp = rqstp->rq_xprt;
8610Sstevel@tonic-gate path = NULL;
8620Sstevel@tonic-gate (void) memset((caddr_t)&p, 0, sizeof (p));
8630Sstevel@tonic-gate
8640Sstevel@tonic-gate if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
8650Sstevel@tonic-gate svcerr_decode(transp);
8660Sstevel@tonic-gate return;
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate if (lstat(path, &st) < 0) {
8690Sstevel@tonic-gate _PC_SET(_PC_ERROR, p.pc_mask);
8700Sstevel@tonic-gate goto done;
8710Sstevel@tonic-gate }
8720Sstevel@tonic-gate /*
8730Sstevel@tonic-gate * Get a path without symbolic links.
8740Sstevel@tonic-gate */
8750Sstevel@tonic-gate if (realpath(path, rpath) == NULL) {
8760Sstevel@tonic-gate syslog(LOG_DEBUG,
8776859Sth199096 "mount request: realpath failed on %s: %m",
8786859Sth199096 path);
8790Sstevel@tonic-gate _PC_SET(_PC_ERROR, p.pc_mask);
8800Sstevel@tonic-gate goto done;
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate (void) memset((caddr_t)&p, 0, sizeof (p));
8830Sstevel@tonic-gate /*
8840Sstevel@tonic-gate * can't ask about devices over NFS
8850Sstevel@tonic-gate */
8860Sstevel@tonic-gate _PC_SET(_PC_MAX_CANON, p.pc_mask);
8870Sstevel@tonic-gate _PC_SET(_PC_MAX_INPUT, p.pc_mask);
8880Sstevel@tonic-gate _PC_SET(_PC_PIPE_BUF, p.pc_mask);
8890Sstevel@tonic-gate _PC_SET(_PC_VDISABLE, p.pc_mask);
8900Sstevel@tonic-gate
8910Sstevel@tonic-gate errno = 0;
8920Sstevel@tonic-gate p.pc_link_max = pathconf(rpath, _PC_LINK_MAX);
8930Sstevel@tonic-gate if (errno)
8940Sstevel@tonic-gate _PC_SET(_PC_LINK_MAX, p.pc_mask);
8950Sstevel@tonic-gate p.pc_name_max = pathconf(rpath, _PC_NAME_MAX);
8960Sstevel@tonic-gate if (errno)
8970Sstevel@tonic-gate _PC_SET(_PC_NAME_MAX, p.pc_mask);
8980Sstevel@tonic-gate p.pc_path_max = pathconf(rpath, _PC_PATH_MAX);
8990Sstevel@tonic-gate if (errno)
9000Sstevel@tonic-gate _PC_SET(_PC_PATH_MAX, p.pc_mask);
9010Sstevel@tonic-gate if (pathconf(rpath, _PC_NO_TRUNC) == 1)
9020Sstevel@tonic-gate _PC_SET(_PC_NO_TRUNC, p.pc_mask);
9030Sstevel@tonic-gate if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1)
9040Sstevel@tonic-gate _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask);
9050Sstevel@tonic-gate
9060Sstevel@tonic-gate done:
9070Sstevel@tonic-gate errno = 0;
9080Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p))
9090Sstevel@tonic-gate log_cant_reply(transp);
9100Sstevel@tonic-gate if (path != NULL)
9110Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate
9140Sstevel@tonic-gate /*
9150Sstevel@tonic-gate * If the rootmount (export) option is specified, the all mount requests for
9160Sstevel@tonic-gate * subdirectories return EACCES.
9170Sstevel@tonic-gate */
9180Sstevel@tonic-gate static int
checkrootmount(share_t * sh,char * rpath)91911211SThomas.Haynes@Sun.COM checkrootmount(share_t *sh, char *rpath)
9200Sstevel@tonic-gate {
9210Sstevel@tonic-gate char *val;
9220Sstevel@tonic-gate
9230Sstevel@tonic-gate if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) {
9240Sstevel@tonic-gate free(val);
9250Sstevel@tonic-gate if (strcmp(sh->sh_path, rpath) != 0)
9260Sstevel@tonic-gate return (0);
9270Sstevel@tonic-gate else
9280Sstevel@tonic-gate return (1);
9290Sstevel@tonic-gate } else
9300Sstevel@tonic-gate return (1);
9310Sstevel@tonic-gate }
9320Sstevel@tonic-gate
9330Sstevel@tonic-gate #define MAX_FLAVORS 128
9340Sstevel@tonic-gate
9350Sstevel@tonic-gate /*
9360Sstevel@tonic-gate * Return only EACCES if client does not have access
9370Sstevel@tonic-gate * to this directory.
9380Sstevel@tonic-gate * "If the server exports only /a/b, an attempt to
9390Sstevel@tonic-gate * mount a/b/c will fail with ENOENT if the directory
9400Sstevel@tonic-gate * does not exist"... However, if the client
9410Sstevel@tonic-gate * does not have access to /a/b, an attacker can
9420Sstevel@tonic-gate * determine whether the directory exists.
9430Sstevel@tonic-gate * This routine checks either existence of the file or
9440Sstevel@tonic-gate * existence of the file name entry in the mount table.
9450Sstevel@tonic-gate * If the file exists and there is no file name entry,
9460Sstevel@tonic-gate * the error returned should be EACCES.
9470Sstevel@tonic-gate * If the file does not exist, it must be determined
9480Sstevel@tonic-gate * whether the client has access to a parent
9490Sstevel@tonic-gate * directory. If the client has access to a parent
9500Sstevel@tonic-gate * directory, the error returned should be ENOENT,
9510Sstevel@tonic-gate * otherwise EACCES.
9520Sstevel@tonic-gate */
9530Sstevel@tonic-gate static int
mount_enoent_error(SVCXPRT * transp,char * path,char * rpath,struct nd_hostservlist ** clnames,struct netbuf ** nb,int * flavor_list)95411211SThomas.Haynes@Sun.COM mount_enoent_error(SVCXPRT *transp, char *path, char *rpath,
95511211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, struct netbuf **nb, int *flavor_list)
9560Sstevel@tonic-gate {
9570Sstevel@tonic-gate char *checkpath, *dp;
95811211SThomas.Haynes@Sun.COM share_t *sh = NULL;
9590Sstevel@tonic-gate int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0;
9600Sstevel@tonic-gate int flavor_count;
9610Sstevel@tonic-gate
9620Sstevel@tonic-gate checkpath = strdup(path);
9630Sstevel@tonic-gate if (checkpath == NULL) {
9640Sstevel@tonic-gate syslog(LOG_ERR, "mount_enoent: no memory");
9650Sstevel@tonic-gate return (EACCES);
9660Sstevel@tonic-gate }
9670Sstevel@tonic-gate
9680Sstevel@tonic-gate /* CONSTCOND */
9690Sstevel@tonic-gate while (1) {
9700Sstevel@tonic-gate if (sh) {
9710Sstevel@tonic-gate sharefree(sh);
9720Sstevel@tonic-gate sh = NULL;
9730Sstevel@tonic-gate }
97411211SThomas.Haynes@Sun.COM
9750Sstevel@tonic-gate if ((sh = findentry(rpath)) == NULL &&
9766859Sth199096 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
9770Sstevel@tonic-gate /*
9780Sstevel@tonic-gate * There is no file name entry.
9790Sstevel@tonic-gate * If the file (with symbolic links resolved) exists,
9800Sstevel@tonic-gate * the error returned should be EACCES.
9810Sstevel@tonic-gate */
9820Sstevel@tonic-gate if (realpath_error == 0)
9830Sstevel@tonic-gate break;
9840Sstevel@tonic-gate } else if (checkrootmount(sh, rpath) == 0) {
9850Sstevel@tonic-gate /*
9860Sstevel@tonic-gate * This is a "nosub" only export, in which case,
9870Sstevel@tonic-gate * mounting subdirectories isn't allowed.
9880Sstevel@tonic-gate * If the file (with symbolic links resolved) exists,
9890Sstevel@tonic-gate * the error returned should be EACCES.
9900Sstevel@tonic-gate */
9910Sstevel@tonic-gate if (realpath_error == 0)
9920Sstevel@tonic-gate break;
9930Sstevel@tonic-gate } else {
9940Sstevel@tonic-gate /*
9950Sstevel@tonic-gate * Check permissions in mount table.
9960Sstevel@tonic-gate */
9970Sstevel@tonic-gate if (newopts(sh->sh_opts))
99811211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_new(sh,
99911211SThomas.Haynes@Sun.COM transp, nb, clnames, flavor_list);
10000Sstevel@tonic-gate else
100111211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_old(sh,
100211211SThomas.Haynes@Sun.COM transp, nb, clnames, flavor_list);
10030Sstevel@tonic-gate if (flavor_count != 0) {
10040Sstevel@tonic-gate /*
10050Sstevel@tonic-gate * Found entry in table and
10060Sstevel@tonic-gate * client has correct permissions.
10070Sstevel@tonic-gate */
10080Sstevel@tonic-gate reply_error = ENOENT;
10090Sstevel@tonic-gate break;
10100Sstevel@tonic-gate }
10110Sstevel@tonic-gate }
101211211SThomas.Haynes@Sun.COM
10130Sstevel@tonic-gate /*
10140Sstevel@tonic-gate * Check all parent directories.
10150Sstevel@tonic-gate */
10160Sstevel@tonic-gate dp = strrchr(checkpath, '/');
10170Sstevel@tonic-gate if (dp == NULL)
10180Sstevel@tonic-gate break;
10190Sstevel@tonic-gate *dp = '\0';
10200Sstevel@tonic-gate if (strlen(checkpath) == 0)
10210Sstevel@tonic-gate break;
10220Sstevel@tonic-gate /*
10230Sstevel@tonic-gate * Get the real path (no symbolic links in it)
10240Sstevel@tonic-gate */
10250Sstevel@tonic-gate if (realpath(checkpath, rpath) == NULL) {
10260Sstevel@tonic-gate if (errno != ENOENT)
10270Sstevel@tonic-gate break;
10280Sstevel@tonic-gate } else {
10290Sstevel@tonic-gate realpath_error = 0;
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate
10330Sstevel@tonic-gate if (sh)
10340Sstevel@tonic-gate sharefree(sh);
10350Sstevel@tonic-gate free(checkpath);
10360Sstevel@tonic-gate return (reply_error);
10370Sstevel@tonic-gate }
10380Sstevel@tonic-gate
10390Sstevel@tonic-gate /*
104011211SThomas.Haynes@Sun.COM * We need to inform the caller whether or not we were
104111211SThomas.Haynes@Sun.COM * able to add a node to the queue. If we are not, then
104211211SThomas.Haynes@Sun.COM * it is up to the caller to go ahead and log the data.
104311211SThomas.Haynes@Sun.COM */
104411211SThomas.Haynes@Sun.COM static int
enqueue_logging_data(char * host,SVCXPRT * transp,char * path,char * rpath,int status,int error)104511211SThomas.Haynes@Sun.COM enqueue_logging_data(char *host, SVCXPRT *transp, char *path,
104611211SThomas.Haynes@Sun.COM char *rpath, int status, int error)
104711211SThomas.Haynes@Sun.COM {
104811211SThomas.Haynes@Sun.COM logging_data *lq;
104911211SThomas.Haynes@Sun.COM struct netbuf *nb;
105011211SThomas.Haynes@Sun.COM
105111211SThomas.Haynes@Sun.COM lq = (logging_data *)calloc(1, sizeof (logging_data));
105211211SThomas.Haynes@Sun.COM if (lq == NULL)
105311211SThomas.Haynes@Sun.COM goto cleanup;
105411211SThomas.Haynes@Sun.COM
105511211SThomas.Haynes@Sun.COM /*
105611211SThomas.Haynes@Sun.COM * We might not yet have the host...
105711211SThomas.Haynes@Sun.COM */
105811211SThomas.Haynes@Sun.COM if (host) {
105911211SThomas.Haynes@Sun.COM DTRACE_PROBE1(mountd, log_host, host);
106011211SThomas.Haynes@Sun.COM lq->ld_host = strdup(host);
106111211SThomas.Haynes@Sun.COM if (lq->ld_host == NULL)
106211211SThomas.Haynes@Sun.COM goto cleanup;
106311211SThomas.Haynes@Sun.COM } else {
106411211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, log_no_host);
106511211SThomas.Haynes@Sun.COM
106611211SThomas.Haynes@Sun.COM lq->ld_netid = strdup(transp->xp_netid);
106711211SThomas.Haynes@Sun.COM if (lq->ld_netid == NULL)
106811211SThomas.Haynes@Sun.COM goto cleanup;
106911211SThomas.Haynes@Sun.COM
107011211SThomas.Haynes@Sun.COM lq->ld_nb = calloc(1, sizeof (struct netbuf));
107111211SThomas.Haynes@Sun.COM if (lq->ld_nb == NULL)
107211211SThomas.Haynes@Sun.COM goto cleanup;
107311211SThomas.Haynes@Sun.COM
107411211SThomas.Haynes@Sun.COM nb = svc_getrpccaller(transp);
107511211SThomas.Haynes@Sun.COM if (nb == NULL) {
107611211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, e__nb__enqueue);
107711211SThomas.Haynes@Sun.COM goto cleanup;
107811211SThomas.Haynes@Sun.COM }
107911211SThomas.Haynes@Sun.COM
108011211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, nb_set_enqueue);
108111211SThomas.Haynes@Sun.COM
108211211SThomas.Haynes@Sun.COM lq->ld_nb->maxlen = nb->maxlen;
108311211SThomas.Haynes@Sun.COM lq->ld_nb->len = nb->len;
108411211SThomas.Haynes@Sun.COM
108511211SThomas.Haynes@Sun.COM lq->ld_nb->buf = malloc(lq->ld_nb->len);
108611211SThomas.Haynes@Sun.COM if (lq->ld_nb->buf == NULL)
108711211SThomas.Haynes@Sun.COM goto cleanup;
108811211SThomas.Haynes@Sun.COM
108911211SThomas.Haynes@Sun.COM bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len);
109011211SThomas.Haynes@Sun.COM }
109111211SThomas.Haynes@Sun.COM
109211211SThomas.Haynes@Sun.COM lq->ld_path = strdup(path);
109311211SThomas.Haynes@Sun.COM if (lq->ld_path == NULL)
109411211SThomas.Haynes@Sun.COM goto cleanup;
109511211SThomas.Haynes@Sun.COM
109611211SThomas.Haynes@Sun.COM if (!error) {
109711211SThomas.Haynes@Sun.COM lq->ld_rpath = strdup(rpath);
109811211SThomas.Haynes@Sun.COM if (lq->ld_rpath == NULL)
109911211SThomas.Haynes@Sun.COM goto cleanup;
110011211SThomas.Haynes@Sun.COM }
110111211SThomas.Haynes@Sun.COM
110211211SThomas.Haynes@Sun.COM lq->ld_status = status;
110311211SThomas.Haynes@Sun.COM
110411211SThomas.Haynes@Sun.COM /*
110511211SThomas.Haynes@Sun.COM * Add to the tail of the logging queue.
110611211SThomas.Haynes@Sun.COM */
110711211SThomas.Haynes@Sun.COM (void) mutex_lock(&logging_queue_lock);
110811211SThomas.Haynes@Sun.COM if (logging_tail == NULL) {
110911211SThomas.Haynes@Sun.COM logging_tail = logging_head = lq;
111011211SThomas.Haynes@Sun.COM } else {
111111211SThomas.Haynes@Sun.COM logging_tail->ld_next = lq;
111211211SThomas.Haynes@Sun.COM logging_tail = lq;
111311211SThomas.Haynes@Sun.COM }
111411211SThomas.Haynes@Sun.COM (void) cond_signal(&logging_queue_cv);
111511211SThomas.Haynes@Sun.COM (void) mutex_unlock(&logging_queue_lock);
111611211SThomas.Haynes@Sun.COM
111711211SThomas.Haynes@Sun.COM return (TRUE);
111811211SThomas.Haynes@Sun.COM
111911211SThomas.Haynes@Sun.COM cleanup:
112011211SThomas.Haynes@Sun.COM
112111211SThomas.Haynes@Sun.COM free_logging_data(lq);
112211211SThomas.Haynes@Sun.COM
112311211SThomas.Haynes@Sun.COM return (FALSE);
112411211SThomas.Haynes@Sun.COM }
112511211SThomas.Haynes@Sun.COM
112611211SThomas.Haynes@Sun.COM /*
11270Sstevel@tonic-gate * Check mount requests, add to mounted list if ok
11280Sstevel@tonic-gate */
112911211SThomas.Haynes@Sun.COM static int
mount(struct svc_req * rqstp)11300Sstevel@tonic-gate mount(struct svc_req *rqstp)
11310Sstevel@tonic-gate {
11320Sstevel@tonic-gate SVCXPRT *transp;
11331610Sthurlow int version, vers;
11340Sstevel@tonic-gate struct fhstatus fhs;
11350Sstevel@tonic-gate struct mountres3 mountres3;
11361610Sthurlow char fh[FHSIZE3];
11371610Sthurlow int len = FHSIZE3;
11380Sstevel@tonic-gate char *path, rpath[MAXPATHLEN];
113911211SThomas.Haynes@Sun.COM share_t *sh = NULL;
11400Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL;
11410Sstevel@tonic-gate char *host = NULL;
114211211SThomas.Haynes@Sun.COM int error = 0, lofs_tried = 0, enqueued;
11430Sstevel@tonic-gate int flavor_list[MAX_FLAVORS];
11440Sstevel@tonic-gate int flavor_count;
114511211SThomas.Haynes@Sun.COM struct netbuf *nb = NULL;
11464971Sjarrett ucred_t *uc = NULL;
11470Sstevel@tonic-gate
114811211SThomas.Haynes@Sun.COM int audit_status;
114911211SThomas.Haynes@Sun.COM
11500Sstevel@tonic-gate transp = rqstp->rq_xprt;
11510Sstevel@tonic-gate version = rqstp->rq_vers;
11520Sstevel@tonic-gate path = NULL;
11530Sstevel@tonic-gate
11540Sstevel@tonic-gate if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
11550Sstevel@tonic-gate svcerr_decode(transp);
115611211SThomas.Haynes@Sun.COM return (EACCES);
11570Sstevel@tonic-gate }
11580Sstevel@tonic-gate
115911211SThomas.Haynes@Sun.COM /*
116011211SThomas.Haynes@Sun.COM * Put off getting the name for the client until we
116111211SThomas.Haynes@Sun.COM * need it. This is a performance gain. If we are logging,
116211211SThomas.Haynes@Sun.COM * then we don't care about performance and might as well
116311211SThomas.Haynes@Sun.COM * get the host name now in case we need to spit out an
116411211SThomas.Haynes@Sun.COM * error message.
116511211SThomas.Haynes@Sun.COM */
116611211SThomas.Haynes@Sun.COM if (verbose) {
116711211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_verbose);
116812393SJan.Kryl@Sun.COM if (getclientsnames(transp, &nb, &clnames) != 0) {
116911211SThomas.Haynes@Sun.COM /*
117011211SThomas.Haynes@Sun.COM * We failed to get a name for the client, even
117111211SThomas.Haynes@Sun.COM * 'anon', probably because we ran out of memory.
117211211SThomas.Haynes@Sun.COM * In this situation it doesn't make sense to
117311211SThomas.Haynes@Sun.COM * allow the mount to succeed.
117411211SThomas.Haynes@Sun.COM */
117511211SThomas.Haynes@Sun.COM error = EACCES;
117611211SThomas.Haynes@Sun.COM goto reply;
117711211SThomas.Haynes@Sun.COM }
117811211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host;
11790Sstevel@tonic-gate }
11800Sstevel@tonic-gate
11810Sstevel@tonic-gate /*
11820Sstevel@tonic-gate * If the version being used is less than the minimum version,
11830Sstevel@tonic-gate * the filehandle translation should not be provided to the
11840Sstevel@tonic-gate * client.
11850Sstevel@tonic-gate */
11860Sstevel@tonic-gate if (rejecting || version < mount_vers_min) {
11870Sstevel@tonic-gate if (verbose)
11880Sstevel@tonic-gate syslog(LOG_NOTICE, "Rejected mount: %s for %s",
11896859Sth199096 host, path);
11900Sstevel@tonic-gate error = EACCES;
11910Sstevel@tonic-gate goto reply;
11920Sstevel@tonic-gate }
11930Sstevel@tonic-gate
11940Sstevel@tonic-gate /*
11954971Sjarrett * Trusted Extension doesn't support nfsv2. nfsv2 client
11964971Sjarrett * uses MOUNT protocol v1 and v2. To prevent circumventing
11974971Sjarrett * TX label policy via using nfsv2 client, reject a mount
11984971Sjarrett * request with version less than 3 and log an error.
11991676Sjpk */
12001676Sjpk if (is_system_labeled()) {
12014971Sjarrett if (version < 3) {
12024971Sjarrett if (verbose)
12034971Sjarrett syslog(LOG_ERR,
12044971Sjarrett "Rejected mount: TX doesn't support NFSv2");
12054971Sjarrett error = EACCES;
12064971Sjarrett goto reply;
12074971Sjarrett }
12081676Sjpk }
12091676Sjpk
12101676Sjpk /*
12110Sstevel@tonic-gate * Get the real path (no symbolic links in it)
12120Sstevel@tonic-gate */
12130Sstevel@tonic-gate if (realpath(path, rpath) == NULL) {
12140Sstevel@tonic-gate error = errno;
12150Sstevel@tonic-gate if (verbose)
12160Sstevel@tonic-gate syslog(LOG_ERR,
12176859Sth199096 "mount request: realpath: %s: %m", path);
12180Sstevel@tonic-gate if (error == ENOENT)
121911211SThomas.Haynes@Sun.COM error = mount_enoent_error(transp, path, rpath,
122011211SThomas.Haynes@Sun.COM &clnames, &nb, flavor_list);
12210Sstevel@tonic-gate goto reply;
12220Sstevel@tonic-gate }
12230Sstevel@tonic-gate
12240Sstevel@tonic-gate if ((sh = findentry(rpath)) == NULL &&
12256859Sth199096 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) {
12260Sstevel@tonic-gate error = EACCES;
12270Sstevel@tonic-gate goto reply;
12280Sstevel@tonic-gate }
12290Sstevel@tonic-gate
12300Sstevel@tonic-gate /*
12310Sstevel@tonic-gate * Check if this is a "nosub" only export, in which case, mounting
12320Sstevel@tonic-gate * subdirectories isn't allowed. Bug 1184573.
12330Sstevel@tonic-gate */
12340Sstevel@tonic-gate if (checkrootmount(sh, rpath) == 0) {
12350Sstevel@tonic-gate error = EACCES;
12360Sstevel@tonic-gate goto reply;
12370Sstevel@tonic-gate }
12380Sstevel@tonic-gate
12390Sstevel@tonic-gate if (newopts(sh->sh_opts))
124011211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_new(sh, transp, &nb, &clnames,
12416859Sth199096 flavor_list);
12420Sstevel@tonic-gate else
124311211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_old(sh, transp, &nb, &clnames,
12446859Sth199096 flavor_list);
12450Sstevel@tonic-gate
124611211SThomas.Haynes@Sun.COM if (clnames)
124711211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host;
124811211SThomas.Haynes@Sun.COM
12490Sstevel@tonic-gate if (flavor_count == 0) {
12500Sstevel@tonic-gate error = EACCES;
12510Sstevel@tonic-gate goto reply;
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate
12540Sstevel@tonic-gate /*
12554971Sjarrett * Check MAC policy here. The server side policy should be
12564971Sjarrett * consistent with client side mount policy, i.e.
12574971Sjarrett * - we disallow an admin_low unlabeled client to mount
12584971Sjarrett * - we disallow mount from a lower labeled client.
12594971Sjarrett */
12604971Sjarrett if (is_system_labeled()) {
12614971Sjarrett m_label_t *clabel = NULL;
12624971Sjarrett m_label_t *slabel = NULL;
12634971Sjarrett m_label_t admin_low;
12644971Sjarrett
12654971Sjarrett if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) {
12664971Sjarrett syslog(LOG_ERR,
12674971Sjarrett "mount request: Failed to get caller's ucred : %m");
12684971Sjarrett error = EACCES;
12694971Sjarrett goto reply;
12704971Sjarrett }
12714971Sjarrett if ((clabel = ucred_getlabel(uc)) == NULL) {
12724971Sjarrett syslog(LOG_ERR,
12734971Sjarrett "mount request: can't get client label from ucred");
12744971Sjarrett error = EACCES;
12754971Sjarrett goto reply;
12764971Sjarrett }
12774971Sjarrett
12784971Sjarrett bsllow(&admin_low);
12794971Sjarrett if (blequal(&admin_low, clabel)) {
12804971Sjarrett struct sockaddr *ca;
12814971Sjarrett tsol_tpent_t *tp;
12824971Sjarrett
12834971Sjarrett ca = (struct sockaddr *)(void *)svc_getrpccaller(
12844971Sjarrett rqstp->rq_xprt)->buf;
12854971Sjarrett if (ca == NULL) {
12864971Sjarrett error = EACCES;
12874971Sjarrett goto reply;
12884971Sjarrett }
12894971Sjarrett /*
12904971Sjarrett * get trusted network template associated
12914971Sjarrett * with the client.
12924971Sjarrett */
12934971Sjarrett tp = get_client_template(ca);
12944971Sjarrett if (tp == NULL || tp->host_type != SUN_CIPSO) {
12954971Sjarrett if (tp != NULL)
12964971Sjarrett tsol_freetpent(tp);
12974971Sjarrett error = EACCES;
12984971Sjarrett goto reply;
12994971Sjarrett }
13004971Sjarrett tsol_freetpent(tp);
13014971Sjarrett } else {
13024971Sjarrett if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) {
13034971Sjarrett error = EACCES;
13044971Sjarrett goto reply;
13054971Sjarrett }
13064971Sjarrett
13074971Sjarrett if (getlabel(rpath, slabel) != 0) {
13084971Sjarrett m_label_free(slabel);
13094971Sjarrett error = EACCES;
13104971Sjarrett goto reply;
13114971Sjarrett }
13124971Sjarrett
13134971Sjarrett if (!bldominates(clabel, slabel)) {
13144971Sjarrett m_label_free(slabel);
13154971Sjarrett error = EACCES;
13164971Sjarrett goto reply;
13174971Sjarrett }
13184971Sjarrett m_label_free(slabel);
13194971Sjarrett }
13204971Sjarrett }
13214971Sjarrett
13224971Sjarrett /*
13230Sstevel@tonic-gate * Now get the filehandle.
13240Sstevel@tonic-gate *
13251610Sthurlow * NFS V2 clients get a 32 byte filehandle.
13261610Sthurlow * NFS V3 clients get a 32 or 64 byte filehandle, depending on
13271610Sthurlow * the embedded FIDs.
13280Sstevel@tonic-gate */
13291610Sthurlow vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION;
13300Sstevel@tonic-gate
13310Sstevel@tonic-gate /* LINTED pointer alignment */
13321610Sthurlow while (nfs_getfh(rpath, vers, &len, fh) < 0) {
13330Sstevel@tonic-gate if (errno == EINVAL &&
13346859Sth199096 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) {
13350Sstevel@tonic-gate errno = 0;
13360Sstevel@tonic-gate continue;
13370Sstevel@tonic-gate }
13380Sstevel@tonic-gate error = errno == EINVAL ? EACCES : errno;
13390Sstevel@tonic-gate syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m",
13406859Sth199096 path);
13410Sstevel@tonic-gate break;
13420Sstevel@tonic-gate }
13430Sstevel@tonic-gate
13441610Sthurlow if (version == MOUNTVERS3) {
13451610Sthurlow mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len;
13461610Sthurlow mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh;
13471610Sthurlow } else {
13481610Sthurlow bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE);
13491610Sthurlow }
13501610Sthurlow
13510Sstevel@tonic-gate reply:
13524971Sjarrett if (uc != NULL)
13534971Sjarrett ucred_free(uc);
135411211SThomas.Haynes@Sun.COM
13550Sstevel@tonic-gate switch (version) {
13560Sstevel@tonic-gate case MOUNTVERS:
13570Sstevel@tonic-gate case MOUNTVERS_POSIX:
13580Sstevel@tonic-gate if (error == EINVAL)
13590Sstevel@tonic-gate fhs.fhs_status = NFSERR_ACCES;
13600Sstevel@tonic-gate else if (error == EREMOTE)
13610Sstevel@tonic-gate fhs.fhs_status = NFSERR_REMOTE;
13620Sstevel@tonic-gate else
13630Sstevel@tonic-gate fhs.fhs_status = error;
136411211SThomas.Haynes@Sun.COM
13650Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs))
13660Sstevel@tonic-gate log_cant_reply(transp);
136711211SThomas.Haynes@Sun.COM
136811211SThomas.Haynes@Sun.COM audit_status = fhs.fhs_status;
13690Sstevel@tonic-gate break;
13700Sstevel@tonic-gate
13710Sstevel@tonic-gate case MOUNTVERS3:
13720Sstevel@tonic-gate if (!error) {
13730Sstevel@tonic-gate mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val =
13746859Sth199096 flavor_list;
13750Sstevel@tonic-gate mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len =
13766859Sth199096 flavor_count;
13770Sstevel@tonic-gate
13780Sstevel@tonic-gate } else if (error == ENAMETOOLONG)
13790Sstevel@tonic-gate error = MNT3ERR_NAMETOOLONG;
13800Sstevel@tonic-gate
13810Sstevel@tonic-gate mountres3.fhs_status = error;
13820Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3))
13830Sstevel@tonic-gate log_cant_reply(transp);
13840Sstevel@tonic-gate
138511211SThomas.Haynes@Sun.COM audit_status = mountres3.fhs_status;
13860Sstevel@tonic-gate break;
13870Sstevel@tonic-gate }
13880Sstevel@tonic-gate
13890Sstevel@tonic-gate if (verbose)
13900Sstevel@tonic-gate syslog(LOG_NOTICE, "MOUNT: %s %s %s",
13916859Sth199096 (host == NULL) ? "unknown host" : host,
13926859Sth199096 error ? "denied" : "mounted", path);
13930Sstevel@tonic-gate
139411211SThomas.Haynes@Sun.COM /*
139511211SThomas.Haynes@Sun.COM * If we can not create a queue entry, go ahead and do it
139611211SThomas.Haynes@Sun.COM * in the context of this thread.
139711211SThomas.Haynes@Sun.COM */
139811211SThomas.Haynes@Sun.COM enqueued = enqueue_logging_data(host, transp, path, rpath,
139911211SThomas.Haynes@Sun.COM audit_status, error);
140011211SThomas.Haynes@Sun.COM if (enqueued == FALSE) {
140111211SThomas.Haynes@Sun.COM if (host == NULL) {
140211211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_in_thread);
140312393SJan.Kryl@Sun.COM if (getclientsnames(transp, &nb, &clnames) == 0)
140411211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host;
140511211SThomas.Haynes@Sun.COM }
140611211SThomas.Haynes@Sun.COM
140711211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, logged_in_thread);
140811211SThomas.Haynes@Sun.COM audit_mountd_mount(host, path, audit_status); /* BSM */
140911211SThomas.Haynes@Sun.COM if (!error)
141011211SThomas.Haynes@Sun.COM mntlist_new(host, rpath); /* add entry to mount list */
141111211SThomas.Haynes@Sun.COM }
141211211SThomas.Haynes@Sun.COM
14130Sstevel@tonic-gate if (path != NULL)
14140Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
14150Sstevel@tonic-gate
14160Sstevel@tonic-gate done:
14170Sstevel@tonic-gate if (sh)
14180Sstevel@tonic-gate sharefree(sh);
14190Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST);
142011211SThomas.Haynes@Sun.COM
142111211SThomas.Haynes@Sun.COM return (error);
14220Sstevel@tonic-gate }
14230Sstevel@tonic-gate
142412157SSam.Falkner@Sun.COM /*
142512157SSam.Falkner@Sun.COM * Determine whether two paths are within the same file system.
142612157SSam.Falkner@Sun.COM * Returns nonzero (true) if paths are the same, zero (false) if
142712157SSam.Falkner@Sun.COM * they are different. If an error occurs, return false.
142812157SSam.Falkner@Sun.COM *
142912157SSam.Falkner@Sun.COM * Use the actual FSID if it's available (via getattrat()); otherwise,
143012157SSam.Falkner@Sun.COM * fall back on st_dev.
143112157SSam.Falkner@Sun.COM *
143212157SSam.Falkner@Sun.COM * With ZFS snapshots, st_dev differs from the regular file system
143312157SSam.Falkner@Sun.COM * versus the snapshot. But the fsid is the same throughout. Thus
143412157SSam.Falkner@Sun.COM * the fsid is a better test.
143512157SSam.Falkner@Sun.COM */
143612157SSam.Falkner@Sun.COM static int
same_file_system(const char * path1,const char * path2)143712157SSam.Falkner@Sun.COM same_file_system(const char *path1, const char *path2)
143812157SSam.Falkner@Sun.COM {
143912157SSam.Falkner@Sun.COM uint64_t fsid1, fsid2;
144012157SSam.Falkner@Sun.COM struct stat64 st1, st2;
144112157SSam.Falkner@Sun.COM nvlist_t *nvl1 = NULL;
144212157SSam.Falkner@Sun.COM nvlist_t *nvl2 = NULL;
144312157SSam.Falkner@Sun.COM
144412157SSam.Falkner@Sun.COM if ((getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path1, &nvl1) == 0) &&
144512157SSam.Falkner@Sun.COM (getattrat(AT_FDCWD, XATTR_VIEW_READONLY, path2, &nvl2) == 0) &&
144612157SSam.Falkner@Sun.COM (nvlist_lookup_uint64(nvl1, A_FSID, &fsid1) == 0) &&
144712157SSam.Falkner@Sun.COM (nvlist_lookup_uint64(nvl2, A_FSID, &fsid2) == 0)) {
144812157SSam.Falkner@Sun.COM nvlist_free(nvl1);
144912157SSam.Falkner@Sun.COM nvlist_free(nvl2);
145012157SSam.Falkner@Sun.COM /*
145112157SSam.Falkner@Sun.COM * We have found fsid's for both paths.
145212157SSam.Falkner@Sun.COM */
145312157SSam.Falkner@Sun.COM
145412157SSam.Falkner@Sun.COM if (fsid1 == fsid2)
145512157SSam.Falkner@Sun.COM return (B_TRUE);
145612157SSam.Falkner@Sun.COM
145712157SSam.Falkner@Sun.COM return (B_FALSE);
145812157SSam.Falkner@Sun.COM }
145912157SSam.Falkner@Sun.COM
146012157SSam.Falkner@Sun.COM if (nvl1 != NULL)
146112157SSam.Falkner@Sun.COM nvlist_free(nvl1);
146212157SSam.Falkner@Sun.COM if (nvl2 != NULL)
146312157SSam.Falkner@Sun.COM nvlist_free(nvl2);
146412157SSam.Falkner@Sun.COM
146512157SSam.Falkner@Sun.COM /*
146612157SSam.Falkner@Sun.COM * We were unable to find fsid's for at least one of the paths.
146712157SSam.Falkner@Sun.COM * fall back on st_dev.
146812157SSam.Falkner@Sun.COM */
146912157SSam.Falkner@Sun.COM
147012157SSam.Falkner@Sun.COM if (stat64(path1, &st1) < 0) {
147112157SSam.Falkner@Sun.COM syslog(LOG_NOTICE, "%s: %m", path1);
147212157SSam.Falkner@Sun.COM return (B_FALSE);
147312157SSam.Falkner@Sun.COM }
147412157SSam.Falkner@Sun.COM if (stat64(path2, &st2) < 0) {
147512157SSam.Falkner@Sun.COM syslog(LOG_NOTICE, "%s: %m", path2);
147612157SSam.Falkner@Sun.COM return (B_FALSE);
147712157SSam.Falkner@Sun.COM }
147812157SSam.Falkner@Sun.COM
147912157SSam.Falkner@Sun.COM if (st1.st_dev == st2.st_dev)
148012157SSam.Falkner@Sun.COM return (B_TRUE);
148112157SSam.Falkner@Sun.COM
148212157SSam.Falkner@Sun.COM return (B_FALSE);
148312157SSam.Falkner@Sun.COM }
148412157SSam.Falkner@Sun.COM
148511211SThomas.Haynes@Sun.COM share_t *
findentry(char * path)14860Sstevel@tonic-gate findentry(char *path)
14870Sstevel@tonic-gate {
148811211SThomas.Haynes@Sun.COM share_t *sh = NULL;
14890Sstevel@tonic-gate struct sh_list *shp;
14900Sstevel@tonic-gate register char *p1, *p2;
14910Sstevel@tonic-gate
14920Sstevel@tonic-gate check_sharetab();
14930Sstevel@tonic-gate
14940Sstevel@tonic-gate (void) rw_rdlock(&sharetab_lock);
14950Sstevel@tonic-gate
14960Sstevel@tonic-gate for (shp = share_list; shp; shp = shp->shl_next) {
14970Sstevel@tonic-gate sh = shp->shl_sh;
14980Sstevel@tonic-gate for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++)
14990Sstevel@tonic-gate if (*p1 == '\0')
15000Sstevel@tonic-gate goto done; /* exact match */
15010Sstevel@tonic-gate
15020Sstevel@tonic-gate /*
15030Sstevel@tonic-gate * Now compare the pathnames for three cases:
15040Sstevel@tonic-gate *
15050Sstevel@tonic-gate * Parent: /export/foo (no trailing slash on parent)
15060Sstevel@tonic-gate * Child: /export/foo/bar
15070Sstevel@tonic-gate *
15080Sstevel@tonic-gate * Parent: /export/foo/ (trailing slash on parent)
15090Sstevel@tonic-gate * Child: /export/foo/bar
15100Sstevel@tonic-gate *
15110Sstevel@tonic-gate * Parent: /export/foo/ (no trailing slash on child)
15120Sstevel@tonic-gate * Child: /export/foo
15130Sstevel@tonic-gate */
15140Sstevel@tonic-gate if ((*p1 == '\0' && *p2 == '/') ||
15150Sstevel@tonic-gate (*p1 == '\0' && *(p1-1) == '/') ||
15160Sstevel@tonic-gate (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) {
15170Sstevel@tonic-gate /*
151812157SSam.Falkner@Sun.COM * We have a subdirectory. Test whether the
151912157SSam.Falkner@Sun.COM * subdirectory is in the same file system.
15200Sstevel@tonic-gate */
152112157SSam.Falkner@Sun.COM if (same_file_system(path, sh->sh_path))
15220Sstevel@tonic-gate goto done;
15230Sstevel@tonic-gate }
15240Sstevel@tonic-gate }
15250Sstevel@tonic-gate done:
15260Sstevel@tonic-gate sh = shp ? sharedup(sh) : NULL;
15270Sstevel@tonic-gate
15280Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock);
15290Sstevel@tonic-gate
15300Sstevel@tonic-gate return (sh);
15310Sstevel@tonic-gate }
15320Sstevel@tonic-gate
15330Sstevel@tonic-gate
15340Sstevel@tonic-gate static int
is_substring(char ** mntp,char ** path)15350Sstevel@tonic-gate is_substring(char **mntp, char **path)
15360Sstevel@tonic-gate {
15370Sstevel@tonic-gate char *p1 = *mntp, *p2 = *path;
15380Sstevel@tonic-gate
15390Sstevel@tonic-gate if (*p1 == '\0' && *p2 == '\0') /* exact match */
15400Sstevel@tonic-gate return (1);
15410Sstevel@tonic-gate else if (*p1 == '\0' && *p2 == '/')
15420Sstevel@tonic-gate return (1);
15430Sstevel@tonic-gate else if (*p1 == '\0' && *(p1-1) == '/') {
15440Sstevel@tonic-gate *path = --p2; /* we need the slash in p2 */
15450Sstevel@tonic-gate return (1);
15460Sstevel@tonic-gate } else if (*p2 == '\0') {
15470Sstevel@tonic-gate while (*p1 == '/')
15480Sstevel@tonic-gate p1++;
15490Sstevel@tonic-gate if (*p1 == '\0') /* exact match */
15500Sstevel@tonic-gate return (1);
15510Sstevel@tonic-gate }
15520Sstevel@tonic-gate return (0);
15530Sstevel@tonic-gate }
15540Sstevel@tonic-gate
15550Sstevel@tonic-gate /*
15560Sstevel@tonic-gate * find_lofsentry() searches for the real path which this requested LOFS path
15570Sstevel@tonic-gate * (rpath) shadows. If found, it will return the sharetab entry of
15580Sstevel@tonic-gate * the real path that corresponds to the LOFS path.
15590Sstevel@tonic-gate * We first search mnttab to see if the requested path is an automounted
15600Sstevel@tonic-gate * path. If it is an automounted path, it will trigger the mount by stat()ing
15610Sstevel@tonic-gate * the requested path. Note that it is important to check that this path is
15620Sstevel@tonic-gate * actually an automounted path, otherwise we would stat() a path which may
15630Sstevel@tonic-gate * turn out to be NFS and block indefinitely on a dead server. The automounter
15640Sstevel@tonic-gate * times-out if the server is dead, so there's no risk of hanging this
15650Sstevel@tonic-gate * thread waiting for stat().
15660Sstevel@tonic-gate * After the mount has been triggered (if necessary), we look for a
15670Sstevel@tonic-gate * mountpoint of type LOFS (by searching /etc/mnttab again) which
15680Sstevel@tonic-gate * is a substring of the rpath. If found, we construct a new path by
15690Sstevel@tonic-gate * concatenating the mnt_special and the remaining of rpath, call findentry()
15700Sstevel@tonic-gate * to make sure the 'real path' is shared.
15710Sstevel@tonic-gate */
157211211SThomas.Haynes@Sun.COM static share_t *
find_lofsentry(char * rpath,int * done_flag)15730Sstevel@tonic-gate find_lofsentry(char *rpath, int *done_flag)
15740Sstevel@tonic-gate {
15750Sstevel@tonic-gate struct stat r_stbuf;
15760Sstevel@tonic-gate mntlist_t *ml, *mntl, *mntpnt = NULL;
157711211SThomas.Haynes@Sun.COM share_t *retcode = NULL;
15780Sstevel@tonic-gate char tmp_path[MAXPATHLEN];
15790Sstevel@tonic-gate int mntpnt_len = 0, tmp;
15800Sstevel@tonic-gate char *p1, *p2;
15810Sstevel@tonic-gate
15820Sstevel@tonic-gate if ((*done_flag)++)
15830Sstevel@tonic-gate return (retcode);
15840Sstevel@tonic-gate
15850Sstevel@tonic-gate /*
15860Sstevel@tonic-gate * While fsgetmntlist() uses lockf() to
15870Sstevel@tonic-gate * lock the mnttab before reading it in,
15880Sstevel@tonic-gate * the lock ignores threads in the same process.
15890Sstevel@tonic-gate * Read in the mnttab with the protection of a mutex.
15900Sstevel@tonic-gate */
15910Sstevel@tonic-gate (void) mutex_lock(&mnttab_lock);
15920Sstevel@tonic-gate mntl = fsgetmntlist();
15930Sstevel@tonic-gate (void) mutex_unlock(&mnttab_lock);
15940Sstevel@tonic-gate
15950Sstevel@tonic-gate /*
15960Sstevel@tonic-gate * Obtain the mountpoint for the requested path.
15970Sstevel@tonic-gate */
15980Sstevel@tonic-gate for (ml = mntl; ml; ml = ml->mntl_next) {
15990Sstevel@tonic-gate for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
16006859Sth199096 *p1 == *p2 && *p1; p1++, p2++)
16016859Sth199096 ;
16020Sstevel@tonic-gate if (is_substring(&p1, &p2) &&
16030Sstevel@tonic-gate (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) {
16040Sstevel@tonic-gate mntpnt = ml;
16050Sstevel@tonic-gate mntpnt_len = tmp;
16060Sstevel@tonic-gate }
16070Sstevel@tonic-gate }
16080Sstevel@tonic-gate
16090Sstevel@tonic-gate /*
16100Sstevel@tonic-gate * If the path needs to be autoFS mounted, trigger the mount by
16110Sstevel@tonic-gate * stat()ing it. This is determined by checking whether the
16120Sstevel@tonic-gate * mountpoint we just found is of type autofs.
16130Sstevel@tonic-gate */
16140Sstevel@tonic-gate if (mntpnt != NULL &&
16150Sstevel@tonic-gate strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) {
16160Sstevel@tonic-gate /*
16170Sstevel@tonic-gate * The requested path is a substring of an autoFS filesystem.
16180Sstevel@tonic-gate * Trigger the mount.
16190Sstevel@tonic-gate */
16200Sstevel@tonic-gate if (stat(rpath, &r_stbuf) < 0) {
16210Sstevel@tonic-gate if (verbose)
16220Sstevel@tonic-gate syslog(LOG_NOTICE, "%s: %m", rpath);
16230Sstevel@tonic-gate goto done;
16240Sstevel@tonic-gate }
16250Sstevel@tonic-gate if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) {
16260Sstevel@tonic-gate /*
16270Sstevel@tonic-gate * The requested path is a directory, stat(2) it
16280Sstevel@tonic-gate * again with a trailing '.' to force the autoFS
16290Sstevel@tonic-gate * module to trigger the mount of indirect
16300Sstevel@tonic-gate * automount entries, such as /net/jurassic/.
16310Sstevel@tonic-gate */
16320Sstevel@tonic-gate if (strlen(rpath) + 2 > MAXPATHLEN) {
16330Sstevel@tonic-gate if (verbose) {
16340Sstevel@tonic-gate syslog(LOG_NOTICE,
16356859Sth199096 "%s/.: exceeds MAXPATHLEN %d",
16366859Sth199096 rpath, MAXPATHLEN);
16370Sstevel@tonic-gate }
16380Sstevel@tonic-gate goto done;
16390Sstevel@tonic-gate }
16400Sstevel@tonic-gate (void) strcpy(tmp_path, rpath);
16410Sstevel@tonic-gate (void) strcat(tmp_path, "/.");
16420Sstevel@tonic-gate
16430Sstevel@tonic-gate if (stat(tmp_path, &r_stbuf) < 0) {
16440Sstevel@tonic-gate if (verbose)
16450Sstevel@tonic-gate syslog(LOG_NOTICE, "%s: %m", tmp_path);
16460Sstevel@tonic-gate goto done;
16470Sstevel@tonic-gate }
16480Sstevel@tonic-gate }
164911211SThomas.Haynes@Sun.COM
16500Sstevel@tonic-gate /*
16510Sstevel@tonic-gate * The mount has been triggered, re-read mnttab to pick up
16520Sstevel@tonic-gate * the changes made by autoFS.
16530Sstevel@tonic-gate */
16540Sstevel@tonic-gate fsfreemntlist(mntl);
16550Sstevel@tonic-gate (void) mutex_lock(&mnttab_lock);
16560Sstevel@tonic-gate mntl = fsgetmntlist();
16570Sstevel@tonic-gate (void) mutex_unlock(&mnttab_lock);
16580Sstevel@tonic-gate }
16590Sstevel@tonic-gate
16600Sstevel@tonic-gate /*
16610Sstevel@tonic-gate * The autoFS mountpoint has been triggered if necessary,
16620Sstevel@tonic-gate * now search mnttab again to determine if the requested path
16630Sstevel@tonic-gate * is an LOFS mount of a shared path.
16640Sstevel@tonic-gate */
16650Sstevel@tonic-gate mntpnt_len = 0;
16660Sstevel@tonic-gate for (ml = mntl; ml; ml = ml->mntl_next) {
16670Sstevel@tonic-gate if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs"))
16680Sstevel@tonic-gate continue;
16690Sstevel@tonic-gate
16700Sstevel@tonic-gate for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath;
16716859Sth199096 *p1 == *p2 && *p1; p1++, p2++)
16726859Sth199096 ;
16730Sstevel@tonic-gate
16740Sstevel@tonic-gate if (is_substring(&p1, &p2) &&
16750Sstevel@tonic-gate ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) {
16760Sstevel@tonic-gate mntpnt_len = tmp;
16770Sstevel@tonic-gate
16780Sstevel@tonic-gate if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) >
16790Sstevel@tonic-gate MAXPATHLEN) {
16800Sstevel@tonic-gate if (verbose) {
16810Sstevel@tonic-gate syslog(LOG_NOTICE, "%s%s: exceeds %d",
16826859Sth199096 ml->mntl_mnt->mnt_special, p2,
16836859Sth199096 MAXPATHLEN);
16840Sstevel@tonic-gate }
16850Sstevel@tonic-gate if (retcode)
16860Sstevel@tonic-gate sharefree(retcode);
16870Sstevel@tonic-gate retcode = NULL;
16880Sstevel@tonic-gate goto done;
16890Sstevel@tonic-gate }
16900Sstevel@tonic-gate
16910Sstevel@tonic-gate (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special);
16920Sstevel@tonic-gate (void) strcat(tmp_path, p2);
16930Sstevel@tonic-gate if (retcode)
16940Sstevel@tonic-gate sharefree(retcode);
16950Sstevel@tonic-gate retcode = findentry(tmp_path);
16960Sstevel@tonic-gate }
16970Sstevel@tonic-gate }
16980Sstevel@tonic-gate
16990Sstevel@tonic-gate if (retcode) {
17000Sstevel@tonic-gate assert(strlen(tmp_path) > 0);
17010Sstevel@tonic-gate (void) strcpy(rpath, tmp_path);
17020Sstevel@tonic-gate }
17030Sstevel@tonic-gate
17040Sstevel@tonic-gate done:
17050Sstevel@tonic-gate fsfreemntlist(mntl);
17060Sstevel@tonic-gate return (retcode);
17070Sstevel@tonic-gate }
17080Sstevel@tonic-gate
17090Sstevel@tonic-gate /*
17100Sstevel@tonic-gate * Determine whether an access list grants rights to a particular host.
17110Sstevel@tonic-gate * We match on aliases of the hostname as well as on the canonical name.
17120Sstevel@tonic-gate * Names in the access list may be either hosts or netgroups; they're
17130Sstevel@tonic-gate * not distinguished syntactically. We check for hosts first because
17140Sstevel@tonic-gate * it's cheaper (just M*N strcmp()s), then try netgroups.
171512393SJan.Kryl@Sun.COM *
171612393SJan.Kryl@Sun.COM * If pnb and pclnames are NULL, it means that we have to use transp
171712393SJan.Kryl@Sun.COM * to resolve client's IP address to host name. If they aren't NULL
171812393SJan.Kryl@Sun.COM * then transp argument won't be used and can be NULL.
17190Sstevel@tonic-gate */
1720249Sjwahlig int
in_access_list(SVCXPRT * transp,struct netbuf ** pnb,struct nd_hostservlist ** pclnames,char * access_list)172111211SThomas.Haynes@Sun.COM in_access_list(SVCXPRT *transp, struct netbuf **pnb,
172211211SThomas.Haynes@Sun.COM struct nd_hostservlist **pclnames,
17230Sstevel@tonic-gate char *access_list) /* N.B. we clobber this "input" parameter */
17240Sstevel@tonic-gate {
17250Sstevel@tonic-gate int nentries;
17260Sstevel@tonic-gate char *gr;
17270Sstevel@tonic-gate char *lasts;
17280Sstevel@tonic-gate char *host;
17290Sstevel@tonic-gate int off;
17300Sstevel@tonic-gate int i;
17310Sstevel@tonic-gate int netgroup_match;
17320Sstevel@tonic-gate int response;
173311211SThomas.Haynes@Sun.COM struct nd_hostservlist *clnames;
173411211SThomas.Haynes@Sun.COM
17350Sstevel@tonic-gate /*
17360Sstevel@tonic-gate * If no access list - then it's unrestricted
17370Sstevel@tonic-gate */
17380Sstevel@tonic-gate if (access_list == NULL || *access_list == '\0')
17390Sstevel@tonic-gate return (1);
17400Sstevel@tonic-gate
174112393SJan.Kryl@Sun.COM assert(transp != NULL || (*pnb != NULL && *pclnames != NULL));
174211211SThomas.Haynes@Sun.COM
17430Sstevel@tonic-gate nentries = 0;
17440Sstevel@tonic-gate
17450Sstevel@tonic-gate for (gr = strtok_r(access_list, ":", &lasts);
17466859Sth199096 gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
17470Sstevel@tonic-gate
17480Sstevel@tonic-gate /*
17490Sstevel@tonic-gate * If the list name has a '-' prepended
17500Sstevel@tonic-gate * then a match of the following name
17510Sstevel@tonic-gate * implies failure instead of success.
17520Sstevel@tonic-gate */
17530Sstevel@tonic-gate if (*gr == '-') {
17540Sstevel@tonic-gate response = 0;
17550Sstevel@tonic-gate gr++;
17560Sstevel@tonic-gate } else
17570Sstevel@tonic-gate response = 1;
17580Sstevel@tonic-gate
17590Sstevel@tonic-gate /*
176011211SThomas.Haynes@Sun.COM * If the list name begins with an at
176111211SThomas.Haynes@Sun.COM * sign then do a network comparison.
176211211SThomas.Haynes@Sun.COM */
176311211SThomas.Haynes@Sun.COM if (*gr == '@') {
176412393SJan.Kryl@Sun.COM /*
176512393SJan.Kryl@Sun.COM * Just get the netbuf, avoiding the costly name
176612393SJan.Kryl@Sun.COM * lookup. This will suffice for access based
176712393SJan.Kryl@Sun.COM * solely on addresses.
176812393SJan.Kryl@Sun.COM */
176912393SJan.Kryl@Sun.COM if (*pnb == NULL) {
177012393SJan.Kryl@Sun.COM /*
177112393SJan.Kryl@Sun.COM * Don't grant access if client's address isn't
177212393SJan.Kryl@Sun.COM * known.
177312393SJan.Kryl@Sun.COM */
177412393SJan.Kryl@Sun.COM if ((*pnb = svc_getrpccaller(transp)) == NULL)
177512393SJan.Kryl@Sun.COM return (0);
177612393SJan.Kryl@Sun.COM }
177712393SJan.Kryl@Sun.COM
177811211SThomas.Haynes@Sun.COM if (netmatch(*pnb, gr + 1))
177911211SThomas.Haynes@Sun.COM return (response);
178011211SThomas.Haynes@Sun.COM continue;
178111211SThomas.Haynes@Sun.COM }
178211211SThomas.Haynes@Sun.COM
178311211SThomas.Haynes@Sun.COM /*
178411211SThomas.Haynes@Sun.COM * We need to get the host name if we haven't gotten
178511211SThomas.Haynes@Sun.COM * it by now!
178611211SThomas.Haynes@Sun.COM */
178712393SJan.Kryl@Sun.COM if (*pclnames == NULL) {
178811211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_addrlist);
178911211SThomas.Haynes@Sun.COM /*
179011211SThomas.Haynes@Sun.COM * Do not grant access if we can't
179111211SThomas.Haynes@Sun.COM * get a name!
179211211SThomas.Haynes@Sun.COM */
179312393SJan.Kryl@Sun.COM if (getclientsnames(transp, pnb, pclnames) != 0)
179411211SThomas.Haynes@Sun.COM return (0);
179511211SThomas.Haynes@Sun.COM }
179611211SThomas.Haynes@Sun.COM
179711211SThomas.Haynes@Sun.COM clnames = *pclnames;
179811211SThomas.Haynes@Sun.COM
179911211SThomas.Haynes@Sun.COM /*
18000Sstevel@tonic-gate * The following loops through all the
18010Sstevel@tonic-gate * client's aliases. Usually it's just one name.
18020Sstevel@tonic-gate */
18030Sstevel@tonic-gate for (i = 0; i < clnames->h_cnt; i++) {
18040Sstevel@tonic-gate host = clnames->h_hostservs[i].h_host;
18050Sstevel@tonic-gate
18060Sstevel@tonic-gate /*
18070Sstevel@tonic-gate * If the list name begins with a dot then
18080Sstevel@tonic-gate * do a domain name suffix comparison.
18090Sstevel@tonic-gate * A single dot matches any name with no
18100Sstevel@tonic-gate * suffix.
18110Sstevel@tonic-gate */
18120Sstevel@tonic-gate if (*gr == '.') {
18130Sstevel@tonic-gate if (*(gr + 1) == '\0') { /* single dot */
18140Sstevel@tonic-gate if (strchr(host, '.') == NULL)
18150Sstevel@tonic-gate return (response);
18160Sstevel@tonic-gate } else {
18170Sstevel@tonic-gate off = strlen(host) - strlen(gr);
18180Sstevel@tonic-gate if (off > 0 &&
18190Sstevel@tonic-gate strcasecmp(host + off, gr) == 0) {
18200Sstevel@tonic-gate return (response);
18210Sstevel@tonic-gate }
18220Sstevel@tonic-gate }
18230Sstevel@tonic-gate } else
18240Sstevel@tonic-gate
18250Sstevel@tonic-gate /*
18260Sstevel@tonic-gate * Just do a hostname match
18270Sstevel@tonic-gate */
18280Sstevel@tonic-gate if (strcasecmp(gr, host) == 0) {
18290Sstevel@tonic-gate return (response); /* Matched a hostname */
18300Sstevel@tonic-gate }
18310Sstevel@tonic-gate }
18320Sstevel@tonic-gate
18330Sstevel@tonic-gate nentries++;
18340Sstevel@tonic-gate }
18350Sstevel@tonic-gate
183611211SThomas.Haynes@Sun.COM /*
183711211SThomas.Haynes@Sun.COM * We need to get the host name if we haven't gotten
183811211SThomas.Haynes@Sun.COM * it by now!
183911211SThomas.Haynes@Sun.COM */
184012393SJan.Kryl@Sun.COM if (*pclnames == NULL) {
184111211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_netgroup);
184211211SThomas.Haynes@Sun.COM /*
184311211SThomas.Haynes@Sun.COM * Do not grant access if we can't
184411211SThomas.Haynes@Sun.COM * get a name!
184511211SThomas.Haynes@Sun.COM */
184612393SJan.Kryl@Sun.COM if (getclientsnames(transp, pnb, pclnames) != 0)
184711211SThomas.Haynes@Sun.COM return (0);
184811211SThomas.Haynes@Sun.COM }
184911211SThomas.Haynes@Sun.COM
185011211SThomas.Haynes@Sun.COM netgroup_match = netgroup_check(*pclnames, access_list, nentries);
18510Sstevel@tonic-gate
18520Sstevel@tonic-gate return (netgroup_match);
18530Sstevel@tonic-gate }
18540Sstevel@tonic-gate
18550Sstevel@tonic-gate int
netmatch(struct netbuf * nb,char * name)18560Sstevel@tonic-gate netmatch(struct netbuf *nb, char *name)
18570Sstevel@tonic-gate {
18580Sstevel@tonic-gate uint_t claddr;
18590Sstevel@tonic-gate struct netent n, *np;
18600Sstevel@tonic-gate char *mp, *p;
18610Sstevel@tonic-gate uint_t addr, mask;
18620Sstevel@tonic-gate int i, bits;
18630Sstevel@tonic-gate char buff[256];
18640Sstevel@tonic-gate
18650Sstevel@tonic-gate /*
18660Sstevel@tonic-gate * Check if it's an IPv4 addr
18670Sstevel@tonic-gate */
18680Sstevel@tonic-gate if (nb->len != sizeof (struct sockaddr_in))
18690Sstevel@tonic-gate return (0);
18700Sstevel@tonic-gate
18710Sstevel@tonic-gate (void) memcpy(&claddr,
18726859Sth199096 /* LINTED pointer alignment */
18736859Sth199096 &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
18746859Sth199096 sizeof (struct in_addr));
18750Sstevel@tonic-gate claddr = ntohl(claddr);
18760Sstevel@tonic-gate
18770Sstevel@tonic-gate mp = strchr(name, '/');
18780Sstevel@tonic-gate if (mp)
18790Sstevel@tonic-gate *mp++ = '\0';
18800Sstevel@tonic-gate
18810Sstevel@tonic-gate if (isdigit(*name)) {
18820Sstevel@tonic-gate /*
18830Sstevel@tonic-gate * Convert a dotted IP address
18840Sstevel@tonic-gate * to an IP address. The conversion
18850Sstevel@tonic-gate * is not the same as that in inet_addr().
18860Sstevel@tonic-gate */
18870Sstevel@tonic-gate p = name;
18880Sstevel@tonic-gate addr = 0;
18890Sstevel@tonic-gate for (i = 0; i < 4; i++) {
18900Sstevel@tonic-gate addr |= atoi(p) << ((3-i) * 8);
18910Sstevel@tonic-gate p = strchr(p, '.');
18920Sstevel@tonic-gate if (p == NULL)
18930Sstevel@tonic-gate break;
18940Sstevel@tonic-gate p++;
18950Sstevel@tonic-gate }
18960Sstevel@tonic-gate } else {
18970Sstevel@tonic-gate /*
18980Sstevel@tonic-gate * Turn the netname into
18990Sstevel@tonic-gate * an IP address.
19000Sstevel@tonic-gate */
19010Sstevel@tonic-gate np = getnetbyname_r(name, &n, buff, sizeof (buff));
19020Sstevel@tonic-gate if (np == NULL) {
19030Sstevel@tonic-gate syslog(LOG_DEBUG, "getnetbyname_r: %s: %m", name);
19040Sstevel@tonic-gate return (0);
19050Sstevel@tonic-gate }
19060Sstevel@tonic-gate addr = np->n_net;
19070Sstevel@tonic-gate }
19080Sstevel@tonic-gate
19090Sstevel@tonic-gate /*
19100Sstevel@tonic-gate * If the mask is specified explicitly then
19110Sstevel@tonic-gate * use that value, e.g.
19120Sstevel@tonic-gate *
19130Sstevel@tonic-gate * @109.104.56/28
19140Sstevel@tonic-gate *
19150Sstevel@tonic-gate * otherwise assume a mask from the zero octets
19160Sstevel@tonic-gate * in the least significant bits of the address, e.g.
19170Sstevel@tonic-gate *
19180Sstevel@tonic-gate * @109.104 or @109.104.0.0
19190Sstevel@tonic-gate */
19200Sstevel@tonic-gate if (mp) {
19210Sstevel@tonic-gate bits = atoi(mp);
19220Sstevel@tonic-gate mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
19236859Sth199096 : 0;
19240Sstevel@tonic-gate addr &= mask;
19250Sstevel@tonic-gate } else {
19267997SThomas.Haynes@Sun.COM if ((addr & IN_CLASSA_HOST) == 0)
19277997SThomas.Haynes@Sun.COM mask = IN_CLASSA_NET;
19287997SThomas.Haynes@Sun.COM else if ((addr & IN_CLASSB_HOST) == 0)
19297997SThomas.Haynes@Sun.COM mask = IN_CLASSB_NET;
19307997SThomas.Haynes@Sun.COM else if ((addr & IN_CLASSC_HOST) == 0)
19317997SThomas.Haynes@Sun.COM mask = IN_CLASSC_NET;
19327997SThomas.Haynes@Sun.COM else
19337997SThomas.Haynes@Sun.COM mask = IN_CLASSE_NET;
19340Sstevel@tonic-gate }
19350Sstevel@tonic-gate
19360Sstevel@tonic-gate return ((claddr & mask) == addr);
19370Sstevel@tonic-gate }
19380Sstevel@tonic-gate
19390Sstevel@tonic-gate
19400Sstevel@tonic-gate static char *optlist[] = {
19410Sstevel@tonic-gate #define OPT_RO 0
19420Sstevel@tonic-gate SHOPT_RO,
19430Sstevel@tonic-gate #define OPT_RW 1
19440Sstevel@tonic-gate SHOPT_RW,
19450Sstevel@tonic-gate #define OPT_ROOT 2
19460Sstevel@tonic-gate SHOPT_ROOT,
19470Sstevel@tonic-gate #define OPT_SECURE 3
19480Sstevel@tonic-gate SHOPT_SECURE,
19490Sstevel@tonic-gate #define OPT_ANON 4
19500Sstevel@tonic-gate SHOPT_ANON,
19510Sstevel@tonic-gate #define OPT_WINDOW 5
19520Sstevel@tonic-gate SHOPT_WINDOW,
19530Sstevel@tonic-gate #define OPT_NOSUID 6
19540Sstevel@tonic-gate SHOPT_NOSUID,
19550Sstevel@tonic-gate #define OPT_ACLOK 7
19560Sstevel@tonic-gate SHOPT_ACLOK,
19570Sstevel@tonic-gate #define OPT_SEC 8
19580Sstevel@tonic-gate SHOPT_SEC,
19597961SNatalie.Li@Sun.COM #define OPT_NONE 9
19607961SNatalie.Li@Sun.COM SHOPT_NONE,
19610Sstevel@tonic-gate NULL
19620Sstevel@tonic-gate };
19630Sstevel@tonic-gate
19640Sstevel@tonic-gate static int
map_flavor(char * str)19650Sstevel@tonic-gate map_flavor(char *str)
19660Sstevel@tonic-gate {
19670Sstevel@tonic-gate seconfig_t sec;
19680Sstevel@tonic-gate
19690Sstevel@tonic-gate if (nfs_getseconfig_byname(str, &sec))
19700Sstevel@tonic-gate return (-1);
19710Sstevel@tonic-gate
19720Sstevel@tonic-gate return (sec.sc_nfsnum);
19730Sstevel@tonic-gate }
19740Sstevel@tonic-gate
19750Sstevel@tonic-gate /*
19760Sstevel@tonic-gate * If the option string contains a "sec="
19770Sstevel@tonic-gate * option, then use new option syntax.
19780Sstevel@tonic-gate */
19790Sstevel@tonic-gate static int
newopts(char * opts)19800Sstevel@tonic-gate newopts(char *opts)
19810Sstevel@tonic-gate {
19820Sstevel@tonic-gate char *head, *p, *val;
19830Sstevel@tonic-gate
19840Sstevel@tonic-gate if (!opts || *opts == '\0')
19850Sstevel@tonic-gate return (0);
19860Sstevel@tonic-gate
19870Sstevel@tonic-gate head = strdup(opts);
19880Sstevel@tonic-gate if (head == NULL) {
19890Sstevel@tonic-gate syslog(LOG_ERR, "opts: no memory");
19900Sstevel@tonic-gate return (0);
19910Sstevel@tonic-gate }
19920Sstevel@tonic-gate
19930Sstevel@tonic-gate p = head;
19940Sstevel@tonic-gate while (*p) {
19950Sstevel@tonic-gate if (getsubopt(&p, optlist, &val) == OPT_SEC) {
19960Sstevel@tonic-gate free(head);
19970Sstevel@tonic-gate return (1);
19980Sstevel@tonic-gate }
19990Sstevel@tonic-gate }
20000Sstevel@tonic-gate
20010Sstevel@tonic-gate free(head);
20020Sstevel@tonic-gate return (0);
20030Sstevel@tonic-gate }
20040Sstevel@tonic-gate
20050Sstevel@tonic-gate /*
20060Sstevel@tonic-gate * Given an export and the clients hostname(s)
20070Sstevel@tonic-gate * determine the security flavors that this
20080Sstevel@tonic-gate * client is permitted to use.
20090Sstevel@tonic-gate *
20100Sstevel@tonic-gate * This routine is called only for "old" syntax, i.e.
20110Sstevel@tonic-gate * only one security flavor is allowed. So we need
20120Sstevel@tonic-gate * to determine two things: the particular flavor,
20130Sstevel@tonic-gate * and whether the client is allowed to use this
20140Sstevel@tonic-gate * flavor, i.e. is in the access list.
20150Sstevel@tonic-gate *
20160Sstevel@tonic-gate * Note that if there is no access list, then the
20170Sstevel@tonic-gate * default is that access is granted.
20180Sstevel@tonic-gate */
20190Sstevel@tonic-gate static int
getclientsflavors_old(share_t * sh,SVCXPRT * transp,struct netbuf ** nb,struct nd_hostservlist ** clnames,int * flavors)202011211SThomas.Haynes@Sun.COM getclientsflavors_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
202111211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int *flavors)
20220Sstevel@tonic-gate {
20230Sstevel@tonic-gate char *opts, *p, *val;
20247961SNatalie.Li@Sun.COM boolean_t ok = B_FALSE;
20250Sstevel@tonic-gate int defaultaccess = 1;
20267961SNatalie.Li@Sun.COM boolean_t reject = B_FALSE;
20270Sstevel@tonic-gate
20280Sstevel@tonic-gate opts = strdup(sh->sh_opts);
20290Sstevel@tonic-gate if (opts == NULL) {
20300Sstevel@tonic-gate syslog(LOG_ERR, "getclientsflavors: no memory");
20310Sstevel@tonic-gate return (0);
20320Sstevel@tonic-gate }
20330Sstevel@tonic-gate
20340Sstevel@tonic-gate flavors[0] = AUTH_SYS;
20350Sstevel@tonic-gate p = opts;
20360Sstevel@tonic-gate
20370Sstevel@tonic-gate while (*p) {
20380Sstevel@tonic-gate
20390Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) {
20400Sstevel@tonic-gate case OPT_SECURE:
20410Sstevel@tonic-gate flavors[0] = AUTH_DES;
20420Sstevel@tonic-gate break;
20430Sstevel@tonic-gate
20440Sstevel@tonic-gate case OPT_RO:
20450Sstevel@tonic-gate case OPT_RW:
20460Sstevel@tonic-gate defaultaccess = 0;
204711211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
20480Sstevel@tonic-gate ok++;
20490Sstevel@tonic-gate break;
20507961SNatalie.Li@Sun.COM
20517961SNatalie.Li@Sun.COM case OPT_NONE:
20527961SNatalie.Li@Sun.COM defaultaccess = 0;
205311211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
20547961SNatalie.Li@Sun.COM reject = B_TRUE;
20550Sstevel@tonic-gate }
20560Sstevel@tonic-gate }
20570Sstevel@tonic-gate
20580Sstevel@tonic-gate free(opts);
20590Sstevel@tonic-gate
20607961SNatalie.Li@Sun.COM /* none takes precedence over everything else */
20617961SNatalie.Li@Sun.COM if (reject)
20627961SNatalie.Li@Sun.COM ok = B_TRUE;
20637961SNatalie.Li@Sun.COM
20640Sstevel@tonic-gate return (defaultaccess || ok);
20650Sstevel@tonic-gate }
20660Sstevel@tonic-gate
20670Sstevel@tonic-gate /*
20680Sstevel@tonic-gate * Given an export and the clients hostname(s)
20690Sstevel@tonic-gate * determine the security flavors that this
20700Sstevel@tonic-gate * client is permitted to use.
20710Sstevel@tonic-gate *
20720Sstevel@tonic-gate * This is somewhat more complicated than the "old"
20730Sstevel@tonic-gate * routine because the options may contain multiple
20740Sstevel@tonic-gate * security flavors (sec=) each with its own access
20750Sstevel@tonic-gate * lists. So a client could be granted access based
20760Sstevel@tonic-gate * on a number of security flavors. Note that the
20770Sstevel@tonic-gate * type of access might not always be the same, the
20780Sstevel@tonic-gate * client may get readonly access with one flavor
20790Sstevel@tonic-gate * and readwrite with another, however the client
20800Sstevel@tonic-gate * is not told this detail, it gets only the list
20810Sstevel@tonic-gate * of flavors, and only if the client is using
20820Sstevel@tonic-gate * version 3 of the mount protocol.
20830Sstevel@tonic-gate */
20840Sstevel@tonic-gate static int
getclientsflavors_new(share_t * sh,SVCXPRT * transp,struct netbuf ** nb,struct nd_hostservlist ** clnames,int * flavors)208511211SThomas.Haynes@Sun.COM getclientsflavors_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
208611211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int *flavors)
20870Sstevel@tonic-gate {
20880Sstevel@tonic-gate char *opts, *p, *val;
20890Sstevel@tonic-gate char *lasts;
20900Sstevel@tonic-gate char *f;
20917961SNatalie.Li@Sun.COM boolean_t access_ok;
20927961SNatalie.Li@Sun.COM int count, c, perm;
20937961SNatalie.Li@Sun.COM boolean_t reject = B_FALSE;
20940Sstevel@tonic-gate
20950Sstevel@tonic-gate opts = strdup(sh->sh_opts);
20960Sstevel@tonic-gate if (opts == NULL) {
20970Sstevel@tonic-gate syslog(LOG_ERR, "getclientsflavors: no memory");
20980Sstevel@tonic-gate return (0);
20990Sstevel@tonic-gate }
21000Sstevel@tonic-gate
21010Sstevel@tonic-gate p = opts;
21027961SNatalie.Li@Sun.COM perm = count = c = 0;
21030Sstevel@tonic-gate /* default access is rw */
21047961SNatalie.Li@Sun.COM access_ok = B_TRUE;
21050Sstevel@tonic-gate
21060Sstevel@tonic-gate while (*p) {
21070Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) {
21080Sstevel@tonic-gate case OPT_SEC:
21090Sstevel@tonic-gate /*
21100Sstevel@tonic-gate * Before a new sec=xxx option, check if we need
21110Sstevel@tonic-gate * to move the c index back to the previous count.
21120Sstevel@tonic-gate */
21130Sstevel@tonic-gate if (!access_ok) {
21140Sstevel@tonic-gate c = count;
21150Sstevel@tonic-gate }
21160Sstevel@tonic-gate
21170Sstevel@tonic-gate /* get all the sec=f1[:f2] flavors */
21180Sstevel@tonic-gate while ((f = strtok_r(val, ":", &lasts))
21196859Sth199096 != NULL) {
21200Sstevel@tonic-gate flavors[c++] = map_flavor(f);
21210Sstevel@tonic-gate val = NULL;
21220Sstevel@tonic-gate }
212311211SThomas.Haynes@Sun.COM
21240Sstevel@tonic-gate /* for a new sec=xxx option, default is rw access */
21257961SNatalie.Li@Sun.COM access_ok = B_TRUE;
21260Sstevel@tonic-gate break;
21270Sstevel@tonic-gate
21280Sstevel@tonic-gate case OPT_RO:
21290Sstevel@tonic-gate case OPT_RW:
213011211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) {
21310Sstevel@tonic-gate count = c;
21327961SNatalie.Li@Sun.COM access_ok = B_TRUE;
21330Sstevel@tonic-gate } else {
21347961SNatalie.Li@Sun.COM access_ok = B_FALSE;
21350Sstevel@tonic-gate }
21360Sstevel@tonic-gate break;
21377961SNatalie.Li@Sun.COM
21387961SNatalie.Li@Sun.COM case OPT_NONE:
213911211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
21407961SNatalie.Li@Sun.COM reject = B_TRUE; /* none overides rw/ro */
21417961SNatalie.Li@Sun.COM break;
21420Sstevel@tonic-gate }
21430Sstevel@tonic-gate }
21440Sstevel@tonic-gate
21457961SNatalie.Li@Sun.COM if (reject)
21467961SNatalie.Li@Sun.COM access_ok = B_FALSE;
21477961SNatalie.Li@Sun.COM
21487961SNatalie.Li@Sun.COM if (!access_ok)
21490Sstevel@tonic-gate c = count;
21507961SNatalie.Li@Sun.COM
21510Sstevel@tonic-gate free(opts);
21520Sstevel@tonic-gate
21530Sstevel@tonic-gate return (c);
21540Sstevel@tonic-gate }
21550Sstevel@tonic-gate
21560Sstevel@tonic-gate /*
21570Sstevel@tonic-gate * This is a tricky piece of code that parses the
21580Sstevel@tonic-gate * share options looking for a match on the auth
21590Sstevel@tonic-gate * flavor that the client is using. If it finds
21600Sstevel@tonic-gate * a match, then the client is given ro, rw, or
21610Sstevel@tonic-gate * no access depending whether it is in the access
21620Sstevel@tonic-gate * list. There is a special case for "secure"
21630Sstevel@tonic-gate * flavor. Other flavors are values of the new "sec=" option.
21640Sstevel@tonic-gate */
21650Sstevel@tonic-gate int
check_client(share_t * sh,struct netbuf * nb,struct nd_hostservlist * clnames,int flavor)216611211SThomas.Haynes@Sun.COM check_client(share_t *sh, struct netbuf *nb,
21670Sstevel@tonic-gate struct nd_hostservlist *clnames, int flavor)
21680Sstevel@tonic-gate {
21690Sstevel@tonic-gate if (newopts(sh->sh_opts))
217011211SThomas.Haynes@Sun.COM return (check_client_new(sh, NULL, &nb, &clnames, flavor));
21710Sstevel@tonic-gate else
217211211SThomas.Haynes@Sun.COM return (check_client_old(sh, NULL, &nb, &clnames, flavor));
21730Sstevel@tonic-gate }
21740Sstevel@tonic-gate
21750Sstevel@tonic-gate static int
check_client_old(share_t * sh,SVCXPRT * transp,struct netbuf ** nb,struct nd_hostservlist ** clnames,int flavor)217611211SThomas.Haynes@Sun.COM check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
217711211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int flavor)
21780Sstevel@tonic-gate {
21790Sstevel@tonic-gate char *opts, *p, *val;
21800Sstevel@tonic-gate int match; /* Set when a flavor is matched */
21810Sstevel@tonic-gate int perm = 0; /* Set when "ro", "rw" or "root" is matched */
21820Sstevel@tonic-gate int list = 0; /* Set when "ro", "rw" is found */
21830Sstevel@tonic-gate int ro_val = 0; /* Set if ro option is 'ro=' */
21840Sstevel@tonic-gate int rw_val = 0; /* Set if rw option is 'rw=' */
21857961SNatalie.Li@Sun.COM boolean_t reject = B_FALSE; /* if none= contains the host */
21860Sstevel@tonic-gate
21870Sstevel@tonic-gate opts = strdup(sh->sh_opts);
21880Sstevel@tonic-gate if (opts == NULL) {
21890Sstevel@tonic-gate syslog(LOG_ERR, "check_client: no memory");
21900Sstevel@tonic-gate return (0);
21910Sstevel@tonic-gate }
21920Sstevel@tonic-gate
21930Sstevel@tonic-gate p = opts;
21940Sstevel@tonic-gate match = AUTH_UNIX;
21950Sstevel@tonic-gate
21960Sstevel@tonic-gate while (*p) {
21970Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) {
21980Sstevel@tonic-gate
21990Sstevel@tonic-gate case OPT_SECURE:
22000Sstevel@tonic-gate match = AUTH_DES;
22010Sstevel@tonic-gate break;
22020Sstevel@tonic-gate
22030Sstevel@tonic-gate case OPT_RO:
22040Sstevel@tonic-gate list++;
22050Sstevel@tonic-gate if (val) ro_val++;
220611211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
22070Sstevel@tonic-gate perm |= NFSAUTH_RO;
22080Sstevel@tonic-gate break;
22090Sstevel@tonic-gate
22100Sstevel@tonic-gate case OPT_RW:
22110Sstevel@tonic-gate list++;
22120Sstevel@tonic-gate if (val) rw_val++;
221311211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
22140Sstevel@tonic-gate perm |= NFSAUTH_RW;
22150Sstevel@tonic-gate break;
22160Sstevel@tonic-gate
22170Sstevel@tonic-gate case OPT_ROOT:
22180Sstevel@tonic-gate /*
22190Sstevel@tonic-gate * Check if the client is in
22200Sstevel@tonic-gate * the root list. Only valid
22210Sstevel@tonic-gate * for AUTH_SYS.
22220Sstevel@tonic-gate */
22230Sstevel@tonic-gate if (flavor != AUTH_SYS)
22240Sstevel@tonic-gate break;
22250Sstevel@tonic-gate
22260Sstevel@tonic-gate if (val == NULL || *val == '\0')
22270Sstevel@tonic-gate break;
22280Sstevel@tonic-gate
222911211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
22300Sstevel@tonic-gate perm |= NFSAUTH_ROOT;
22310Sstevel@tonic-gate break;
22327961SNatalie.Li@Sun.COM
22337961SNatalie.Li@Sun.COM case OPT_NONE:
22347961SNatalie.Li@Sun.COM /*
22357961SNatalie.Li@Sun.COM * Check if the client should have no access
22367961SNatalie.Li@Sun.COM * to this share at all. This option behaves
22377961SNatalie.Li@Sun.COM * more like "root" than either "rw" or "ro".
22387961SNatalie.Li@Sun.COM */
223911211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
22407961SNatalie.Li@Sun.COM reject = B_TRUE;
22417961SNatalie.Li@Sun.COM break;
22420Sstevel@tonic-gate }
22430Sstevel@tonic-gate }
22440Sstevel@tonic-gate
22450Sstevel@tonic-gate free(opts);
22460Sstevel@tonic-gate
22477961SNatalie.Li@Sun.COM if (flavor != match || reject)
22480Sstevel@tonic-gate return (NFSAUTH_DENIED);
22490Sstevel@tonic-gate
22500Sstevel@tonic-gate if (list) {
22510Sstevel@tonic-gate /*
22520Sstevel@tonic-gate * If the client doesn't match an "ro" or "rw"
22530Sstevel@tonic-gate * list then set no access.
22540Sstevel@tonic-gate */
22550Sstevel@tonic-gate if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0)
22560Sstevel@tonic-gate perm |= NFSAUTH_DENIED;
22570Sstevel@tonic-gate } else {
22580Sstevel@tonic-gate /*
22590Sstevel@tonic-gate * The client matched a flavor entry that
22600Sstevel@tonic-gate * has no explicit "rw" or "ro" determination.
22610Sstevel@tonic-gate * Default it to "rw".
22620Sstevel@tonic-gate */
22630Sstevel@tonic-gate perm |= NFSAUTH_RW;
22640Sstevel@tonic-gate }
22650Sstevel@tonic-gate
22660Sstevel@tonic-gate
22670Sstevel@tonic-gate /*
22680Sstevel@tonic-gate * The client may show up in both ro= and rw=
22690Sstevel@tonic-gate * lists. If so, then turn off the RO access
22700Sstevel@tonic-gate * bit leaving RW access.
22710Sstevel@tonic-gate */
22720Sstevel@tonic-gate if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
22730Sstevel@tonic-gate /*
22740Sstevel@tonic-gate * Logically cover all permutations of rw=,ro=.
22750Sstevel@tonic-gate * In the case where, rw,ro=<host> we would like
22760Sstevel@tonic-gate * to remove RW access for the host. In all other cases
22770Sstevel@tonic-gate * RW wins the precedence battle.
22780Sstevel@tonic-gate */
22790Sstevel@tonic-gate if (!rw_val && ro_val) {
22800Sstevel@tonic-gate perm &= ~(NFSAUTH_RW);
22810Sstevel@tonic-gate } else {
22820Sstevel@tonic-gate perm &= ~(NFSAUTH_RO);
22830Sstevel@tonic-gate }
22840Sstevel@tonic-gate }
22850Sstevel@tonic-gate
22860Sstevel@tonic-gate return (perm);
22870Sstevel@tonic-gate }
22880Sstevel@tonic-gate
22890Sstevel@tonic-gate /*
22900Sstevel@tonic-gate * Check if the client has access by using a flavor different from
22910Sstevel@tonic-gate * the given "flavor". If "flavor" is not in the flavor list,
22920Sstevel@tonic-gate * return TRUE to indicate that this "flavor" is a wrong sec.
22930Sstevel@tonic-gate */
22940Sstevel@tonic-gate static bool_t
is_wrongsec(share_t * sh,SVCXPRT * transp,struct netbuf ** nb,struct nd_hostservlist ** clnames,int flavor)229511211SThomas.Haynes@Sun.COM is_wrongsec(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
229611211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int flavor)
22970Sstevel@tonic-gate {
22980Sstevel@tonic-gate int flavor_list[MAX_FLAVORS];
22990Sstevel@tonic-gate int flavor_count, i;
23000Sstevel@tonic-gate
23010Sstevel@tonic-gate /* get the flavor list that the client has access with */
230211211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_new(sh, transp, nb,
230311211SThomas.Haynes@Sun.COM clnames, flavor_list);
23040Sstevel@tonic-gate
23050Sstevel@tonic-gate if (flavor_count == 0)
23060Sstevel@tonic-gate return (FALSE);
23070Sstevel@tonic-gate
23080Sstevel@tonic-gate /*
23090Sstevel@tonic-gate * Check if the given "flavor" is in the flavor_list.
23100Sstevel@tonic-gate */
23110Sstevel@tonic-gate for (i = 0; i < flavor_count; i++) {
23120Sstevel@tonic-gate if (flavor == flavor_list[i])
23130Sstevel@tonic-gate return (FALSE);
23140Sstevel@tonic-gate }
23150Sstevel@tonic-gate
23160Sstevel@tonic-gate /*
23170Sstevel@tonic-gate * If "flavor" is not in the flavor_list, return TRUE to indicate
23180Sstevel@tonic-gate * that the client should have access by using a security flavor
23190Sstevel@tonic-gate * different from this "flavor".
23200Sstevel@tonic-gate */
23210Sstevel@tonic-gate return (TRUE);
23220Sstevel@tonic-gate }
23230Sstevel@tonic-gate
23240Sstevel@tonic-gate /*
23250Sstevel@tonic-gate * Given an export and the client's hostname, we
23260Sstevel@tonic-gate * check the security options to see whether the
23270Sstevel@tonic-gate * client is allowed to use the given security flavor.
23280Sstevel@tonic-gate *
23290Sstevel@tonic-gate * The strategy is to proceed through the options looking
23300Sstevel@tonic-gate * for a flavor match, then pay attention to the ro, rw,
23310Sstevel@tonic-gate * and root options.
23320Sstevel@tonic-gate *
23330Sstevel@tonic-gate * Note that an entry may list several flavors in a
23340Sstevel@tonic-gate * single entry, e.g.
23350Sstevel@tonic-gate *
23360Sstevel@tonic-gate * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
23370Sstevel@tonic-gate *
23380Sstevel@tonic-gate */
23390Sstevel@tonic-gate
23400Sstevel@tonic-gate static int
check_client_new(share_t * sh,SVCXPRT * transp,struct netbuf ** nb,struct nd_hostservlist ** clnames,int flavor)234111211SThomas.Haynes@Sun.COM check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb,
234211211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int flavor)
23430Sstevel@tonic-gate {
23440Sstevel@tonic-gate char *opts, *p, *val;
23450Sstevel@tonic-gate char *lasts;
23460Sstevel@tonic-gate char *f;
23470Sstevel@tonic-gate int match = 0; /* Set when a flavor is matched */
23480Sstevel@tonic-gate int perm = 0; /* Set when "ro", "rw" or "root" is matched */
23490Sstevel@tonic-gate int list = 0; /* Set when "ro", "rw" is found */
23500Sstevel@tonic-gate int ro_val = 0; /* Set if ro option is 'ro=' */
23510Sstevel@tonic-gate int rw_val = 0; /* Set if rw option is 'rw=' */
23527961SNatalie.Li@Sun.COM boolean_t reject;
23530Sstevel@tonic-gate
23540Sstevel@tonic-gate opts = strdup(sh->sh_opts);
23550Sstevel@tonic-gate if (opts == NULL) {
23560Sstevel@tonic-gate syslog(LOG_ERR, "check_client: no memory");
23570Sstevel@tonic-gate return (0);
23580Sstevel@tonic-gate }
23590Sstevel@tonic-gate
23600Sstevel@tonic-gate p = opts;
23610Sstevel@tonic-gate
23620Sstevel@tonic-gate while (*p) {
23630Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) {
23640Sstevel@tonic-gate
23650Sstevel@tonic-gate case OPT_SEC:
23660Sstevel@tonic-gate if (match)
23670Sstevel@tonic-gate goto done;
23680Sstevel@tonic-gate
23690Sstevel@tonic-gate while ((f = strtok_r(val, ":", &lasts))
23706859Sth199096 != NULL) {
23710Sstevel@tonic-gate if (flavor == map_flavor(f)) {
23720Sstevel@tonic-gate match = 1;
23730Sstevel@tonic-gate break;
23740Sstevel@tonic-gate }
23750Sstevel@tonic-gate val = NULL;
23760Sstevel@tonic-gate }
23770Sstevel@tonic-gate break;
23780Sstevel@tonic-gate
23790Sstevel@tonic-gate case OPT_RO:
23800Sstevel@tonic-gate if (!match)
23810Sstevel@tonic-gate break;
23820Sstevel@tonic-gate
23830Sstevel@tonic-gate list++;
23840Sstevel@tonic-gate if (val) ro_val++;
238511211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
23860Sstevel@tonic-gate perm |= NFSAUTH_RO;
23870Sstevel@tonic-gate break;
23880Sstevel@tonic-gate
23890Sstevel@tonic-gate case OPT_RW:
23900Sstevel@tonic-gate if (!match)
23910Sstevel@tonic-gate break;
23920Sstevel@tonic-gate
23930Sstevel@tonic-gate list++;
23940Sstevel@tonic-gate if (val) rw_val++;
239511211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
23960Sstevel@tonic-gate perm |= NFSAUTH_RW;
23970Sstevel@tonic-gate break;
23980Sstevel@tonic-gate
23990Sstevel@tonic-gate case OPT_ROOT:
24000Sstevel@tonic-gate /*
24010Sstevel@tonic-gate * Check if the client is in
24020Sstevel@tonic-gate * the root list. Only valid
24030Sstevel@tonic-gate * for AUTH_SYS.
24040Sstevel@tonic-gate */
24050Sstevel@tonic-gate if (flavor != AUTH_SYS)
24060Sstevel@tonic-gate break;
24070Sstevel@tonic-gate
24080Sstevel@tonic-gate if (!match)
24090Sstevel@tonic-gate break;
24100Sstevel@tonic-gate
24110Sstevel@tonic-gate if (val == NULL || *val == '\0')
24120Sstevel@tonic-gate break;
24130Sstevel@tonic-gate
241411211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
24150Sstevel@tonic-gate perm |= NFSAUTH_ROOT;
24160Sstevel@tonic-gate break;
24177961SNatalie.Li@Sun.COM
24187961SNatalie.Li@Sun.COM case OPT_NONE:
24197961SNatalie.Li@Sun.COM /*
24207961SNatalie.Li@Sun.COM * Check if the client should have no access
24217961SNatalie.Li@Sun.COM * to this share at all. This option behaves
24227961SNatalie.Li@Sun.COM * more like "root" than either "rw" or "ro".
24237961SNatalie.Li@Sun.COM */
242411211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val))
24257961SNatalie.Li@Sun.COM perm |= NFSAUTH_DENIED;
24267961SNatalie.Li@Sun.COM break;
24270Sstevel@tonic-gate }
24280Sstevel@tonic-gate }
24290Sstevel@tonic-gate
24300Sstevel@tonic-gate done:
24310Sstevel@tonic-gate /*
24320Sstevel@tonic-gate * If no match then set the perm accordingly
24330Sstevel@tonic-gate */
24347961SNatalie.Li@Sun.COM if (!match || perm & NFSAUTH_DENIED)
24350Sstevel@tonic-gate return (NFSAUTH_DENIED);
24360Sstevel@tonic-gate
24370Sstevel@tonic-gate if (list) {
24380Sstevel@tonic-gate /*
24390Sstevel@tonic-gate * If the client doesn't match an "ro" or "rw" list then
24400Sstevel@tonic-gate * check if it may have access by using a different flavor.
24410Sstevel@tonic-gate * If so, return NFSAUTH_WRONGSEC.
24420Sstevel@tonic-gate * If not, return NFSAUTH_DENIED.
24430Sstevel@tonic-gate */
24440Sstevel@tonic-gate if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) {
244511211SThomas.Haynes@Sun.COM if (is_wrongsec(sh, transp, nb, clnames, flavor))
24460Sstevel@tonic-gate perm |= NFSAUTH_WRONGSEC;
24470Sstevel@tonic-gate else
24480Sstevel@tonic-gate perm |= NFSAUTH_DENIED;
24490Sstevel@tonic-gate }
24500Sstevel@tonic-gate } else {
24510Sstevel@tonic-gate /*
24520Sstevel@tonic-gate * The client matched a flavor entry that
24530Sstevel@tonic-gate * has no explicit "rw" or "ro" determination.
24540Sstevel@tonic-gate * Make sure it defaults to "rw".
24550Sstevel@tonic-gate */
24560Sstevel@tonic-gate perm |= NFSAUTH_RW;
24570Sstevel@tonic-gate }
24580Sstevel@tonic-gate
24590Sstevel@tonic-gate /*
24600Sstevel@tonic-gate * The client may show up in both ro= and rw=
24610Sstevel@tonic-gate * lists. If so, then turn off the RO access
24620Sstevel@tonic-gate * bit leaving RW access.
24630Sstevel@tonic-gate */
24640Sstevel@tonic-gate if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) {
24650Sstevel@tonic-gate /*
24660Sstevel@tonic-gate * Logically cover all permutations of rw=,ro=.
24670Sstevel@tonic-gate * In the case where, rw,ro=<host> we would like
24680Sstevel@tonic-gate * to remove RW access for the host. In all other cases
24690Sstevel@tonic-gate * RW wins the precedence battle.
24700Sstevel@tonic-gate */
24710Sstevel@tonic-gate if (!rw_val && ro_val) {
24720Sstevel@tonic-gate perm &= ~(NFSAUTH_RW);
24730Sstevel@tonic-gate } else {
24740Sstevel@tonic-gate perm &= ~(NFSAUTH_RO);
24750Sstevel@tonic-gate }
24760Sstevel@tonic-gate }
24770Sstevel@tonic-gate
24780Sstevel@tonic-gate free(opts);
24790Sstevel@tonic-gate
24800Sstevel@tonic-gate return (perm);
24810Sstevel@tonic-gate }
24820Sstevel@tonic-gate
24830Sstevel@tonic-gate void
check_sharetab()24840Sstevel@tonic-gate check_sharetab()
24850Sstevel@tonic-gate {
24860Sstevel@tonic-gate FILE *f;
24870Sstevel@tonic-gate struct stat st;
24880Sstevel@tonic-gate static timestruc_t last_sharetab_time;
24890Sstevel@tonic-gate timestruc_t prev_sharetab_time;
249011211SThomas.Haynes@Sun.COM share_t *sh;
24910Sstevel@tonic-gate struct sh_list *shp, *shp_prev;
24920Sstevel@tonic-gate int res, c = 0;
24930Sstevel@tonic-gate
24940Sstevel@tonic-gate /*
24950Sstevel@tonic-gate * read in /etc/dfs/sharetab if it has changed
24960Sstevel@tonic-gate */
24970Sstevel@tonic-gate if (stat(SHARETAB, &st) != 0) {
24980Sstevel@tonic-gate syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
24990Sstevel@tonic-gate return;
25000Sstevel@tonic-gate }
25010Sstevel@tonic-gate
25020Sstevel@tonic-gate if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec &&
25030Sstevel@tonic-gate st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) {
25040Sstevel@tonic-gate /*
25050Sstevel@tonic-gate * No change.
25060Sstevel@tonic-gate */
25070Sstevel@tonic-gate return;
25080Sstevel@tonic-gate }
25090Sstevel@tonic-gate
25100Sstevel@tonic-gate /*
25110Sstevel@tonic-gate * Remember the mod time, then after getting the
25120Sstevel@tonic-gate * write lock check again. If another thread
25130Sstevel@tonic-gate * already did the update, then there's no
25140Sstevel@tonic-gate * work to do.
25150Sstevel@tonic-gate */
25160Sstevel@tonic-gate prev_sharetab_time = last_sharetab_time;
25170Sstevel@tonic-gate
25180Sstevel@tonic-gate (void) rw_wrlock(&sharetab_lock);
25190Sstevel@tonic-gate
25200Sstevel@tonic-gate if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec ||
25210Sstevel@tonic-gate prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) {
25220Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock);
25230Sstevel@tonic-gate return;
25240Sstevel@tonic-gate }
25250Sstevel@tonic-gate
25263957Sth199096 /*
25273957Sth199096 * Note that since the sharetab is now in memory
25283957Sth199096 * and a snapshot is taken, we no longer have to
25293957Sth199096 * lock the file.
25303957Sth199096 */
25313957Sth199096 f = fopen(SHARETAB, "r");
25320Sstevel@tonic-gate if (f == NULL) {
25330Sstevel@tonic-gate syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB);
25340Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock);
25350Sstevel@tonic-gate return;
25360Sstevel@tonic-gate }
25370Sstevel@tonic-gate
25380Sstevel@tonic-gate /*
25390Sstevel@tonic-gate * Once we are sure /etc/dfs/sharetab has been
25400Sstevel@tonic-gate * modified, flush netgroup cache entries.
25410Sstevel@tonic-gate */
25420Sstevel@tonic-gate netgrp_cache_flush();
25430Sstevel@tonic-gate
25440Sstevel@tonic-gate sh_free(share_list); /* free old list */
25450Sstevel@tonic-gate share_list = NULL;
25460Sstevel@tonic-gate
25470Sstevel@tonic-gate while ((res = getshare(f, &sh)) > 0) {
25480Sstevel@tonic-gate c++;
25490Sstevel@tonic-gate if (strcmp(sh->sh_fstype, "nfs") != 0)
25500Sstevel@tonic-gate continue;
25510Sstevel@tonic-gate
25520Sstevel@tonic-gate shp = malloc(sizeof (*shp));
25530Sstevel@tonic-gate if (shp == NULL)
25540Sstevel@tonic-gate goto alloc_failed;
25550Sstevel@tonic-gate if (share_list == NULL)
25560Sstevel@tonic-gate share_list = shp;
25570Sstevel@tonic-gate else
25580Sstevel@tonic-gate /* LINTED not used before set */
25590Sstevel@tonic-gate shp_prev->shl_next = shp;
25600Sstevel@tonic-gate shp_prev = shp;
25610Sstevel@tonic-gate shp->shl_next = NULL;
25620Sstevel@tonic-gate shp->shl_sh = sharedup(sh);
25630Sstevel@tonic-gate if (shp->shl_sh == NULL)
25640Sstevel@tonic-gate goto alloc_failed;
25650Sstevel@tonic-gate }
256611211SThomas.Haynes@Sun.COM
25670Sstevel@tonic-gate if (res < 0)
25680Sstevel@tonic-gate syslog(LOG_ERR, "%s: invalid at line %d\n",
25696859Sth199096 SHARETAB, c + 1);
25700Sstevel@tonic-gate
25710Sstevel@tonic-gate if (stat(SHARETAB, &st) != 0) {
25720Sstevel@tonic-gate syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB);
25733701Sth199096 (void) fclose(f);
25740Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock);
25750Sstevel@tonic-gate return;
25760Sstevel@tonic-gate }
257711211SThomas.Haynes@Sun.COM
25780Sstevel@tonic-gate last_sharetab_time = st.st_mtim;
25790Sstevel@tonic-gate (void) fclose(f);
25800Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock);
258111211SThomas.Haynes@Sun.COM
25820Sstevel@tonic-gate return;
25830Sstevel@tonic-gate
25840Sstevel@tonic-gate alloc_failed:
258511211SThomas.Haynes@Sun.COM
25860Sstevel@tonic-gate syslog(LOG_ERR, "check_sharetab: no memory");
25870Sstevel@tonic-gate sh_free(share_list);
25880Sstevel@tonic-gate share_list = NULL;
25890Sstevel@tonic-gate (void) fclose(f);
25900Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock);
25910Sstevel@tonic-gate }
25920Sstevel@tonic-gate
25930Sstevel@tonic-gate static void
sh_free(struct sh_list * shp)25940Sstevel@tonic-gate sh_free(struct sh_list *shp)
25950Sstevel@tonic-gate {
25960Sstevel@tonic-gate register struct sh_list *next;
25970Sstevel@tonic-gate
25980Sstevel@tonic-gate while (shp) {
25990Sstevel@tonic-gate sharefree(shp->shl_sh);
26000Sstevel@tonic-gate next = shp->shl_next;
26010Sstevel@tonic-gate free(shp);
26020Sstevel@tonic-gate shp = next;
26030Sstevel@tonic-gate }
26040Sstevel@tonic-gate }
26050Sstevel@tonic-gate
26060Sstevel@tonic-gate
26070Sstevel@tonic-gate /*
26080Sstevel@tonic-gate * Remove an entry from mounted list
26090Sstevel@tonic-gate */
26100Sstevel@tonic-gate static void
umount(struct svc_req * rqstp)26110Sstevel@tonic-gate umount(struct svc_req *rqstp)
26120Sstevel@tonic-gate {
26130Sstevel@tonic-gate char *host, *path, *remove_path;
26140Sstevel@tonic-gate char rpath[MAXPATHLEN];
26150Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL;
26160Sstevel@tonic-gate SVCXPRT *transp;
26170Sstevel@tonic-gate struct netbuf *nb;
26180Sstevel@tonic-gate
26190Sstevel@tonic-gate transp = rqstp->rq_xprt;
26200Sstevel@tonic-gate path = NULL;
26210Sstevel@tonic-gate if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) {
26220Sstevel@tonic-gate svcerr_decode(transp);
26230Sstevel@tonic-gate return;
26240Sstevel@tonic-gate }
26250Sstevel@tonic-gate errno = 0;
26260Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_void, (char *)NULL))
26270Sstevel@tonic-gate log_cant_reply(transp);
26280Sstevel@tonic-gate
262912393SJan.Kryl@Sun.COM if (getclientsnames(transp, &nb, &clnames) != 0) {
26300Sstevel@tonic-gate /*
26310Sstevel@tonic-gate * Without the hostname we can't do audit or delete
26320Sstevel@tonic-gate * this host from the mount entries.
26330Sstevel@tonic-gate */
26340Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
26350Sstevel@tonic-gate return;
26360Sstevel@tonic-gate }
26370Sstevel@tonic-gate host = clnames->h_hostservs[0].h_host;
26380Sstevel@tonic-gate
26390Sstevel@tonic-gate if (verbose)
26400Sstevel@tonic-gate syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path);
26410Sstevel@tonic-gate
26420Sstevel@tonic-gate audit_mountd_umount(host, path);
26430Sstevel@tonic-gate
26440Sstevel@tonic-gate remove_path = rpath; /* assume we will use the cannonical path */
26450Sstevel@tonic-gate if (realpath(path, rpath) == NULL) {
26460Sstevel@tonic-gate if (verbose)
26470Sstevel@tonic-gate syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path);
26480Sstevel@tonic-gate remove_path = path; /* use path provided instead */
26490Sstevel@tonic-gate }
26500Sstevel@tonic-gate
26510Sstevel@tonic-gate mntlist_delete(host, remove_path); /* remove from mount list */
26520Sstevel@tonic-gate
26530Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path);
26540Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST);
26550Sstevel@tonic-gate }
26560Sstevel@tonic-gate
26570Sstevel@tonic-gate /*
26580Sstevel@tonic-gate * Remove all entries for one machine from mounted list
26590Sstevel@tonic-gate */
26600Sstevel@tonic-gate static void
umountall(struct svc_req * rqstp)26610Sstevel@tonic-gate umountall(struct svc_req *rqstp)
26620Sstevel@tonic-gate {
26630Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL;
26640Sstevel@tonic-gate SVCXPRT *transp;
26650Sstevel@tonic-gate char *host;
26660Sstevel@tonic-gate struct netbuf *nb;
26670Sstevel@tonic-gate
26680Sstevel@tonic-gate transp = rqstp->rq_xprt;
26690Sstevel@tonic-gate if (!svc_getargs(transp, xdr_void, NULL)) {
26700Sstevel@tonic-gate svcerr_decode(transp);
26710Sstevel@tonic-gate return;
26720Sstevel@tonic-gate }
26730Sstevel@tonic-gate /*
26740Sstevel@tonic-gate * We assume that this call is asynchronous and made via rpcbind
26750Sstevel@tonic-gate * callit routine. Therefore return control immediately. The error
26760Sstevel@tonic-gate * causes rpcbind to remain silent, as opposed to every machine
26770Sstevel@tonic-gate * on the net blasting the requester with a response.
26780Sstevel@tonic-gate */
26790Sstevel@tonic-gate svcerr_systemerr(transp);
268012393SJan.Kryl@Sun.COM if (getclientsnames(transp, &nb, &clnames) != 0) {
26810Sstevel@tonic-gate /* Can't do anything without the name of the client */
26820Sstevel@tonic-gate return;
26830Sstevel@tonic-gate }
26840Sstevel@tonic-gate
26850Sstevel@tonic-gate host = clnames->h_hostservs[0].h_host;
26860Sstevel@tonic-gate
26870Sstevel@tonic-gate /*
26880Sstevel@tonic-gate * Remove all hosts entries from mount list
26890Sstevel@tonic-gate */
26900Sstevel@tonic-gate mntlist_delete_all(host);
26910Sstevel@tonic-gate
26920Sstevel@tonic-gate if (verbose)
26930Sstevel@tonic-gate syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host);
26940Sstevel@tonic-gate
26950Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST);
26960Sstevel@tonic-gate }
26970Sstevel@tonic-gate
26980Sstevel@tonic-gate void *
exmalloc(size_t size)26990Sstevel@tonic-gate exmalloc(size_t size)
27000Sstevel@tonic-gate {
27010Sstevel@tonic-gate void *ret;
27020Sstevel@tonic-gate
27030Sstevel@tonic-gate if ((ret = malloc(size)) == NULL) {
27040Sstevel@tonic-gate syslog(LOG_ERR, "Out of memory");
27050Sstevel@tonic-gate exit(1);
27060Sstevel@tonic-gate }
27070Sstevel@tonic-gate return (ret);
27080Sstevel@tonic-gate }
27090Sstevel@tonic-gate
27100Sstevel@tonic-gate static void
sigexit(int signum)27110Sstevel@tonic-gate sigexit(int signum)
27120Sstevel@tonic-gate {
27130Sstevel@tonic-gate
27140Sstevel@tonic-gate if (signum == SIGHUP)
2715199Sgt29601 _exit(0);
2716199Sgt29601 _exit(1);
27170Sstevel@tonic-gate }
27184971Sjarrett
27194971Sjarrett static tsol_tpent_t *
get_client_template(struct sockaddr * sock)27204971Sjarrett get_client_template(struct sockaddr *sock)
27214971Sjarrett {
27224971Sjarrett in_addr_t v4client;
27234971Sjarrett in6_addr_t v6client;
27244971Sjarrett char v4_addr[INET_ADDRSTRLEN];
27254971Sjarrett char v6_addr[INET6_ADDRSTRLEN];
27264971Sjarrett tsol_rhent_t *rh;
27274971Sjarrett tsol_tpent_t *tp;
27284971Sjarrett
27294971Sjarrett switch (sock->sa_family) {
27304971Sjarrett case AF_INET:
27314971Sjarrett v4client = ((struct sockaddr_in *)(void *)sock)->
27324971Sjarrett sin_addr.s_addr;
27334971Sjarrett if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) ==
27344971Sjarrett NULL)
27354971Sjarrett return (NULL);
27364971Sjarrett rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET);
27374971Sjarrett if (rh == NULL)
27384971Sjarrett return (NULL);
27394971Sjarrett tp = tsol_gettpbyname(rh->rh_template);
27404971Sjarrett tsol_freerhent(rh);
27414971Sjarrett return (tp);
27424971Sjarrett break;
27434971Sjarrett case AF_INET6:
27444971Sjarrett v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr;
27454971Sjarrett if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) ==
27464971Sjarrett NULL)
27474971Sjarrett return (NULL);
27484971Sjarrett rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6);
27494971Sjarrett if (rh == NULL)
27504971Sjarrett return (NULL);
27514971Sjarrett tp = tsol_gettpbyname(rh->rh_template);
27524971Sjarrett tsol_freerhent(rh);
27534971Sjarrett return (tp);
27544971Sjarrett break;
27554971Sjarrett default:
27564971Sjarrett return (NULL);
27574971Sjarrett }
27584971Sjarrett }
2759