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 /* 23*11211SThomas.Haynes@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 280Sstevel@tonic-gate /* All Rights Reserved */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate /* 310Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD 320Sstevel@tonic-gate * under license from the Regents of the University of California. 330Sstevel@tonic-gate */ 340Sstevel@tonic-gate 350Sstevel@tonic-gate #include <stdio.h> 360Sstevel@tonic-gate #include <stdlib.h> 370Sstevel@tonic-gate #include <ctype.h> 380Sstevel@tonic-gate #include <sys/types.h> 390Sstevel@tonic-gate #include <string.h> 400Sstevel@tonic-gate #include <syslog.h> 410Sstevel@tonic-gate #include <sys/param.h> 420Sstevel@tonic-gate #include <rpc/rpc.h> 430Sstevel@tonic-gate #include <sys/stat.h> 440Sstevel@tonic-gate #include <netconfig.h> 450Sstevel@tonic-gate #include <netdir.h> 460Sstevel@tonic-gate #include <sys/file.h> 470Sstevel@tonic-gate #include <sys/time.h> 480Sstevel@tonic-gate #include <sys/errno.h> 490Sstevel@tonic-gate #include <rpcsvc/mount.h> 500Sstevel@tonic-gate #include <sys/pathconf.h> 510Sstevel@tonic-gate #include <sys/systeminfo.h> 520Sstevel@tonic-gate #include <sys/utsname.h> 536859Sth199096 #include <sys/wait.h> 540Sstevel@tonic-gate #include <signal.h> 550Sstevel@tonic-gate #include <locale.h> 560Sstevel@tonic-gate #include <unistd.h> 570Sstevel@tonic-gate #include <errno.h> 580Sstevel@tonic-gate #include <sys/socket.h> 590Sstevel@tonic-gate #include <netinet/in.h> 600Sstevel@tonic-gate #include <arpa/inet.h> 610Sstevel@tonic-gate #include <netdb.h> 620Sstevel@tonic-gate #include <thread.h> 630Sstevel@tonic-gate #include <assert.h> 640Sstevel@tonic-gate #include <priv_utils.h> 652140Srmesta #include <nfs/auth.h> 662140Srmesta #include <nfs/nfssys.h> 670Sstevel@tonic-gate #include <nfs/nfs.h> 680Sstevel@tonic-gate #include <nfs/nfs_sec.h> 690Sstevel@tonic-gate #include <rpcsvc/daemon_utils.h> 700Sstevel@tonic-gate #include <deflt.h> 710Sstevel@tonic-gate #include "../../fslib.h" 723957Sth199096 #include <sharefs/share.h> 733957Sth199096 #include <sharefs/sharetab.h> 740Sstevel@tonic-gate #include "../lib/sharetab.h" 750Sstevel@tonic-gate #include "mountd.h" 764971Sjarrett #include <tsol/label.h> 774971Sjarrett #include <sys/tsol/label_macro.h> 784971Sjarrett #include <libtsnet.h> 79*11211SThomas.Haynes@Sun.COM #include <sys/sdt.h> 800Sstevel@tonic-gate 816859Sth199096 extern int daemonize_init(void); 826859Sth199096 extern void daemonize_fini(int fd); 836859Sth199096 840Sstevel@tonic-gate struct sh_list *share_list; 850Sstevel@tonic-gate 860Sstevel@tonic-gate rwlock_t sharetab_lock; /* lock to protect the cached sharetab */ 870Sstevel@tonic-gate static mutex_t mnttab_lock; /* prevent concurrent mnttab readers */ 880Sstevel@tonic-gate 89*11211SThomas.Haynes@Sun.COM static mutex_t logging_queue_lock; 90*11211SThomas.Haynes@Sun.COM static cond_t logging_queue_cv; 91*11211SThomas.Haynes@Sun.COM 92*11211SThomas.Haynes@Sun.COM static share_t *find_lofsentry(char *, int *); 93*11211SThomas.Haynes@Sun.COM static void getclientsnames_lazy(char *, struct netbuf **, 94*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **); 950Sstevel@tonic-gate static void getclientsnames(SVCXPRT *, struct netbuf **, 96*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **); 97*11211SThomas.Haynes@Sun.COM static int getclientsflavors_old(share_t *, SVCXPRT *, struct netbuf **, 98*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int *); 99*11211SThomas.Haynes@Sun.COM static int getclientsflavors_new(share_t *, SVCXPRT *, struct netbuf **, 100*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int *); 101*11211SThomas.Haynes@Sun.COM static int check_client_old(share_t *, SVCXPRT *, struct netbuf **, 102*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int); 103*11211SThomas.Haynes@Sun.COM static int check_client_new(share_t *, SVCXPRT *, struct netbuf **, 104*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **, int); 1050Sstevel@tonic-gate static void mnt(struct svc_req *, SVCXPRT *); 1060Sstevel@tonic-gate static void mnt_pathconf(struct svc_req *); 107*11211SThomas.Haynes@Sun.COM static int mount(struct svc_req *r); 1080Sstevel@tonic-gate static void sh_free(struct sh_list *); 1090Sstevel@tonic-gate static void umount(struct svc_req *); 1100Sstevel@tonic-gate static void umountall(struct svc_req *); 1110Sstevel@tonic-gate static int netmatch(struct netbuf *, char *); 1120Sstevel@tonic-gate static void sigexit(int); 1130Sstevel@tonic-gate static int newopts(char *); 1144971Sjarrett static tsol_tpent_t *get_client_template(struct sockaddr *); 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate static int verbose; 1170Sstevel@tonic-gate static int rejecting; 1180Sstevel@tonic-gate static int mount_vers_min = MOUNTVERS; 1190Sstevel@tonic-gate static int mount_vers_max = MOUNTVERS3; 1200Sstevel@tonic-gate 1217961SNatalie.Li@Sun.COM /* Needs to be accessed by nfscmd.c */ 122*11211SThomas.Haynes@Sun.COM int in_access_list(SVCXPRT *, struct netbuf **, 123*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **, char *); 1247961SNatalie.Li@Sun.COM 1257961SNatalie.Li@Sun.COM extern void nfscmd_func(void *, char *, size_t, door_desc_t *, uint_t); 1267961SNatalie.Li@Sun.COM 1272140Srmesta thread_t nfsauth_thread; 1287961SNatalie.Li@Sun.COM thread_t cmd_thread; 129*11211SThomas.Haynes@Sun.COM thread_t logging_thread; 130*11211SThomas.Haynes@Sun.COM 131*11211SThomas.Haynes@Sun.COM typedef struct logging_data { 132*11211SThomas.Haynes@Sun.COM char *ld_host; 133*11211SThomas.Haynes@Sun.COM char *ld_path; 134*11211SThomas.Haynes@Sun.COM char *ld_rpath; 135*11211SThomas.Haynes@Sun.COM int ld_status; 136*11211SThomas.Haynes@Sun.COM char *ld_netid; 137*11211SThomas.Haynes@Sun.COM struct netbuf *ld_nb; 138*11211SThomas.Haynes@Sun.COM struct logging_data *ld_next; 139*11211SThomas.Haynes@Sun.COM } logging_data; 140*11211SThomas.Haynes@Sun.COM 141*11211SThomas.Haynes@Sun.COM static logging_data *logging_head = NULL; 142*11211SThomas.Haynes@Sun.COM static logging_data *logging_tail = NULL; 1432140Srmesta 1442140Srmesta /* ARGSUSED */ 1452140Srmesta static void * 1462140Srmesta nfsauth_svc(void *arg) 1472140Srmesta { 1482140Srmesta int doorfd = -1; 1492140Srmesta uint_t darg; 1502140Srmesta #ifdef DEBUG 1512140Srmesta int dfd; 1522140Srmesta #endif 1532140Srmesta 1542140Srmesta if ((doorfd = door_create(nfsauth_func, NULL, 1552140Srmesta DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { 1562140Srmesta syslog(LOG_ERR, "Unable to create door: %m\n"); 1572140Srmesta exit(10); 1582140Srmesta } 1592140Srmesta 1602140Srmesta #ifdef DEBUG 1612140Srmesta /* 1622140Srmesta * Create a file system path for the door 1632140Srmesta */ 1642140Srmesta if ((dfd = open(MOUNTD_DOOR, O_RDWR|O_CREAT|O_TRUNC, 1652140Srmesta S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) { 1662140Srmesta syslog(LOG_ERR, "Unable to open %s: %m\n", MOUNTD_DOOR); 1672140Srmesta (void) close(doorfd); 1682140Srmesta exit(11); 1692140Srmesta } 1702140Srmesta 1712140Srmesta /* 1722140Srmesta * Clean up any stale namespace associations 1732140Srmesta */ 1742140Srmesta (void) fdetach(MOUNTD_DOOR); 1752140Srmesta 1762140Srmesta /* 1772140Srmesta * Register in namespace to pass to the kernel to door_ki_open 1782140Srmesta */ 1792140Srmesta if (fattach(doorfd, MOUNTD_DOOR) == -1) { 1802140Srmesta syslog(LOG_ERR, "Unable to fattach door: %m\n"); 1812140Srmesta (void) close(dfd); 1822140Srmesta (void) close(doorfd); 1832140Srmesta exit(12); 1842140Srmesta } 1852140Srmesta (void) close(dfd); 1862140Srmesta #endif 1872140Srmesta 1882140Srmesta /* 1892140Srmesta * Must pass the doorfd down to the kernel. 1902140Srmesta */ 1912140Srmesta darg = doorfd; 1922140Srmesta (void) _nfssys(MOUNTD_ARGS, &darg); 1932140Srmesta 1942140Srmesta /* 1952140Srmesta * Wait for incoming calls 1962140Srmesta */ 1972140Srmesta /*CONSTCOND*/ 1982140Srmesta for (;;) 1992140Srmesta (void) pause(); 2002140Srmesta 2012140Srmesta /*NOTREACHED*/ 2022140Srmesta syslog(LOG_ERR, gettext("Door server exited")); 2032140Srmesta return (NULL); 2042140Srmesta } 2052140Srmesta 2067961SNatalie.Li@Sun.COM /* 2077961SNatalie.Li@Sun.COM * NFS command service thread code for setup and handling of the 2087961SNatalie.Li@Sun.COM * nfs_cmd requests for character set conversion and other future 2097961SNatalie.Li@Sun.COM * events. 2107961SNatalie.Li@Sun.COM */ 2117961SNatalie.Li@Sun.COM 2127961SNatalie.Li@Sun.COM static void * 2137961SNatalie.Li@Sun.COM cmd_svc(void *arg) 2147961SNatalie.Li@Sun.COM { 2157961SNatalie.Li@Sun.COM int doorfd = -1; 2167961SNatalie.Li@Sun.COM uint_t darg; 2177961SNatalie.Li@Sun.COM 2187961SNatalie.Li@Sun.COM if ((doorfd = door_create(nfscmd_func, NULL, 2197961SNatalie.Li@Sun.COM DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { 2207961SNatalie.Li@Sun.COM syslog(LOG_ERR, "Unable to create cmd door: %m\n"); 2217961SNatalie.Li@Sun.COM exit(10); 2227961SNatalie.Li@Sun.COM } 2237961SNatalie.Li@Sun.COM 2247961SNatalie.Li@Sun.COM /* 2257961SNatalie.Li@Sun.COM * Must pass the doorfd down to the kernel. 2267961SNatalie.Li@Sun.COM */ 2277961SNatalie.Li@Sun.COM darg = doorfd; 2287961SNatalie.Li@Sun.COM (void) _nfssys(NFSCMD_ARGS, &darg); 2297961SNatalie.Li@Sun.COM 2307961SNatalie.Li@Sun.COM /* 2317961SNatalie.Li@Sun.COM * Wait for incoming calls 2327961SNatalie.Li@Sun.COM */ 2337961SNatalie.Li@Sun.COM /*CONSTCOND*/ 2347961SNatalie.Li@Sun.COM for (;;) 2357961SNatalie.Li@Sun.COM (void) pause(); 2367961SNatalie.Li@Sun.COM 2377961SNatalie.Li@Sun.COM /*NOTREACHED*/ 2387961SNatalie.Li@Sun.COM syslog(LOG_ERR, gettext("Cmd door server exited")); 2397961SNatalie.Li@Sun.COM return (NULL); 2407961SNatalie.Li@Sun.COM } 2416859Sth199096 242*11211SThomas.Haynes@Sun.COM static void 243*11211SThomas.Haynes@Sun.COM free_logging_data(logging_data *lq) 244*11211SThomas.Haynes@Sun.COM { 245*11211SThomas.Haynes@Sun.COM if (lq != NULL) { 246*11211SThomas.Haynes@Sun.COM free(lq->ld_host); 247*11211SThomas.Haynes@Sun.COM free(lq->ld_netid); 248*11211SThomas.Haynes@Sun.COM 249*11211SThomas.Haynes@Sun.COM if (lq->ld_nb != NULL) { 250*11211SThomas.Haynes@Sun.COM free(lq->ld_nb->buf); 251*11211SThomas.Haynes@Sun.COM free(lq->ld_nb); 252*11211SThomas.Haynes@Sun.COM } 253*11211SThomas.Haynes@Sun.COM 254*11211SThomas.Haynes@Sun.COM free(lq->ld_path); 255*11211SThomas.Haynes@Sun.COM free(lq->ld_rpath); 256*11211SThomas.Haynes@Sun.COM 257*11211SThomas.Haynes@Sun.COM free(lq); 258*11211SThomas.Haynes@Sun.COM } 259*11211SThomas.Haynes@Sun.COM } 260*11211SThomas.Haynes@Sun.COM 261*11211SThomas.Haynes@Sun.COM static logging_data * 262*11211SThomas.Haynes@Sun.COM remove_head_of_queue(void) 263*11211SThomas.Haynes@Sun.COM { 264*11211SThomas.Haynes@Sun.COM logging_data *lq; 265*11211SThomas.Haynes@Sun.COM 266*11211SThomas.Haynes@Sun.COM /* 267*11211SThomas.Haynes@Sun.COM * Pull it off the queue. 268*11211SThomas.Haynes@Sun.COM */ 269*11211SThomas.Haynes@Sun.COM lq = logging_head; 270*11211SThomas.Haynes@Sun.COM if (lq) { 271*11211SThomas.Haynes@Sun.COM logging_head = lq->ld_next; 272*11211SThomas.Haynes@Sun.COM 273*11211SThomas.Haynes@Sun.COM /* 274*11211SThomas.Haynes@Sun.COM * Drained it. 275*11211SThomas.Haynes@Sun.COM */ 276*11211SThomas.Haynes@Sun.COM if (logging_head == NULL) { 277*11211SThomas.Haynes@Sun.COM logging_tail = NULL; 278*11211SThomas.Haynes@Sun.COM } 279*11211SThomas.Haynes@Sun.COM } 280*11211SThomas.Haynes@Sun.COM 281*11211SThomas.Haynes@Sun.COM return (lq); 282*11211SThomas.Haynes@Sun.COM } 283*11211SThomas.Haynes@Sun.COM 284*11211SThomas.Haynes@Sun.COM static void 285*11211SThomas.Haynes@Sun.COM do_logging_queue(logging_data *lq) 286*11211SThomas.Haynes@Sun.COM { 287*11211SThomas.Haynes@Sun.COM logging_data *lq_clean = NULL; 288*11211SThomas.Haynes@Sun.COM int cleared = 0; 289*11211SThomas.Haynes@Sun.COM char *host; 290*11211SThomas.Haynes@Sun.COM 291*11211SThomas.Haynes@Sun.COM struct nd_hostservlist *clnames; 292*11211SThomas.Haynes@Sun.COM 293*11211SThomas.Haynes@Sun.COM while (lq) { 294*11211SThomas.Haynes@Sun.COM if (lq->ld_host == NULL) { 295*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_lazy); 296*11211SThomas.Haynes@Sun.COM getclientsnames_lazy(lq->ld_netid, 297*11211SThomas.Haynes@Sun.COM &lq->ld_nb, &clnames); 298*11211SThomas.Haynes@Sun.COM if (clnames == NULL) 299*11211SThomas.Haynes@Sun.COM host = NULL; 300*11211SThomas.Haynes@Sun.COM else 301*11211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host; 302*11211SThomas.Haynes@Sun.COM } else 303*11211SThomas.Haynes@Sun.COM host = lq->ld_host; 304*11211SThomas.Haynes@Sun.COM 305*11211SThomas.Haynes@Sun.COM audit_mountd_mount(host, lq->ld_path, lq->ld_status); /* BSM */ 306*11211SThomas.Haynes@Sun.COM 307*11211SThomas.Haynes@Sun.COM /* add entry to mount list */ 308*11211SThomas.Haynes@Sun.COM if (lq->ld_rpath) 309*11211SThomas.Haynes@Sun.COM mntlist_new(host, lq->ld_rpath); 310*11211SThomas.Haynes@Sun.COM 311*11211SThomas.Haynes@Sun.COM lq->ld_next = lq_clean; 312*11211SThomas.Haynes@Sun.COM lq_clean = lq; 313*11211SThomas.Haynes@Sun.COM 314*11211SThomas.Haynes@Sun.COM (void) mutex_lock(&logging_queue_lock); 315*11211SThomas.Haynes@Sun.COM lq = remove_head_of_queue(); 316*11211SThomas.Haynes@Sun.COM (void) mutex_unlock(&logging_queue_lock); 317*11211SThomas.Haynes@Sun.COM } 318*11211SThomas.Haynes@Sun.COM 319*11211SThomas.Haynes@Sun.COM while (lq_clean) { 320*11211SThomas.Haynes@Sun.COM lq = lq_clean; 321*11211SThomas.Haynes@Sun.COM lq_clean = lq->ld_next; 322*11211SThomas.Haynes@Sun.COM 323*11211SThomas.Haynes@Sun.COM free_logging_data(lq); 324*11211SThomas.Haynes@Sun.COM cleared++; 325*11211SThomas.Haynes@Sun.COM } 326*11211SThomas.Haynes@Sun.COM 327*11211SThomas.Haynes@Sun.COM DTRACE_PROBE1(mountd, logging_cleared, cleared); 328*11211SThomas.Haynes@Sun.COM } 329*11211SThomas.Haynes@Sun.COM 330*11211SThomas.Haynes@Sun.COM static void * 331*11211SThomas.Haynes@Sun.COM logging_svc(void *arg) 332*11211SThomas.Haynes@Sun.COM { 333*11211SThomas.Haynes@Sun.COM logging_data *lq; 334*11211SThomas.Haynes@Sun.COM 335*11211SThomas.Haynes@Sun.COM for (;;) { 336*11211SThomas.Haynes@Sun.COM (void) mutex_lock(&logging_queue_lock); 337*11211SThomas.Haynes@Sun.COM while (logging_head == NULL) { 338*11211SThomas.Haynes@Sun.COM (void) cond_wait(&logging_queue_cv, 339*11211SThomas.Haynes@Sun.COM &logging_queue_lock); 340*11211SThomas.Haynes@Sun.COM } 341*11211SThomas.Haynes@Sun.COM 342*11211SThomas.Haynes@Sun.COM lq = remove_head_of_queue(); 343*11211SThomas.Haynes@Sun.COM (void) mutex_unlock(&logging_queue_lock); 344*11211SThomas.Haynes@Sun.COM 345*11211SThomas.Haynes@Sun.COM do_logging_queue(lq); 346*11211SThomas.Haynes@Sun.COM } 347*11211SThomas.Haynes@Sun.COM 348*11211SThomas.Haynes@Sun.COM /*NOTREACHED*/ 349*11211SThomas.Haynes@Sun.COM syslog(LOG_ERR, gettext("Logging server exited")); 350*11211SThomas.Haynes@Sun.COM return (NULL); 351*11211SThomas.Haynes@Sun.COM } 352*11211SThomas.Haynes@Sun.COM 353249Sjwahlig int 354249Sjwahlig main(int argc, char *argv[]) 3550Sstevel@tonic-gate { 3562140Srmesta int pid; 3572140Srmesta int c; 3582140Srmesta int rpc_svc_mode = RPC_SVC_MT_AUTO; 3592140Srmesta int maxthreads; 3602140Srmesta int maxrecsz = RPC_MAXDATASIZE; 3612140Srmesta bool_t exclbind = TRUE; 3622140Srmesta bool_t can_do_mlp; 3632140Srmesta long thr_flags = (THR_NEW_LWP|THR_DAEMON); 3640Sstevel@tonic-gate 3656859Sth199096 int pipe_fd = -1; 3666859Sth199096 3670Sstevel@tonic-gate /* 3680Sstevel@tonic-gate * Mountd requires uid 0 for: 3690Sstevel@tonic-gate * /etc/rmtab updates (we could chown it to daemon) 3700Sstevel@tonic-gate * /etc/dfs/dfstab reading (it wants to lock out share which 3710Sstevel@tonic-gate * doesn't do any locking before first truncate; 3720Sstevel@tonic-gate * NFS share does; should use fcntl locking instead) 3730Sstevel@tonic-gate * Needed privileges: 3740Sstevel@tonic-gate * auditing 3750Sstevel@tonic-gate * nfs syscall 3760Sstevel@tonic-gate * file dac search (so it can stat all files) 3771676Sjpk * Optional privileges: 3781676Sjpk * MLP 3790Sstevel@tonic-gate */ 3801676Sjpk can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP); 3810Sstevel@tonic-gate if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, -1, -1, 3821676Sjpk PRIV_SYS_NFS, PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH, 3831676Sjpk can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) { 3840Sstevel@tonic-gate (void) fprintf(stderr, 3856859Sth199096 "%s: must be run with sufficient privileges\n", 3866859Sth199096 argv[0]); 3870Sstevel@tonic-gate exit(1); 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate maxthreads = 0; 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate while ((c = getopt(argc, argv, "vrm:")) != EOF) { 3930Sstevel@tonic-gate switch (c) { 3940Sstevel@tonic-gate case 'v': 3950Sstevel@tonic-gate verbose++; 3960Sstevel@tonic-gate break; 3970Sstevel@tonic-gate case 'r': 3980Sstevel@tonic-gate rejecting = 1; 3990Sstevel@tonic-gate break; 4000Sstevel@tonic-gate case 'm': 4010Sstevel@tonic-gate maxthreads = atoi(optarg); 4020Sstevel@tonic-gate if (maxthreads < 1) { 4030Sstevel@tonic-gate (void) fprintf(stderr, 4040Sstevel@tonic-gate "%s: must specify positive maximum threads count, using default\n", 4056859Sth199096 argv[0]); 4060Sstevel@tonic-gate maxthreads = 0; 4070Sstevel@tonic-gate } 4080Sstevel@tonic-gate break; 4090Sstevel@tonic-gate } 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate /* 4130Sstevel@tonic-gate * Read in the NFS version values from config file. 4140Sstevel@tonic-gate */ 4150Sstevel@tonic-gate if ((defopen(NFSADMIN)) == 0) { 4160Sstevel@tonic-gate char *defval; 4170Sstevel@tonic-gate int defvers; 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate if ((defval = defread("NFS_SERVER_VERSMIN=")) != NULL) { 4200Sstevel@tonic-gate errno = 0; 4210Sstevel@tonic-gate defvers = strtol(defval, (char **)NULL, 10); 4220Sstevel@tonic-gate if (errno == 0) { 4230Sstevel@tonic-gate mount_vers_min = defvers; 4240Sstevel@tonic-gate /* 4250Sstevel@tonic-gate * special because NFSv2 is 4260Sstevel@tonic-gate * supported by mount v1 & v2 4270Sstevel@tonic-gate */ 4280Sstevel@tonic-gate if (defvers == NFS_VERSION) 4290Sstevel@tonic-gate mount_vers_min = MOUNTVERS; 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate if ((defval = defread("NFS_SERVER_VERSMAX=")) != NULL) { 4330Sstevel@tonic-gate errno = 0; 4340Sstevel@tonic-gate defvers = strtol(defval, (char **)NULL, 10); 4350Sstevel@tonic-gate if (errno == 0) { 4360Sstevel@tonic-gate mount_vers_max = defvers; 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate } 4390Sstevel@tonic-gate 4400Sstevel@tonic-gate /* close defaults file */ 4410Sstevel@tonic-gate defopen(NULL); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate /* 4450Sstevel@tonic-gate * Sanity check versions, 4460Sstevel@tonic-gate * even though we may get versions > MOUNTVERS3, we still need 4470Sstevel@tonic-gate * to start nfsauth service, so continue on regardless of values. 4480Sstevel@tonic-gate */ 4490Sstevel@tonic-gate if (mount_vers_min > mount_vers_max) { 4506859Sth199096 fprintf(stderr, "NFS_SERVER_VERSMIN > NFS_SERVER_VERSMAX"); 4510Sstevel@tonic-gate mount_vers_max = mount_vers_min; 4520Sstevel@tonic-gate } 4530Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 4540Sstevel@tonic-gate (void) rwlock_init(&sharetab_lock, USYNC_THREAD, NULL); 4550Sstevel@tonic-gate (void) mutex_init(&mnttab_lock, USYNC_THREAD, NULL); 456*11211SThomas.Haynes@Sun.COM (void) mutex_init(&logging_queue_lock, USYNC_THREAD, NULL); 457*11211SThomas.Haynes@Sun.COM (void) cond_init(&logging_queue_cv, USYNC_THREAD, NULL); 458*11211SThomas.Haynes@Sun.COM 4590Sstevel@tonic-gate netgroup_init(); 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) 4620Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" 4630Sstevel@tonic-gate #endif 4640Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate /* Don't drop core if the NFS module isn't loaded. */ 4670Sstevel@tonic-gate (void) signal(SIGSYS, SIG_IGN); 4680Sstevel@tonic-gate 4696859Sth199096 pipe_fd = daemonize_init(); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate /* 4720Sstevel@tonic-gate * If we coredump it'll be in /core 4730Sstevel@tonic-gate */ 4740Sstevel@tonic-gate if (chdir("/") < 0) 4756859Sth199096 fprintf(stderr, "chdir /: %s", strerror(errno)); 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate openlog("mountd", LOG_PID, LOG_DAEMON); 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * establish our lock on the lock file and write our pid to it. 4810Sstevel@tonic-gate * exit if some other process holds the lock, or if there's any 4820Sstevel@tonic-gate * error in writing/locking the file. 4830Sstevel@tonic-gate */ 4840Sstevel@tonic-gate pid = _enter_daemon_lock(MOUNTD); 4850Sstevel@tonic-gate switch (pid) { 4860Sstevel@tonic-gate case 0: 4870Sstevel@tonic-gate break; 4880Sstevel@tonic-gate case -1: 4896859Sth199096 fprintf(stderr, "error locking for %s: %s", MOUNTD, 4900Sstevel@tonic-gate strerror(errno)); 4910Sstevel@tonic-gate exit(2); 4920Sstevel@tonic-gate default: 4930Sstevel@tonic-gate /* daemon was already running */ 4940Sstevel@tonic-gate exit(0); 4950Sstevel@tonic-gate } 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate audit_mountd_setup(); /* BSM */ 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate /* 5000Sstevel@tonic-gate * Tell RPC that we want automatic thread mode. 5010Sstevel@tonic-gate * A new thread will be spawned for each request. 5020Sstevel@tonic-gate */ 5030Sstevel@tonic-gate if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) { 5046859Sth199096 fprintf(stderr, "unable to set automatic MT mode"); 5050Sstevel@tonic-gate exit(1); 5060Sstevel@tonic-gate } 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate /* 5090Sstevel@tonic-gate * Enable non-blocking mode and maximum record size checks for 5100Sstevel@tonic-gate * connection oriented transports. 5110Sstevel@tonic-gate */ 5120Sstevel@tonic-gate if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) { 5136859Sth199096 fprintf(stderr, "unable to set RPC max record size"); 5140Sstevel@tonic-gate } 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate /* 5170Sstevel@tonic-gate * Prevent our non-priv udp and tcp ports bound w/wildcard addr 5180Sstevel@tonic-gate * from being hijacked by a bind to a more specific addr. 5190Sstevel@tonic-gate */ 5200Sstevel@tonic-gate if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) { 5216859Sth199096 fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND"); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate /* 5250Sstevel@tonic-gate * If the -m argument was specified, then set the 5260Sstevel@tonic-gate * maximum number of threads to the value specified. 5270Sstevel@tonic-gate */ 5280Sstevel@tonic-gate if (maxthreads > 0 && !rpc_control(RPC_SVC_THRMAX_SET, &maxthreads)) { 5296859Sth199096 fprintf(stderr, "unable to set maxthreads"); 5300Sstevel@tonic-gate exit(1); 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate /* 5340Sstevel@tonic-gate * Make sure to unregister any previous versions in case the 5350Sstevel@tonic-gate * user is reconfiguring the server in interesting ways. 5360Sstevel@tonic-gate */ 5370Sstevel@tonic-gate svc_unreg(MOUNTPROG, MOUNTVERS); 5380Sstevel@tonic-gate svc_unreg(MOUNTPROG, MOUNTVERS_POSIX); 5390Sstevel@tonic-gate svc_unreg(MOUNTPROG, MOUNTVERS3); 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate /* 5422140Srmesta * Create the nfsauth thread with same signal disposition 5432140Srmesta * as the main thread. We need to create a separate thread 5442140Srmesta * since mountd() will be both an RPC server (for remote 5452140Srmesta * traffic) _and_ a doors server (for kernel upcalls). 5460Sstevel@tonic-gate */ 5472140Srmesta if (thr_create(NULL, 0, nfsauth_svc, 0, thr_flags, &nfsauth_thread)) { 5486859Sth199096 fprintf(stderr, gettext("Failed to create NFSAUTH svc thread")); 5492140Srmesta exit(2); 5500Sstevel@tonic-gate } 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate /* 5537961SNatalie.Li@Sun.COM * Create the cmd service thread with same signal disposition 5547961SNatalie.Li@Sun.COM * as the main thread. We need to create a separate thread 5557961SNatalie.Li@Sun.COM * since mountd() will be both an RPC server (for remote 5567961SNatalie.Li@Sun.COM * traffic) _and_ a doors server (for kernel upcalls). 5577961SNatalie.Li@Sun.COM */ 5587961SNatalie.Li@Sun.COM if (thr_create(NULL, 0, cmd_svc, 0, thr_flags, &cmd_thread)) { 5597961SNatalie.Li@Sun.COM syslog(LOG_ERR, gettext("Failed to create CMD svc thread")); 5607961SNatalie.Li@Sun.COM exit(2); 5617961SNatalie.Li@Sun.COM } 5627961SNatalie.Li@Sun.COM 5637961SNatalie.Li@Sun.COM /* 564*11211SThomas.Haynes@Sun.COM * Create an additional thread to service the rmtab and 565*11211SThomas.Haynes@Sun.COM * audit_mountd_mount logging for mount requests. Use the same 566*11211SThomas.Haynes@Sun.COM * signal disposition as the main thread. We create 567*11211SThomas.Haynes@Sun.COM * a separate thread to allow the mount request threads to 568*11211SThomas.Haynes@Sun.COM * clear as soon as possible. 569*11211SThomas.Haynes@Sun.COM */ 570*11211SThomas.Haynes@Sun.COM if (thr_create(NULL, 0, logging_svc, 0, thr_flags, &logging_thread)) { 571*11211SThomas.Haynes@Sun.COM syslog(LOG_ERR, gettext("Failed to create LOGGING svc thread")); 572*11211SThomas.Haynes@Sun.COM exit(2); 573*11211SThomas.Haynes@Sun.COM } 574*11211SThomas.Haynes@Sun.COM 575*11211SThomas.Haynes@Sun.COM /* 5760Sstevel@tonic-gate * Create datagram and connection oriented services 5770Sstevel@tonic-gate */ 5780Sstevel@tonic-gate if (mount_vers_max >= MOUNTVERS) { 5790Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "datagram_v") == 0) { 5806859Sth199096 fprintf(stderr, 5816859Sth199096 "couldn't register datagram_v MOUNTVERS"); 5820Sstevel@tonic-gate exit(1); 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS, "circuit_v") == 0) { 5856859Sth199096 fprintf(stderr, 5866859Sth199096 "couldn't register circuit_v MOUNTVERS"); 5870Sstevel@tonic-gate exit(1); 5880Sstevel@tonic-gate } 5890Sstevel@tonic-gate } 590*11211SThomas.Haynes@Sun.COM 5910Sstevel@tonic-gate if (mount_vers_max >= MOUNTVERS_POSIX) { 5920Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX, 5936859Sth199096 "datagram_v") == 0) { 5946859Sth199096 fprintf(stderr, 5956859Sth199096 "couldn't register datagram_v MOUNTVERS_POSIX"); 5960Sstevel@tonic-gate exit(1); 5970Sstevel@tonic-gate } 5980Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS_POSIX, 5996859Sth199096 "circuit_v") == 0) { 6006859Sth199096 fprintf(stderr, 6016859Sth199096 "couldn't register circuit_v MOUNTVERS_POSIX"); 6020Sstevel@tonic-gate exit(1); 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate } 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate if (mount_vers_max >= MOUNTVERS3) { 6070Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "datagram_v") == 0) { 6086859Sth199096 fprintf(stderr, 6096859Sth199096 "couldn't register datagram_v MOUNTVERS3"); 6100Sstevel@tonic-gate exit(1); 6110Sstevel@tonic-gate } 6120Sstevel@tonic-gate if (svc_create(mnt, MOUNTPROG, MOUNTVERS3, "circuit_v") == 0) { 6136859Sth199096 fprintf(stderr, 6146859Sth199096 "couldn't register circuit_v MOUNTVERS3"); 6150Sstevel@tonic-gate exit(1); 6160Sstevel@tonic-gate } 6170Sstevel@tonic-gate } 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate /* 6200Sstevel@tonic-gate * Start serving 6210Sstevel@tonic-gate */ 6220Sstevel@tonic-gate rmtab_load(); 6236859Sth199096 6246859Sth199096 daemonize_fini(pipe_fd); 6250Sstevel@tonic-gate 6260Sstevel@tonic-gate /* Get rid of the most dangerous basic privileges. */ 6270Sstevel@tonic-gate __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_INFO, PRIV_PROC_SESSION, 6280Sstevel@tonic-gate (char *)NULL); 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate svc_run(); 6310Sstevel@tonic-gate syslog(LOG_ERR, "Error: svc_run shouldn't have returned"); 6320Sstevel@tonic-gate abort(); 6336859Sth199096 6340Sstevel@tonic-gate /* NOTREACHED */ 635249Sjwahlig return (0); 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate /* 6390Sstevel@tonic-gate * Server procedure switch routine 6400Sstevel@tonic-gate */ 6410Sstevel@tonic-gate void 6420Sstevel@tonic-gate mnt(struct svc_req *rqstp, SVCXPRT *transp) 6430Sstevel@tonic-gate { 6440Sstevel@tonic-gate switch (rqstp->rq_proc) { 6450Sstevel@tonic-gate case NULLPROC: 6460Sstevel@tonic-gate errno = 0; 6470Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_void, (char *)0)) 6480Sstevel@tonic-gate log_cant_reply(transp); 6490Sstevel@tonic-gate return; 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate case MOUNTPROC_MNT: 652*11211SThomas.Haynes@Sun.COM (void) mount(rqstp); 6530Sstevel@tonic-gate return; 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate case MOUNTPROC_DUMP: 6560Sstevel@tonic-gate mntlist_send(transp); 6570Sstevel@tonic-gate return; 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate case MOUNTPROC_UMNT: 6600Sstevel@tonic-gate umount(rqstp); 6610Sstevel@tonic-gate return; 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate case MOUNTPROC_UMNTALL: 6640Sstevel@tonic-gate umountall(rqstp); 6650Sstevel@tonic-gate return; 6660Sstevel@tonic-gate 6670Sstevel@tonic-gate case MOUNTPROC_EXPORT: 6680Sstevel@tonic-gate case MOUNTPROC_EXPORTALL: 6690Sstevel@tonic-gate export(rqstp); 6700Sstevel@tonic-gate return; 6710Sstevel@tonic-gate 6720Sstevel@tonic-gate case MOUNTPROC_PATHCONF: 6730Sstevel@tonic-gate if (rqstp->rq_vers == MOUNTVERS_POSIX) 6740Sstevel@tonic-gate mnt_pathconf(rqstp); 6750Sstevel@tonic-gate else 6760Sstevel@tonic-gate svcerr_noproc(transp); 6770Sstevel@tonic-gate return; 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate default: 6800Sstevel@tonic-gate svcerr_noproc(transp); 6810Sstevel@tonic-gate return; 6820Sstevel@tonic-gate } 6830Sstevel@tonic-gate } 6840Sstevel@tonic-gate 6850Sstevel@tonic-gate /* Set up anonymous client */ 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate struct nd_hostservlist * 6880Sstevel@tonic-gate anon_client(char *host) 6890Sstevel@tonic-gate { 6900Sstevel@tonic-gate struct nd_hostservlist *anon_hsl; 6910Sstevel@tonic-gate struct nd_hostserv *anon_hs; 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate anon_hsl = malloc(sizeof (*anon_hsl)); 6940Sstevel@tonic-gate if (anon_hsl == NULL) 6950Sstevel@tonic-gate return (NULL); 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate anon_hs = malloc(sizeof (*anon_hs)); 6980Sstevel@tonic-gate if (anon_hs == NULL) { 6990Sstevel@tonic-gate free(anon_hsl); 7000Sstevel@tonic-gate return (NULL); 7010Sstevel@tonic-gate } 7020Sstevel@tonic-gate 7030Sstevel@tonic-gate if (host == NULL) 7040Sstevel@tonic-gate anon_hs->h_host = strdup("(anon)"); 7050Sstevel@tonic-gate else 7060Sstevel@tonic-gate anon_hs->h_host = strdup(host); 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate if (anon_hs->h_host == NULL) { 7090Sstevel@tonic-gate free(anon_hs); 7100Sstevel@tonic-gate free(anon_hsl); 7110Sstevel@tonic-gate return (NULL); 7120Sstevel@tonic-gate } 7130Sstevel@tonic-gate anon_hs->h_serv = '\0'; 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate anon_hsl->h_cnt = 1; 7160Sstevel@tonic-gate anon_hsl->h_hostservs = anon_hs; 7170Sstevel@tonic-gate 7180Sstevel@tonic-gate return (anon_hsl); 7190Sstevel@tonic-gate } 7200Sstevel@tonic-gate 721*11211SThomas.Haynes@Sun.COM static void 722*11211SThomas.Haynes@Sun.COM getclientsnames_common(struct netconfig *nconf, struct netbuf **nbuf, 7230Sstevel@tonic-gate struct nd_hostservlist **serv) 7240Sstevel@tonic-gate { 7250Sstevel@tonic-gate char tmp[MAXIPADDRLEN]; 7260Sstevel@tonic-gate char *host = NULL; 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate /* 7290Sstevel@tonic-gate * Use the this API instead of the netdir_getbyaddr() 7300Sstevel@tonic-gate * to avoid service lookup. 7310Sstevel@tonic-gate */ 7320Sstevel@tonic-gate if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf)) { 7330Sstevel@tonic-gate host = &tmp[0]; 7340Sstevel@tonic-gate if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { 7350Sstevel@tonic-gate struct sockaddr_in *sa; 7360Sstevel@tonic-gate 7370Sstevel@tonic-gate /* LINTED pointer alignment */ 7380Sstevel@tonic-gate sa = (struct sockaddr_in *)((*nbuf)->buf); 7390Sstevel@tonic-gate (void) inet_ntoa_r(sa->sin_addr, tmp); 7400Sstevel@tonic-gate } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { 7410Sstevel@tonic-gate struct sockaddr_in6 *sa; 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate /* LINTED pointer alignment */ 7440Sstevel@tonic-gate sa = (struct sockaddr_in6 *)((*nbuf)->buf); 7450Sstevel@tonic-gate (void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr, 7466859Sth199096 tmp, INET6_ADDRSTRLEN); 7470Sstevel@tonic-gate } 748*11211SThomas.Haynes@Sun.COM 7490Sstevel@tonic-gate *serv = anon_client(host); 750*11211SThomas.Haynes@Sun.COM } 751*11211SThomas.Haynes@Sun.COM } 752*11211SThomas.Haynes@Sun.COM 753*11211SThomas.Haynes@Sun.COM /* 754*11211SThomas.Haynes@Sun.COM * Get the client's hostname from the copy of the 755*11211SThomas.Haynes@Sun.COM * relevant transport handle parts. 756*11211SThomas.Haynes@Sun.COM * If the name is not available then return "(anon)". 757*11211SThomas.Haynes@Sun.COM */ 758*11211SThomas.Haynes@Sun.COM static void 759*11211SThomas.Haynes@Sun.COM getclientsnames_lazy(char *netid, struct netbuf **nbuf, 760*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **serv) 761*11211SThomas.Haynes@Sun.COM { 762*11211SThomas.Haynes@Sun.COM struct netconfig *nconf; 763*11211SThomas.Haynes@Sun.COM 764*11211SThomas.Haynes@Sun.COM nconf = getnetconfigent(netid); 765*11211SThomas.Haynes@Sun.COM if (nconf == NULL) { 766*11211SThomas.Haynes@Sun.COM syslog(LOG_ERR, "%s: getnetconfigent failed", netid); 767*11211SThomas.Haynes@Sun.COM *serv = anon_client(NULL); 7680Sstevel@tonic-gate return; 7690Sstevel@tonic-gate } 770*11211SThomas.Haynes@Sun.COM 771*11211SThomas.Haynes@Sun.COM getclientsnames_common(nconf, nbuf, serv); 772*11211SThomas.Haynes@Sun.COM freenetconfigent(nconf); 773*11211SThomas.Haynes@Sun.COM } 774*11211SThomas.Haynes@Sun.COM 775*11211SThomas.Haynes@Sun.COM /* 776*11211SThomas.Haynes@Sun.COM * Get the client's hostname from the transport handle. 777*11211SThomas.Haynes@Sun.COM * If the name is not available then return "(anon)". 778*11211SThomas.Haynes@Sun.COM */ 779*11211SThomas.Haynes@Sun.COM void 780*11211SThomas.Haynes@Sun.COM getclientsnames(SVCXPRT *transp, struct netbuf **nbuf, 781*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **serv) 782*11211SThomas.Haynes@Sun.COM { 783*11211SThomas.Haynes@Sun.COM struct netconfig *nconf; 784*11211SThomas.Haynes@Sun.COM 785*11211SThomas.Haynes@Sun.COM nconf = getnetconfigent(transp->xp_netid); 786*11211SThomas.Haynes@Sun.COM if (nconf == NULL) { 787*11211SThomas.Haynes@Sun.COM syslog(LOG_ERR, "%s: getnetconfigent failed", 788*11211SThomas.Haynes@Sun.COM transp->xp_netid); 789*11211SThomas.Haynes@Sun.COM *serv = anon_client(NULL); 790*11211SThomas.Haynes@Sun.COM return; 791*11211SThomas.Haynes@Sun.COM } 792*11211SThomas.Haynes@Sun.COM 793*11211SThomas.Haynes@Sun.COM *nbuf = svc_getrpccaller(transp); 794*11211SThomas.Haynes@Sun.COM if (*nbuf == NULL) { 795*11211SThomas.Haynes@Sun.COM freenetconfigent(nconf); 796*11211SThomas.Haynes@Sun.COM *serv = anon_client(NULL); 797*11211SThomas.Haynes@Sun.COM return; 798*11211SThomas.Haynes@Sun.COM } 799*11211SThomas.Haynes@Sun.COM 800*11211SThomas.Haynes@Sun.COM getclientsnames_common(nconf, nbuf, serv); 8010Sstevel@tonic-gate freenetconfigent(nconf); 8020Sstevel@tonic-gate } 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate void 8050Sstevel@tonic-gate log_cant_reply(SVCXPRT *transp) 8060Sstevel@tonic-gate { 8070Sstevel@tonic-gate int saverrno; 8080Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL; 8090Sstevel@tonic-gate register char *host; 8100Sstevel@tonic-gate struct netbuf *nb; 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate saverrno = errno; /* save error code */ 8130Sstevel@tonic-gate getclientsnames(transp, &nb, &clnames); 8140Sstevel@tonic-gate if (clnames == NULL) 8150Sstevel@tonic-gate return; 8160Sstevel@tonic-gate host = clnames->h_hostservs->h_host; 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate errno = saverrno; 8190Sstevel@tonic-gate if (errno == 0) 8200Sstevel@tonic-gate syslog(LOG_ERR, "couldn't send reply to %s", host); 8210Sstevel@tonic-gate else 8220Sstevel@tonic-gate syslog(LOG_ERR, "couldn't send reply to %s: %m", host); 8230Sstevel@tonic-gate 8240Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST); 8250Sstevel@tonic-gate } 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate /* 8280Sstevel@tonic-gate * Answer pathconf questions for the mount point fs 8290Sstevel@tonic-gate */ 8300Sstevel@tonic-gate static void 8310Sstevel@tonic-gate mnt_pathconf(struct svc_req *rqstp) 8320Sstevel@tonic-gate { 8330Sstevel@tonic-gate SVCXPRT *transp; 8340Sstevel@tonic-gate struct pathcnf p; 8350Sstevel@tonic-gate char *path, rpath[MAXPATHLEN]; 8360Sstevel@tonic-gate struct stat st; 8370Sstevel@tonic-gate 8380Sstevel@tonic-gate transp = rqstp->rq_xprt; 8390Sstevel@tonic-gate path = NULL; 8400Sstevel@tonic-gate (void) memset((caddr_t)&p, 0, sizeof (p)); 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) { 8430Sstevel@tonic-gate svcerr_decode(transp); 8440Sstevel@tonic-gate return; 8450Sstevel@tonic-gate } 8460Sstevel@tonic-gate if (lstat(path, &st) < 0) { 8470Sstevel@tonic-gate _PC_SET(_PC_ERROR, p.pc_mask); 8480Sstevel@tonic-gate goto done; 8490Sstevel@tonic-gate } 8500Sstevel@tonic-gate /* 8510Sstevel@tonic-gate * Get a path without symbolic links. 8520Sstevel@tonic-gate */ 8530Sstevel@tonic-gate if (realpath(path, rpath) == NULL) { 8540Sstevel@tonic-gate syslog(LOG_DEBUG, 8556859Sth199096 "mount request: realpath failed on %s: %m", 8566859Sth199096 path); 8570Sstevel@tonic-gate _PC_SET(_PC_ERROR, p.pc_mask); 8580Sstevel@tonic-gate goto done; 8590Sstevel@tonic-gate } 8600Sstevel@tonic-gate (void) memset((caddr_t)&p, 0, sizeof (p)); 8610Sstevel@tonic-gate /* 8620Sstevel@tonic-gate * can't ask about devices over NFS 8630Sstevel@tonic-gate */ 8640Sstevel@tonic-gate _PC_SET(_PC_MAX_CANON, p.pc_mask); 8650Sstevel@tonic-gate _PC_SET(_PC_MAX_INPUT, p.pc_mask); 8660Sstevel@tonic-gate _PC_SET(_PC_PIPE_BUF, p.pc_mask); 8670Sstevel@tonic-gate _PC_SET(_PC_VDISABLE, p.pc_mask); 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate errno = 0; 8700Sstevel@tonic-gate p.pc_link_max = pathconf(rpath, _PC_LINK_MAX); 8710Sstevel@tonic-gate if (errno) 8720Sstevel@tonic-gate _PC_SET(_PC_LINK_MAX, p.pc_mask); 8730Sstevel@tonic-gate p.pc_name_max = pathconf(rpath, _PC_NAME_MAX); 8740Sstevel@tonic-gate if (errno) 8750Sstevel@tonic-gate _PC_SET(_PC_NAME_MAX, p.pc_mask); 8760Sstevel@tonic-gate p.pc_path_max = pathconf(rpath, _PC_PATH_MAX); 8770Sstevel@tonic-gate if (errno) 8780Sstevel@tonic-gate _PC_SET(_PC_PATH_MAX, p.pc_mask); 8790Sstevel@tonic-gate if (pathconf(rpath, _PC_NO_TRUNC) == 1) 8800Sstevel@tonic-gate _PC_SET(_PC_NO_TRUNC, p.pc_mask); 8810Sstevel@tonic-gate if (pathconf(rpath, _PC_CHOWN_RESTRICTED) == 1) 8820Sstevel@tonic-gate _PC_SET(_PC_CHOWN_RESTRICTED, p.pc_mask); 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate done: 8850Sstevel@tonic-gate errno = 0; 8860Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_ppathcnf, (char *)&p)) 8870Sstevel@tonic-gate log_cant_reply(transp); 8880Sstevel@tonic-gate if (path != NULL) 8890Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate /* 8930Sstevel@tonic-gate * If the rootmount (export) option is specified, the all mount requests for 8940Sstevel@tonic-gate * subdirectories return EACCES. 8950Sstevel@tonic-gate */ 8960Sstevel@tonic-gate static int 897*11211SThomas.Haynes@Sun.COM checkrootmount(share_t *sh, char *rpath) 8980Sstevel@tonic-gate { 8990Sstevel@tonic-gate char *val; 9000Sstevel@tonic-gate 9010Sstevel@tonic-gate if ((val = getshareopt(sh->sh_opts, SHOPT_NOSUB)) != NULL) { 9020Sstevel@tonic-gate free(val); 9030Sstevel@tonic-gate if (strcmp(sh->sh_path, rpath) != 0) 9040Sstevel@tonic-gate return (0); 9050Sstevel@tonic-gate else 9060Sstevel@tonic-gate return (1); 9070Sstevel@tonic-gate } else 9080Sstevel@tonic-gate return (1); 9090Sstevel@tonic-gate } 9100Sstevel@tonic-gate 9110Sstevel@tonic-gate #define MAX_FLAVORS 128 9120Sstevel@tonic-gate 9130Sstevel@tonic-gate /* 9140Sstevel@tonic-gate * Return only EACCES if client does not have access 9150Sstevel@tonic-gate * to this directory. 9160Sstevel@tonic-gate * "If the server exports only /a/b, an attempt to 9170Sstevel@tonic-gate * mount a/b/c will fail with ENOENT if the directory 9180Sstevel@tonic-gate * does not exist"... However, if the client 9190Sstevel@tonic-gate * does not have access to /a/b, an attacker can 9200Sstevel@tonic-gate * determine whether the directory exists. 9210Sstevel@tonic-gate * This routine checks either existence of the file or 9220Sstevel@tonic-gate * existence of the file name entry in the mount table. 9230Sstevel@tonic-gate * If the file exists and there is no file name entry, 9240Sstevel@tonic-gate * the error returned should be EACCES. 9250Sstevel@tonic-gate * If the file does not exist, it must be determined 9260Sstevel@tonic-gate * whether the client has access to a parent 9270Sstevel@tonic-gate * directory. If the client has access to a parent 9280Sstevel@tonic-gate * directory, the error returned should be ENOENT, 9290Sstevel@tonic-gate * otherwise EACCES. 9300Sstevel@tonic-gate */ 9310Sstevel@tonic-gate static int 932*11211SThomas.Haynes@Sun.COM mount_enoent_error(SVCXPRT *transp, char *path, char *rpath, 933*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, struct netbuf **nb, int *flavor_list) 9340Sstevel@tonic-gate { 9350Sstevel@tonic-gate char *checkpath, *dp; 936*11211SThomas.Haynes@Sun.COM share_t *sh = NULL; 9370Sstevel@tonic-gate int realpath_error = ENOENT, reply_error = EACCES, lofs_tried = 0; 9380Sstevel@tonic-gate int flavor_count; 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate checkpath = strdup(path); 9410Sstevel@tonic-gate if (checkpath == NULL) { 9420Sstevel@tonic-gate syslog(LOG_ERR, "mount_enoent: no memory"); 9430Sstevel@tonic-gate return (EACCES); 9440Sstevel@tonic-gate } 9450Sstevel@tonic-gate 9460Sstevel@tonic-gate /* CONSTCOND */ 9470Sstevel@tonic-gate while (1) { 9480Sstevel@tonic-gate if (sh) { 9490Sstevel@tonic-gate sharefree(sh); 9500Sstevel@tonic-gate sh = NULL; 9510Sstevel@tonic-gate } 952*11211SThomas.Haynes@Sun.COM 9530Sstevel@tonic-gate if ((sh = findentry(rpath)) == NULL && 9546859Sth199096 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) { 9550Sstevel@tonic-gate /* 9560Sstevel@tonic-gate * There is no file name entry. 9570Sstevel@tonic-gate * If the file (with symbolic links resolved) exists, 9580Sstevel@tonic-gate * the error returned should be EACCES. 9590Sstevel@tonic-gate */ 9600Sstevel@tonic-gate if (realpath_error == 0) 9610Sstevel@tonic-gate break; 9620Sstevel@tonic-gate } else if (checkrootmount(sh, rpath) == 0) { 9630Sstevel@tonic-gate /* 9640Sstevel@tonic-gate * This is a "nosub" only export, in which case, 9650Sstevel@tonic-gate * mounting subdirectories isn't allowed. 9660Sstevel@tonic-gate * If the file (with symbolic links resolved) exists, 9670Sstevel@tonic-gate * the error returned should be EACCES. 9680Sstevel@tonic-gate */ 9690Sstevel@tonic-gate if (realpath_error == 0) 9700Sstevel@tonic-gate break; 9710Sstevel@tonic-gate } else { 9720Sstevel@tonic-gate /* 9730Sstevel@tonic-gate * Check permissions in mount table. 9740Sstevel@tonic-gate */ 9750Sstevel@tonic-gate if (newopts(sh->sh_opts)) 976*11211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_new(sh, 977*11211SThomas.Haynes@Sun.COM transp, nb, clnames, flavor_list); 9780Sstevel@tonic-gate else 979*11211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_old(sh, 980*11211SThomas.Haynes@Sun.COM transp, nb, clnames, flavor_list); 9810Sstevel@tonic-gate if (flavor_count != 0) { 9820Sstevel@tonic-gate /* 9830Sstevel@tonic-gate * Found entry in table and 9840Sstevel@tonic-gate * client has correct permissions. 9850Sstevel@tonic-gate */ 9860Sstevel@tonic-gate reply_error = ENOENT; 9870Sstevel@tonic-gate break; 9880Sstevel@tonic-gate } 9890Sstevel@tonic-gate } 990*11211SThomas.Haynes@Sun.COM 9910Sstevel@tonic-gate /* 9920Sstevel@tonic-gate * Check all parent directories. 9930Sstevel@tonic-gate */ 9940Sstevel@tonic-gate dp = strrchr(checkpath, '/'); 9950Sstevel@tonic-gate if (dp == NULL) 9960Sstevel@tonic-gate break; 9970Sstevel@tonic-gate *dp = '\0'; 9980Sstevel@tonic-gate if (strlen(checkpath) == 0) 9990Sstevel@tonic-gate break; 10000Sstevel@tonic-gate /* 10010Sstevel@tonic-gate * Get the real path (no symbolic links in it) 10020Sstevel@tonic-gate */ 10030Sstevel@tonic-gate if (realpath(checkpath, rpath) == NULL) { 10040Sstevel@tonic-gate if (errno != ENOENT) 10050Sstevel@tonic-gate break; 10060Sstevel@tonic-gate } else { 10070Sstevel@tonic-gate realpath_error = 0; 10080Sstevel@tonic-gate } 10090Sstevel@tonic-gate } 10100Sstevel@tonic-gate 10110Sstevel@tonic-gate if (sh) 10120Sstevel@tonic-gate sharefree(sh); 10130Sstevel@tonic-gate free(checkpath); 10140Sstevel@tonic-gate return (reply_error); 10150Sstevel@tonic-gate } 10160Sstevel@tonic-gate 10170Sstevel@tonic-gate /* 1018*11211SThomas.Haynes@Sun.COM * We need to inform the caller whether or not we were 1019*11211SThomas.Haynes@Sun.COM * able to add a node to the queue. If we are not, then 1020*11211SThomas.Haynes@Sun.COM * it is up to the caller to go ahead and log the data. 1021*11211SThomas.Haynes@Sun.COM */ 1022*11211SThomas.Haynes@Sun.COM static int 1023*11211SThomas.Haynes@Sun.COM enqueue_logging_data(char *host, SVCXPRT *transp, char *path, 1024*11211SThomas.Haynes@Sun.COM char *rpath, int status, int error) 1025*11211SThomas.Haynes@Sun.COM { 1026*11211SThomas.Haynes@Sun.COM logging_data *lq; 1027*11211SThomas.Haynes@Sun.COM struct netbuf *nb; 1028*11211SThomas.Haynes@Sun.COM 1029*11211SThomas.Haynes@Sun.COM lq = (logging_data *)calloc(1, sizeof (logging_data)); 1030*11211SThomas.Haynes@Sun.COM if (lq == NULL) 1031*11211SThomas.Haynes@Sun.COM goto cleanup; 1032*11211SThomas.Haynes@Sun.COM 1033*11211SThomas.Haynes@Sun.COM /* 1034*11211SThomas.Haynes@Sun.COM * We might not yet have the host... 1035*11211SThomas.Haynes@Sun.COM */ 1036*11211SThomas.Haynes@Sun.COM if (host) { 1037*11211SThomas.Haynes@Sun.COM DTRACE_PROBE1(mountd, log_host, host); 1038*11211SThomas.Haynes@Sun.COM lq->ld_host = strdup(host); 1039*11211SThomas.Haynes@Sun.COM if (lq->ld_host == NULL) 1040*11211SThomas.Haynes@Sun.COM goto cleanup; 1041*11211SThomas.Haynes@Sun.COM } else { 1042*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, log_no_host); 1043*11211SThomas.Haynes@Sun.COM 1044*11211SThomas.Haynes@Sun.COM lq->ld_netid = strdup(transp->xp_netid); 1045*11211SThomas.Haynes@Sun.COM if (lq->ld_netid == NULL) 1046*11211SThomas.Haynes@Sun.COM goto cleanup; 1047*11211SThomas.Haynes@Sun.COM 1048*11211SThomas.Haynes@Sun.COM lq->ld_nb = calloc(1, sizeof (struct netbuf)); 1049*11211SThomas.Haynes@Sun.COM if (lq->ld_nb == NULL) 1050*11211SThomas.Haynes@Sun.COM goto cleanup; 1051*11211SThomas.Haynes@Sun.COM 1052*11211SThomas.Haynes@Sun.COM nb = svc_getrpccaller(transp); 1053*11211SThomas.Haynes@Sun.COM if (nb == NULL) { 1054*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, e__nb__enqueue); 1055*11211SThomas.Haynes@Sun.COM goto cleanup; 1056*11211SThomas.Haynes@Sun.COM } 1057*11211SThomas.Haynes@Sun.COM 1058*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, nb_set_enqueue); 1059*11211SThomas.Haynes@Sun.COM 1060*11211SThomas.Haynes@Sun.COM lq->ld_nb->maxlen = nb->maxlen; 1061*11211SThomas.Haynes@Sun.COM lq->ld_nb->len = nb->len; 1062*11211SThomas.Haynes@Sun.COM 1063*11211SThomas.Haynes@Sun.COM lq->ld_nb->buf = malloc(lq->ld_nb->len); 1064*11211SThomas.Haynes@Sun.COM if (lq->ld_nb->buf == NULL) 1065*11211SThomas.Haynes@Sun.COM goto cleanup; 1066*11211SThomas.Haynes@Sun.COM 1067*11211SThomas.Haynes@Sun.COM bcopy(nb->buf, lq->ld_nb->buf, lq->ld_nb->len); 1068*11211SThomas.Haynes@Sun.COM } 1069*11211SThomas.Haynes@Sun.COM 1070*11211SThomas.Haynes@Sun.COM lq->ld_path = strdup(path); 1071*11211SThomas.Haynes@Sun.COM if (lq->ld_path == NULL) 1072*11211SThomas.Haynes@Sun.COM goto cleanup; 1073*11211SThomas.Haynes@Sun.COM 1074*11211SThomas.Haynes@Sun.COM if (!error) { 1075*11211SThomas.Haynes@Sun.COM lq->ld_rpath = strdup(rpath); 1076*11211SThomas.Haynes@Sun.COM if (lq->ld_rpath == NULL) 1077*11211SThomas.Haynes@Sun.COM goto cleanup; 1078*11211SThomas.Haynes@Sun.COM } 1079*11211SThomas.Haynes@Sun.COM 1080*11211SThomas.Haynes@Sun.COM lq->ld_status = status; 1081*11211SThomas.Haynes@Sun.COM 1082*11211SThomas.Haynes@Sun.COM /* 1083*11211SThomas.Haynes@Sun.COM * Add to the tail of the logging queue. 1084*11211SThomas.Haynes@Sun.COM */ 1085*11211SThomas.Haynes@Sun.COM (void) mutex_lock(&logging_queue_lock); 1086*11211SThomas.Haynes@Sun.COM if (logging_tail == NULL) { 1087*11211SThomas.Haynes@Sun.COM logging_tail = logging_head = lq; 1088*11211SThomas.Haynes@Sun.COM } else { 1089*11211SThomas.Haynes@Sun.COM logging_tail->ld_next = lq; 1090*11211SThomas.Haynes@Sun.COM logging_tail = lq; 1091*11211SThomas.Haynes@Sun.COM } 1092*11211SThomas.Haynes@Sun.COM (void) cond_signal(&logging_queue_cv); 1093*11211SThomas.Haynes@Sun.COM (void) mutex_unlock(&logging_queue_lock); 1094*11211SThomas.Haynes@Sun.COM 1095*11211SThomas.Haynes@Sun.COM return (TRUE); 1096*11211SThomas.Haynes@Sun.COM 1097*11211SThomas.Haynes@Sun.COM cleanup: 1098*11211SThomas.Haynes@Sun.COM 1099*11211SThomas.Haynes@Sun.COM free_logging_data(lq); 1100*11211SThomas.Haynes@Sun.COM 1101*11211SThomas.Haynes@Sun.COM return (FALSE); 1102*11211SThomas.Haynes@Sun.COM } 1103*11211SThomas.Haynes@Sun.COM 1104*11211SThomas.Haynes@Sun.COM /* 11050Sstevel@tonic-gate * Check mount requests, add to mounted list if ok 11060Sstevel@tonic-gate */ 1107*11211SThomas.Haynes@Sun.COM static int 11080Sstevel@tonic-gate mount(struct svc_req *rqstp) 11090Sstevel@tonic-gate { 11100Sstevel@tonic-gate SVCXPRT *transp; 11111610Sthurlow int version, vers; 11120Sstevel@tonic-gate struct fhstatus fhs; 11130Sstevel@tonic-gate struct mountres3 mountres3; 11141610Sthurlow char fh[FHSIZE3]; 11151610Sthurlow int len = FHSIZE3; 11160Sstevel@tonic-gate char *path, rpath[MAXPATHLEN]; 1117*11211SThomas.Haynes@Sun.COM share_t *sh = NULL; 11180Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL; 11190Sstevel@tonic-gate char *host = NULL; 1120*11211SThomas.Haynes@Sun.COM int error = 0, lofs_tried = 0, enqueued; 11210Sstevel@tonic-gate int flavor_list[MAX_FLAVORS]; 11220Sstevel@tonic-gate int flavor_count; 1123*11211SThomas.Haynes@Sun.COM struct netbuf *nb = NULL; 11244971Sjarrett ucred_t *uc = NULL; 11250Sstevel@tonic-gate 1126*11211SThomas.Haynes@Sun.COM int audit_status; 1127*11211SThomas.Haynes@Sun.COM 11280Sstevel@tonic-gate transp = rqstp->rq_xprt; 11290Sstevel@tonic-gate version = rqstp->rq_vers; 11300Sstevel@tonic-gate path = NULL; 11310Sstevel@tonic-gate 11320Sstevel@tonic-gate if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) { 11330Sstevel@tonic-gate svcerr_decode(transp); 1134*11211SThomas.Haynes@Sun.COM return (EACCES); 11350Sstevel@tonic-gate } 11360Sstevel@tonic-gate 1137*11211SThomas.Haynes@Sun.COM /* 1138*11211SThomas.Haynes@Sun.COM * Put off getting the name for the client until we 1139*11211SThomas.Haynes@Sun.COM * need it. This is a performance gain. If we are logging, 1140*11211SThomas.Haynes@Sun.COM * then we don't care about performance and might as well 1141*11211SThomas.Haynes@Sun.COM * get the host name now in case we need to spit out an 1142*11211SThomas.Haynes@Sun.COM * error message. 1143*11211SThomas.Haynes@Sun.COM */ 1144*11211SThomas.Haynes@Sun.COM if (verbose) { 1145*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_verbose); 1146*11211SThomas.Haynes@Sun.COM getclientsnames(transp, &nb, &clnames); 1147*11211SThomas.Haynes@Sun.COM if (clnames == NULL || nb == NULL) { 1148*11211SThomas.Haynes@Sun.COM /* 1149*11211SThomas.Haynes@Sun.COM * We failed to get a name for the client, even 1150*11211SThomas.Haynes@Sun.COM * 'anon', probably because we ran out of memory. 1151*11211SThomas.Haynes@Sun.COM * In this situation it doesn't make sense to 1152*11211SThomas.Haynes@Sun.COM * allow the mount to succeed. 1153*11211SThomas.Haynes@Sun.COM */ 1154*11211SThomas.Haynes@Sun.COM error = EACCES; 1155*11211SThomas.Haynes@Sun.COM goto reply; 1156*11211SThomas.Haynes@Sun.COM } 1157*11211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host; 11580Sstevel@tonic-gate } 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate /* 11610Sstevel@tonic-gate * If the version being used is less than the minimum version, 11620Sstevel@tonic-gate * the filehandle translation should not be provided to the 11630Sstevel@tonic-gate * client. 11640Sstevel@tonic-gate */ 11650Sstevel@tonic-gate if (rejecting || version < mount_vers_min) { 11660Sstevel@tonic-gate if (verbose) 11670Sstevel@tonic-gate syslog(LOG_NOTICE, "Rejected mount: %s for %s", 11686859Sth199096 host, path); 11690Sstevel@tonic-gate error = EACCES; 11700Sstevel@tonic-gate goto reply; 11710Sstevel@tonic-gate } 11720Sstevel@tonic-gate 11730Sstevel@tonic-gate /* 11744971Sjarrett * Trusted Extension doesn't support nfsv2. nfsv2 client 11754971Sjarrett * uses MOUNT protocol v1 and v2. To prevent circumventing 11764971Sjarrett * TX label policy via using nfsv2 client, reject a mount 11774971Sjarrett * request with version less than 3 and log an error. 11781676Sjpk */ 11791676Sjpk if (is_system_labeled()) { 11804971Sjarrett if (version < 3) { 11814971Sjarrett if (verbose) 11824971Sjarrett syslog(LOG_ERR, 11834971Sjarrett "Rejected mount: TX doesn't support NFSv2"); 11844971Sjarrett error = EACCES; 11854971Sjarrett goto reply; 11864971Sjarrett } 11871676Sjpk } 11881676Sjpk 11891676Sjpk /* 11900Sstevel@tonic-gate * Get the real path (no symbolic links in it) 11910Sstevel@tonic-gate */ 11920Sstevel@tonic-gate if (realpath(path, rpath) == NULL) { 11930Sstevel@tonic-gate error = errno; 11940Sstevel@tonic-gate if (verbose) 11950Sstevel@tonic-gate syslog(LOG_ERR, 11966859Sth199096 "mount request: realpath: %s: %m", path); 11970Sstevel@tonic-gate if (error == ENOENT) 1198*11211SThomas.Haynes@Sun.COM error = mount_enoent_error(transp, path, rpath, 1199*11211SThomas.Haynes@Sun.COM &clnames, &nb, flavor_list); 12000Sstevel@tonic-gate goto reply; 12010Sstevel@tonic-gate } 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate if ((sh = findentry(rpath)) == NULL && 12046859Sth199096 (sh = find_lofsentry(rpath, &lofs_tried)) == NULL) { 12050Sstevel@tonic-gate error = EACCES; 12060Sstevel@tonic-gate goto reply; 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate 12090Sstevel@tonic-gate /* 12100Sstevel@tonic-gate * Check if this is a "nosub" only export, in which case, mounting 12110Sstevel@tonic-gate * subdirectories isn't allowed. Bug 1184573. 12120Sstevel@tonic-gate */ 12130Sstevel@tonic-gate if (checkrootmount(sh, rpath) == 0) { 12140Sstevel@tonic-gate error = EACCES; 12150Sstevel@tonic-gate goto reply; 12160Sstevel@tonic-gate } 12170Sstevel@tonic-gate 12180Sstevel@tonic-gate if (newopts(sh->sh_opts)) 1219*11211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_new(sh, transp, &nb, &clnames, 12206859Sth199096 flavor_list); 12210Sstevel@tonic-gate else 1222*11211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_old(sh, transp, &nb, &clnames, 12236859Sth199096 flavor_list); 12240Sstevel@tonic-gate 1225*11211SThomas.Haynes@Sun.COM if (clnames) 1226*11211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host; 1227*11211SThomas.Haynes@Sun.COM 12280Sstevel@tonic-gate if (flavor_count == 0) { 12290Sstevel@tonic-gate error = EACCES; 12300Sstevel@tonic-gate goto reply; 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate 12330Sstevel@tonic-gate /* 12344971Sjarrett * Check MAC policy here. The server side policy should be 12354971Sjarrett * consistent with client side mount policy, i.e. 12364971Sjarrett * - we disallow an admin_low unlabeled client to mount 12374971Sjarrett * - we disallow mount from a lower labeled client. 12384971Sjarrett */ 12394971Sjarrett if (is_system_labeled()) { 12404971Sjarrett m_label_t *clabel = NULL; 12414971Sjarrett m_label_t *slabel = NULL; 12424971Sjarrett m_label_t admin_low; 12434971Sjarrett 12444971Sjarrett if (svc_getcallerucred(rqstp->rq_xprt, &uc) != 0) { 12454971Sjarrett syslog(LOG_ERR, 12464971Sjarrett "mount request: Failed to get caller's ucred : %m"); 12474971Sjarrett error = EACCES; 12484971Sjarrett goto reply; 12494971Sjarrett } 12504971Sjarrett if ((clabel = ucred_getlabel(uc)) == NULL) { 12514971Sjarrett syslog(LOG_ERR, 12524971Sjarrett "mount request: can't get client label from ucred"); 12534971Sjarrett error = EACCES; 12544971Sjarrett goto reply; 12554971Sjarrett } 12564971Sjarrett 12574971Sjarrett bsllow(&admin_low); 12584971Sjarrett if (blequal(&admin_low, clabel)) { 12594971Sjarrett struct sockaddr *ca; 12604971Sjarrett tsol_tpent_t *tp; 12614971Sjarrett 12624971Sjarrett ca = (struct sockaddr *)(void *)svc_getrpccaller( 12634971Sjarrett rqstp->rq_xprt)->buf; 12644971Sjarrett if (ca == NULL) { 12654971Sjarrett error = EACCES; 12664971Sjarrett goto reply; 12674971Sjarrett } 12684971Sjarrett /* 12694971Sjarrett * get trusted network template associated 12704971Sjarrett * with the client. 12714971Sjarrett */ 12724971Sjarrett tp = get_client_template(ca); 12734971Sjarrett if (tp == NULL || tp->host_type != SUN_CIPSO) { 12744971Sjarrett if (tp != NULL) 12754971Sjarrett tsol_freetpent(tp); 12764971Sjarrett error = EACCES; 12774971Sjarrett goto reply; 12784971Sjarrett } 12794971Sjarrett tsol_freetpent(tp); 12804971Sjarrett } else { 12814971Sjarrett if ((slabel = m_label_alloc(MAC_LABEL)) == NULL) { 12824971Sjarrett error = EACCES; 12834971Sjarrett goto reply; 12844971Sjarrett } 12854971Sjarrett 12864971Sjarrett if (getlabel(rpath, slabel) != 0) { 12874971Sjarrett m_label_free(slabel); 12884971Sjarrett error = EACCES; 12894971Sjarrett goto reply; 12904971Sjarrett } 12914971Sjarrett 12924971Sjarrett if (!bldominates(clabel, slabel)) { 12934971Sjarrett m_label_free(slabel); 12944971Sjarrett error = EACCES; 12954971Sjarrett goto reply; 12964971Sjarrett } 12974971Sjarrett m_label_free(slabel); 12984971Sjarrett } 12994971Sjarrett } 13004971Sjarrett 13014971Sjarrett /* 13020Sstevel@tonic-gate * Now get the filehandle. 13030Sstevel@tonic-gate * 13041610Sthurlow * NFS V2 clients get a 32 byte filehandle. 13051610Sthurlow * NFS V3 clients get a 32 or 64 byte filehandle, depending on 13061610Sthurlow * the embedded FIDs. 13070Sstevel@tonic-gate */ 13081610Sthurlow vers = (version == MOUNTVERS3) ? NFS_V3 : NFS_VERSION; 13090Sstevel@tonic-gate 13100Sstevel@tonic-gate /* LINTED pointer alignment */ 13111610Sthurlow while (nfs_getfh(rpath, vers, &len, fh) < 0) { 13120Sstevel@tonic-gate if (errno == EINVAL && 13136859Sth199096 (sh = find_lofsentry(rpath, &lofs_tried)) != NULL) { 13140Sstevel@tonic-gate errno = 0; 13150Sstevel@tonic-gate continue; 13160Sstevel@tonic-gate } 13170Sstevel@tonic-gate error = errno == EINVAL ? EACCES : errno; 13180Sstevel@tonic-gate syslog(LOG_DEBUG, "mount request: getfh failed on %s: %m", 13196859Sth199096 path); 13200Sstevel@tonic-gate break; 13210Sstevel@tonic-gate } 13220Sstevel@tonic-gate 13231610Sthurlow if (version == MOUNTVERS3) { 13241610Sthurlow mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len = len; 13251610Sthurlow mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = fh; 13261610Sthurlow } else { 13271610Sthurlow bcopy(fh, &fhs.fhstatus_u.fhs_fhandle, NFS_FHSIZE); 13281610Sthurlow } 13291610Sthurlow 13300Sstevel@tonic-gate reply: 13314971Sjarrett if (uc != NULL) 13324971Sjarrett ucred_free(uc); 1333*11211SThomas.Haynes@Sun.COM 13340Sstevel@tonic-gate switch (version) { 13350Sstevel@tonic-gate case MOUNTVERS: 13360Sstevel@tonic-gate case MOUNTVERS_POSIX: 13370Sstevel@tonic-gate if (error == EINVAL) 13380Sstevel@tonic-gate fhs.fhs_status = NFSERR_ACCES; 13390Sstevel@tonic-gate else if (error == EREMOTE) 13400Sstevel@tonic-gate fhs.fhs_status = NFSERR_REMOTE; 13410Sstevel@tonic-gate else 13420Sstevel@tonic-gate fhs.fhs_status = error; 1343*11211SThomas.Haynes@Sun.COM 13440Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_fhstatus, (char *)&fhs)) 13450Sstevel@tonic-gate log_cant_reply(transp); 1346*11211SThomas.Haynes@Sun.COM 1347*11211SThomas.Haynes@Sun.COM audit_status = fhs.fhs_status; 13480Sstevel@tonic-gate break; 13490Sstevel@tonic-gate 13500Sstevel@tonic-gate case MOUNTVERS3: 13510Sstevel@tonic-gate if (!error) { 13520Sstevel@tonic-gate mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = 13536859Sth199096 flavor_list; 13540Sstevel@tonic-gate mountres3.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = 13556859Sth199096 flavor_count; 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate } else if (error == ENAMETOOLONG) 13580Sstevel@tonic-gate error = MNT3ERR_NAMETOOLONG; 13590Sstevel@tonic-gate 13600Sstevel@tonic-gate mountres3.fhs_status = error; 13610Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_mountres3, (char *)&mountres3)) 13620Sstevel@tonic-gate log_cant_reply(transp); 13630Sstevel@tonic-gate 1364*11211SThomas.Haynes@Sun.COM audit_status = mountres3.fhs_status; 13650Sstevel@tonic-gate break; 13660Sstevel@tonic-gate } 13670Sstevel@tonic-gate 13680Sstevel@tonic-gate if (verbose) 13690Sstevel@tonic-gate syslog(LOG_NOTICE, "MOUNT: %s %s %s", 13706859Sth199096 (host == NULL) ? "unknown host" : host, 13716859Sth199096 error ? "denied" : "mounted", path); 13720Sstevel@tonic-gate 1373*11211SThomas.Haynes@Sun.COM /* 1374*11211SThomas.Haynes@Sun.COM * If we can not create a queue entry, go ahead and do it 1375*11211SThomas.Haynes@Sun.COM * in the context of this thread. 1376*11211SThomas.Haynes@Sun.COM */ 1377*11211SThomas.Haynes@Sun.COM enqueued = enqueue_logging_data(host, transp, path, rpath, 1378*11211SThomas.Haynes@Sun.COM audit_status, error); 1379*11211SThomas.Haynes@Sun.COM if (enqueued == FALSE) { 1380*11211SThomas.Haynes@Sun.COM if (host == NULL) { 1381*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_in_thread); 1382*11211SThomas.Haynes@Sun.COM getclientsnames(transp, &nb, &clnames); 1383*11211SThomas.Haynes@Sun.COM if (clnames != NULL) 1384*11211SThomas.Haynes@Sun.COM host = clnames->h_hostservs[0].h_host; 1385*11211SThomas.Haynes@Sun.COM } 1386*11211SThomas.Haynes@Sun.COM 1387*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, logged_in_thread); 1388*11211SThomas.Haynes@Sun.COM audit_mountd_mount(host, path, audit_status); /* BSM */ 1389*11211SThomas.Haynes@Sun.COM if (!error) 1390*11211SThomas.Haynes@Sun.COM mntlist_new(host, rpath); /* add entry to mount list */ 1391*11211SThomas.Haynes@Sun.COM } 1392*11211SThomas.Haynes@Sun.COM 13930Sstevel@tonic-gate if (path != NULL) 13940Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); 13950Sstevel@tonic-gate 13960Sstevel@tonic-gate done: 13970Sstevel@tonic-gate if (sh) 13980Sstevel@tonic-gate sharefree(sh); 13990Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST); 1400*11211SThomas.Haynes@Sun.COM 1401*11211SThomas.Haynes@Sun.COM return (error); 14020Sstevel@tonic-gate } 14030Sstevel@tonic-gate 1404*11211SThomas.Haynes@Sun.COM share_t * 14050Sstevel@tonic-gate findentry(char *path) 14060Sstevel@tonic-gate { 1407*11211SThomas.Haynes@Sun.COM share_t *sh = NULL; 14080Sstevel@tonic-gate struct sh_list *shp; 14090Sstevel@tonic-gate register char *p1, *p2; 14100Sstevel@tonic-gate struct stat st1; 14110Sstevel@tonic-gate struct stat64 st2; 14120Sstevel@tonic-gate 14130Sstevel@tonic-gate check_sharetab(); 14140Sstevel@tonic-gate 14150Sstevel@tonic-gate (void) rw_rdlock(&sharetab_lock); 14160Sstevel@tonic-gate 14170Sstevel@tonic-gate for (shp = share_list; shp; shp = shp->shl_next) { 14180Sstevel@tonic-gate sh = shp->shl_sh; 14190Sstevel@tonic-gate for (p1 = sh->sh_path, p2 = path; *p1 == *p2; p1++, p2++) 14200Sstevel@tonic-gate if (*p1 == '\0') 14210Sstevel@tonic-gate goto done; /* exact match */ 14220Sstevel@tonic-gate 14230Sstevel@tonic-gate /* 14240Sstevel@tonic-gate * Now compare the pathnames for three cases: 14250Sstevel@tonic-gate * 14260Sstevel@tonic-gate * Parent: /export/foo (no trailing slash on parent) 14270Sstevel@tonic-gate * Child: /export/foo/bar 14280Sstevel@tonic-gate * 14290Sstevel@tonic-gate * Parent: /export/foo/ (trailing slash on parent) 14300Sstevel@tonic-gate * Child: /export/foo/bar 14310Sstevel@tonic-gate * 14320Sstevel@tonic-gate * Parent: /export/foo/ (no trailing slash on child) 14330Sstevel@tonic-gate * Child: /export/foo 14340Sstevel@tonic-gate * 14350Sstevel@tonic-gate * Then compare the dev_t of the parent and child to 14360Sstevel@tonic-gate * make sure that they're both in the same filesystem. 14370Sstevel@tonic-gate */ 14380Sstevel@tonic-gate if ((*p1 == '\0' && *p2 == '/') || 14390Sstevel@tonic-gate (*p1 == '\0' && *(p1-1) == '/') || 14400Sstevel@tonic-gate (*p2 == '\0' && *p1 == '/' && *(p1+1) == '\0')) { 14410Sstevel@tonic-gate if (stat(sh->sh_path, &st1) < 0) { 14420Sstevel@tonic-gate if (verbose) 14430Sstevel@tonic-gate syslog(LOG_NOTICE, "%s: %m", p1); 14440Sstevel@tonic-gate shp = NULL; 14450Sstevel@tonic-gate goto done; 14460Sstevel@tonic-gate } 1447*11211SThomas.Haynes@Sun.COM 14480Sstevel@tonic-gate /* 14490Sstevel@tonic-gate * Use stat64 on "path" since it might be larger 14500Sstevel@tonic-gate * than 2 Gb and 32 bit stat would fail EOVERFLOW 14510Sstevel@tonic-gate */ 14520Sstevel@tonic-gate if (stat64(path, &st2) < 0) { 14530Sstevel@tonic-gate if (verbose) 14540Sstevel@tonic-gate syslog(LOG_NOTICE, "%s: %m", p2); 14550Sstevel@tonic-gate shp = NULL; 14560Sstevel@tonic-gate goto done; 14570Sstevel@tonic-gate } 14580Sstevel@tonic-gate if (st1.st_dev == st2.st_dev) 14590Sstevel@tonic-gate goto done; 14600Sstevel@tonic-gate } 14610Sstevel@tonic-gate } 14620Sstevel@tonic-gate done: 14630Sstevel@tonic-gate sh = shp ? sharedup(sh) : NULL; 14640Sstevel@tonic-gate 14650Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock); 14660Sstevel@tonic-gate 14670Sstevel@tonic-gate return (sh); 14680Sstevel@tonic-gate } 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate 14710Sstevel@tonic-gate static int 14720Sstevel@tonic-gate is_substring(char **mntp, char **path) 14730Sstevel@tonic-gate { 14740Sstevel@tonic-gate char *p1 = *mntp, *p2 = *path; 14750Sstevel@tonic-gate 14760Sstevel@tonic-gate if (*p1 == '\0' && *p2 == '\0') /* exact match */ 14770Sstevel@tonic-gate return (1); 14780Sstevel@tonic-gate else if (*p1 == '\0' && *p2 == '/') 14790Sstevel@tonic-gate return (1); 14800Sstevel@tonic-gate else if (*p1 == '\0' && *(p1-1) == '/') { 14810Sstevel@tonic-gate *path = --p2; /* we need the slash in p2 */ 14820Sstevel@tonic-gate return (1); 14830Sstevel@tonic-gate } else if (*p2 == '\0') { 14840Sstevel@tonic-gate while (*p1 == '/') 14850Sstevel@tonic-gate p1++; 14860Sstevel@tonic-gate if (*p1 == '\0') /* exact match */ 14870Sstevel@tonic-gate return (1); 14880Sstevel@tonic-gate } 14890Sstevel@tonic-gate return (0); 14900Sstevel@tonic-gate } 14910Sstevel@tonic-gate 14920Sstevel@tonic-gate /* 14930Sstevel@tonic-gate * find_lofsentry() searches for the real path which this requested LOFS path 14940Sstevel@tonic-gate * (rpath) shadows. If found, it will return the sharetab entry of 14950Sstevel@tonic-gate * the real path that corresponds to the LOFS path. 14960Sstevel@tonic-gate * We first search mnttab to see if the requested path is an automounted 14970Sstevel@tonic-gate * path. If it is an automounted path, it will trigger the mount by stat()ing 14980Sstevel@tonic-gate * the requested path. Note that it is important to check that this path is 14990Sstevel@tonic-gate * actually an automounted path, otherwise we would stat() a path which may 15000Sstevel@tonic-gate * turn out to be NFS and block indefinitely on a dead server. The automounter 15010Sstevel@tonic-gate * times-out if the server is dead, so there's no risk of hanging this 15020Sstevel@tonic-gate * thread waiting for stat(). 15030Sstevel@tonic-gate * After the mount has been triggered (if necessary), we look for a 15040Sstevel@tonic-gate * mountpoint of type LOFS (by searching /etc/mnttab again) which 15050Sstevel@tonic-gate * is a substring of the rpath. If found, we construct a new path by 15060Sstevel@tonic-gate * concatenating the mnt_special and the remaining of rpath, call findentry() 15070Sstevel@tonic-gate * to make sure the 'real path' is shared. 15080Sstevel@tonic-gate */ 1509*11211SThomas.Haynes@Sun.COM static share_t * 15100Sstevel@tonic-gate find_lofsentry(char *rpath, int *done_flag) 15110Sstevel@tonic-gate { 15120Sstevel@tonic-gate struct stat r_stbuf; 15130Sstevel@tonic-gate mntlist_t *ml, *mntl, *mntpnt = NULL; 1514*11211SThomas.Haynes@Sun.COM share_t *retcode = NULL; 15150Sstevel@tonic-gate char tmp_path[MAXPATHLEN]; 15160Sstevel@tonic-gate int mntpnt_len = 0, tmp; 15170Sstevel@tonic-gate char *p1, *p2; 15180Sstevel@tonic-gate 15190Sstevel@tonic-gate if ((*done_flag)++) 15200Sstevel@tonic-gate return (retcode); 15210Sstevel@tonic-gate 15220Sstevel@tonic-gate /* 15230Sstevel@tonic-gate * While fsgetmntlist() uses lockf() to 15240Sstevel@tonic-gate * lock the mnttab before reading it in, 15250Sstevel@tonic-gate * the lock ignores threads in the same process. 15260Sstevel@tonic-gate * Read in the mnttab with the protection of a mutex. 15270Sstevel@tonic-gate */ 15280Sstevel@tonic-gate (void) mutex_lock(&mnttab_lock); 15290Sstevel@tonic-gate mntl = fsgetmntlist(); 15300Sstevel@tonic-gate (void) mutex_unlock(&mnttab_lock); 15310Sstevel@tonic-gate 15320Sstevel@tonic-gate /* 15330Sstevel@tonic-gate * Obtain the mountpoint for the requested path. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate for (ml = mntl; ml; ml = ml->mntl_next) { 15360Sstevel@tonic-gate for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath; 15376859Sth199096 *p1 == *p2 && *p1; p1++, p2++) 15386859Sth199096 ; 15390Sstevel@tonic-gate if (is_substring(&p1, &p2) && 15400Sstevel@tonic-gate (tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len) { 15410Sstevel@tonic-gate mntpnt = ml; 15420Sstevel@tonic-gate mntpnt_len = tmp; 15430Sstevel@tonic-gate } 15440Sstevel@tonic-gate } 15450Sstevel@tonic-gate 15460Sstevel@tonic-gate /* 15470Sstevel@tonic-gate * If the path needs to be autoFS mounted, trigger the mount by 15480Sstevel@tonic-gate * stat()ing it. This is determined by checking whether the 15490Sstevel@tonic-gate * mountpoint we just found is of type autofs. 15500Sstevel@tonic-gate */ 15510Sstevel@tonic-gate if (mntpnt != NULL && 15520Sstevel@tonic-gate strcmp(mntpnt->mntl_mnt->mnt_fstype, "autofs") == 0) { 15530Sstevel@tonic-gate /* 15540Sstevel@tonic-gate * The requested path is a substring of an autoFS filesystem. 15550Sstevel@tonic-gate * Trigger the mount. 15560Sstevel@tonic-gate */ 15570Sstevel@tonic-gate if (stat(rpath, &r_stbuf) < 0) { 15580Sstevel@tonic-gate if (verbose) 15590Sstevel@tonic-gate syslog(LOG_NOTICE, "%s: %m", rpath); 15600Sstevel@tonic-gate goto done; 15610Sstevel@tonic-gate } 15620Sstevel@tonic-gate if ((r_stbuf.st_mode & S_IFMT) == S_IFDIR) { 15630Sstevel@tonic-gate /* 15640Sstevel@tonic-gate * The requested path is a directory, stat(2) it 15650Sstevel@tonic-gate * again with a trailing '.' to force the autoFS 15660Sstevel@tonic-gate * module to trigger the mount of indirect 15670Sstevel@tonic-gate * automount entries, such as /net/jurassic/. 15680Sstevel@tonic-gate */ 15690Sstevel@tonic-gate if (strlen(rpath) + 2 > MAXPATHLEN) { 15700Sstevel@tonic-gate if (verbose) { 15710Sstevel@tonic-gate syslog(LOG_NOTICE, 15726859Sth199096 "%s/.: exceeds MAXPATHLEN %d", 15736859Sth199096 rpath, MAXPATHLEN); 15740Sstevel@tonic-gate } 15750Sstevel@tonic-gate goto done; 15760Sstevel@tonic-gate } 15770Sstevel@tonic-gate (void) strcpy(tmp_path, rpath); 15780Sstevel@tonic-gate (void) strcat(tmp_path, "/."); 15790Sstevel@tonic-gate 15800Sstevel@tonic-gate if (stat(tmp_path, &r_stbuf) < 0) { 15810Sstevel@tonic-gate if (verbose) 15820Sstevel@tonic-gate syslog(LOG_NOTICE, "%s: %m", tmp_path); 15830Sstevel@tonic-gate goto done; 15840Sstevel@tonic-gate } 15850Sstevel@tonic-gate } 1586*11211SThomas.Haynes@Sun.COM 15870Sstevel@tonic-gate /* 15880Sstevel@tonic-gate * The mount has been triggered, re-read mnttab to pick up 15890Sstevel@tonic-gate * the changes made by autoFS. 15900Sstevel@tonic-gate */ 15910Sstevel@tonic-gate fsfreemntlist(mntl); 15920Sstevel@tonic-gate (void) mutex_lock(&mnttab_lock); 15930Sstevel@tonic-gate mntl = fsgetmntlist(); 15940Sstevel@tonic-gate (void) mutex_unlock(&mnttab_lock); 15950Sstevel@tonic-gate } 15960Sstevel@tonic-gate 15970Sstevel@tonic-gate /* 15980Sstevel@tonic-gate * The autoFS mountpoint has been triggered if necessary, 15990Sstevel@tonic-gate * now search mnttab again to determine if the requested path 16000Sstevel@tonic-gate * is an LOFS mount of a shared path. 16010Sstevel@tonic-gate */ 16020Sstevel@tonic-gate mntpnt_len = 0; 16030Sstevel@tonic-gate for (ml = mntl; ml; ml = ml->mntl_next) { 16040Sstevel@tonic-gate if (strcmp(ml->mntl_mnt->mnt_fstype, "lofs")) 16050Sstevel@tonic-gate continue; 16060Sstevel@tonic-gate 16070Sstevel@tonic-gate for (p1 = ml->mntl_mnt->mnt_mountp, p2 = rpath; 16086859Sth199096 *p1 == *p2 && *p1; p1++, p2++) 16096859Sth199096 ; 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate if (is_substring(&p1, &p2) && 16120Sstevel@tonic-gate ((tmp = strlen(ml->mntl_mnt->mnt_mountp)) >= mntpnt_len)) { 16130Sstevel@tonic-gate mntpnt_len = tmp; 16140Sstevel@tonic-gate 16150Sstevel@tonic-gate if ((strlen(ml->mntl_mnt->mnt_special) + strlen(p2)) > 16160Sstevel@tonic-gate MAXPATHLEN) { 16170Sstevel@tonic-gate if (verbose) { 16180Sstevel@tonic-gate syslog(LOG_NOTICE, "%s%s: exceeds %d", 16196859Sth199096 ml->mntl_mnt->mnt_special, p2, 16206859Sth199096 MAXPATHLEN); 16210Sstevel@tonic-gate } 16220Sstevel@tonic-gate if (retcode) 16230Sstevel@tonic-gate sharefree(retcode); 16240Sstevel@tonic-gate retcode = NULL; 16250Sstevel@tonic-gate goto done; 16260Sstevel@tonic-gate } 16270Sstevel@tonic-gate 16280Sstevel@tonic-gate (void) strcpy(tmp_path, ml->mntl_mnt->mnt_special); 16290Sstevel@tonic-gate (void) strcat(tmp_path, p2); 16300Sstevel@tonic-gate if (retcode) 16310Sstevel@tonic-gate sharefree(retcode); 16320Sstevel@tonic-gate retcode = findentry(tmp_path); 16330Sstevel@tonic-gate } 16340Sstevel@tonic-gate } 16350Sstevel@tonic-gate 16360Sstevel@tonic-gate if (retcode) { 16370Sstevel@tonic-gate assert(strlen(tmp_path) > 0); 16380Sstevel@tonic-gate (void) strcpy(rpath, tmp_path); 16390Sstevel@tonic-gate } 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate done: 16420Sstevel@tonic-gate fsfreemntlist(mntl); 16430Sstevel@tonic-gate return (retcode); 16440Sstevel@tonic-gate } 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate /* 16470Sstevel@tonic-gate * Determine whether an access list grants rights to a particular host. 16480Sstevel@tonic-gate * We match on aliases of the hostname as well as on the canonical name. 16490Sstevel@tonic-gate * Names in the access list may be either hosts or netgroups; they're 16500Sstevel@tonic-gate * not distinguished syntactically. We check for hosts first because 16510Sstevel@tonic-gate * it's cheaper (just M*N strcmp()s), then try netgroups. 16520Sstevel@tonic-gate */ 1653249Sjwahlig int 1654*11211SThomas.Haynes@Sun.COM in_access_list(SVCXPRT *transp, struct netbuf **pnb, 1655*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **pclnames, 16560Sstevel@tonic-gate char *access_list) /* N.B. we clobber this "input" parameter */ 16570Sstevel@tonic-gate { 16580Sstevel@tonic-gate int nentries; 16590Sstevel@tonic-gate char *gr; 16600Sstevel@tonic-gate char *lasts; 16610Sstevel@tonic-gate char *host; 16620Sstevel@tonic-gate int off; 16630Sstevel@tonic-gate int i; 16640Sstevel@tonic-gate int netgroup_match; 16650Sstevel@tonic-gate int response; 16660Sstevel@tonic-gate 1667*11211SThomas.Haynes@Sun.COM bool_t lookup_names; 1668*11211SThomas.Haynes@Sun.COM struct nd_hostservlist *clnames; 1669*11211SThomas.Haynes@Sun.COM 16700Sstevel@tonic-gate /* 16710Sstevel@tonic-gate * If no access list - then it's unrestricted 16720Sstevel@tonic-gate */ 16730Sstevel@tonic-gate if (access_list == NULL || *access_list == '\0') 16740Sstevel@tonic-gate return (1); 16750Sstevel@tonic-gate 1676*11211SThomas.Haynes@Sun.COM /* 1677*11211SThomas.Haynes@Sun.COM * Just get the netbuf, avoiding the costly name 1678*11211SThomas.Haynes@Sun.COM * lookup. This will suffice for access based 1679*11211SThomas.Haynes@Sun.COM * soley on addresses. 1680*11211SThomas.Haynes@Sun.COM */ 1681*11211SThomas.Haynes@Sun.COM if (*pnb == NULL) { 1682*11211SThomas.Haynes@Sun.COM lookup_names = TRUE; 1683*11211SThomas.Haynes@Sun.COM *pnb = svc_getrpccaller(transp); 1684*11211SThomas.Haynes@Sun.COM if (*pnb == NULL) 1685*11211SThomas.Haynes@Sun.COM *pclnames = anon_client(NULL); 1686*11211SThomas.Haynes@Sun.COM } else 1687*11211SThomas.Haynes@Sun.COM lookup_names = FALSE; 1688*11211SThomas.Haynes@Sun.COM 16890Sstevel@tonic-gate nentries = 0; 16900Sstevel@tonic-gate 16910Sstevel@tonic-gate for (gr = strtok_r(access_list, ":", &lasts); 16926859Sth199096 gr != NULL; gr = strtok_r(NULL, ":", &lasts)) { 16930Sstevel@tonic-gate 16940Sstevel@tonic-gate /* 16950Sstevel@tonic-gate * If the list name has a '-' prepended 16960Sstevel@tonic-gate * then a match of the following name 16970Sstevel@tonic-gate * implies failure instead of success. 16980Sstevel@tonic-gate */ 16990Sstevel@tonic-gate if (*gr == '-') { 17000Sstevel@tonic-gate response = 0; 17010Sstevel@tonic-gate gr++; 17020Sstevel@tonic-gate } else 17030Sstevel@tonic-gate response = 1; 17040Sstevel@tonic-gate 17050Sstevel@tonic-gate /* 1706*11211SThomas.Haynes@Sun.COM * If the list name begins with an at 1707*11211SThomas.Haynes@Sun.COM * sign then do a network comparison. 1708*11211SThomas.Haynes@Sun.COM */ 1709*11211SThomas.Haynes@Sun.COM if (*gr == '@') { 1710*11211SThomas.Haynes@Sun.COM if (netmatch(*pnb, gr + 1)) 1711*11211SThomas.Haynes@Sun.COM return (response); 1712*11211SThomas.Haynes@Sun.COM continue; 1713*11211SThomas.Haynes@Sun.COM } 1714*11211SThomas.Haynes@Sun.COM 1715*11211SThomas.Haynes@Sun.COM /* 1716*11211SThomas.Haynes@Sun.COM * We need to get the host name if we haven't gotten 1717*11211SThomas.Haynes@Sun.COM * it by now! 1718*11211SThomas.Haynes@Sun.COM */ 1719*11211SThomas.Haynes@Sun.COM if (lookup_names) { 1720*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_addrlist); 1721*11211SThomas.Haynes@Sun.COM getclientsnames(transp, pnb, pclnames); 1722*11211SThomas.Haynes@Sun.COM 1723*11211SThomas.Haynes@Sun.COM /* 1724*11211SThomas.Haynes@Sun.COM * Do not grant access if we can't 1725*11211SThomas.Haynes@Sun.COM * get a name! 1726*11211SThomas.Haynes@Sun.COM */ 1727*11211SThomas.Haynes@Sun.COM if (*pclnames == NULL || *pnb == NULL) 1728*11211SThomas.Haynes@Sun.COM return (0); 1729*11211SThomas.Haynes@Sun.COM 1730*11211SThomas.Haynes@Sun.COM lookup_names = FALSE; 1731*11211SThomas.Haynes@Sun.COM } 1732*11211SThomas.Haynes@Sun.COM 1733*11211SThomas.Haynes@Sun.COM clnames = *pclnames; 1734*11211SThomas.Haynes@Sun.COM 1735*11211SThomas.Haynes@Sun.COM /* 17360Sstevel@tonic-gate * The following loops through all the 17370Sstevel@tonic-gate * client's aliases. Usually it's just one name. 17380Sstevel@tonic-gate */ 17390Sstevel@tonic-gate for (i = 0; i < clnames->h_cnt; i++) { 17400Sstevel@tonic-gate host = clnames->h_hostservs[i].h_host; 17410Sstevel@tonic-gate 17420Sstevel@tonic-gate /* 17430Sstevel@tonic-gate * If the list name begins with a dot then 17440Sstevel@tonic-gate * do a domain name suffix comparison. 17450Sstevel@tonic-gate * A single dot matches any name with no 17460Sstevel@tonic-gate * suffix. 17470Sstevel@tonic-gate */ 17480Sstevel@tonic-gate if (*gr == '.') { 17490Sstevel@tonic-gate if (*(gr + 1) == '\0') { /* single dot */ 17500Sstevel@tonic-gate if (strchr(host, '.') == NULL) 17510Sstevel@tonic-gate return (response); 17520Sstevel@tonic-gate } else { 17530Sstevel@tonic-gate off = strlen(host) - strlen(gr); 17540Sstevel@tonic-gate if (off > 0 && 17550Sstevel@tonic-gate strcasecmp(host + off, gr) == 0) { 17560Sstevel@tonic-gate return (response); 17570Sstevel@tonic-gate } 17580Sstevel@tonic-gate } 17590Sstevel@tonic-gate } else 17600Sstevel@tonic-gate 17610Sstevel@tonic-gate /* 17620Sstevel@tonic-gate * Just do a hostname match 17630Sstevel@tonic-gate */ 17640Sstevel@tonic-gate if (strcasecmp(gr, host) == 0) { 17650Sstevel@tonic-gate return (response); /* Matched a hostname */ 17660Sstevel@tonic-gate } 17670Sstevel@tonic-gate } 17680Sstevel@tonic-gate 17690Sstevel@tonic-gate nentries++; 17700Sstevel@tonic-gate } 17710Sstevel@tonic-gate 1772*11211SThomas.Haynes@Sun.COM /* 1773*11211SThomas.Haynes@Sun.COM * We need to get the host name if we haven't gotten 1774*11211SThomas.Haynes@Sun.COM * it by now! 1775*11211SThomas.Haynes@Sun.COM */ 1776*11211SThomas.Haynes@Sun.COM if (lookup_names) { 1777*11211SThomas.Haynes@Sun.COM DTRACE_PROBE(mountd, name_by_netgroup); 1778*11211SThomas.Haynes@Sun.COM getclientsnames(transp, pnb, pclnames); 1779*11211SThomas.Haynes@Sun.COM 1780*11211SThomas.Haynes@Sun.COM /* 1781*11211SThomas.Haynes@Sun.COM * Do not grant access if we can't 1782*11211SThomas.Haynes@Sun.COM * get a name! 1783*11211SThomas.Haynes@Sun.COM */ 1784*11211SThomas.Haynes@Sun.COM if (*pclnames == NULL || *pnb == NULL) 1785*11211SThomas.Haynes@Sun.COM return (0); 1786*11211SThomas.Haynes@Sun.COM 1787*11211SThomas.Haynes@Sun.COM lookup_names = FALSE; 1788*11211SThomas.Haynes@Sun.COM } 1789*11211SThomas.Haynes@Sun.COM 1790*11211SThomas.Haynes@Sun.COM netgroup_match = netgroup_check(*pclnames, access_list, nentries); 17910Sstevel@tonic-gate 17920Sstevel@tonic-gate return (netgroup_match); 17930Sstevel@tonic-gate } 17940Sstevel@tonic-gate 17950Sstevel@tonic-gate int 17960Sstevel@tonic-gate netmatch(struct netbuf *nb, char *name) 17970Sstevel@tonic-gate { 17980Sstevel@tonic-gate uint_t claddr; 17990Sstevel@tonic-gate struct netent n, *np; 18000Sstevel@tonic-gate char *mp, *p; 18010Sstevel@tonic-gate uint_t addr, mask; 18020Sstevel@tonic-gate int i, bits; 18030Sstevel@tonic-gate char buff[256]; 18040Sstevel@tonic-gate 18050Sstevel@tonic-gate /* 18060Sstevel@tonic-gate * Check if it's an IPv4 addr 18070Sstevel@tonic-gate */ 18080Sstevel@tonic-gate if (nb->len != sizeof (struct sockaddr_in)) 18090Sstevel@tonic-gate return (0); 18100Sstevel@tonic-gate 18110Sstevel@tonic-gate (void) memcpy(&claddr, 18126859Sth199096 /* LINTED pointer alignment */ 18136859Sth199096 &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr, 18146859Sth199096 sizeof (struct in_addr)); 18150Sstevel@tonic-gate claddr = ntohl(claddr); 18160Sstevel@tonic-gate 18170Sstevel@tonic-gate mp = strchr(name, '/'); 18180Sstevel@tonic-gate if (mp) 18190Sstevel@tonic-gate *mp++ = '\0'; 18200Sstevel@tonic-gate 18210Sstevel@tonic-gate if (isdigit(*name)) { 18220Sstevel@tonic-gate /* 18230Sstevel@tonic-gate * Convert a dotted IP address 18240Sstevel@tonic-gate * to an IP address. The conversion 18250Sstevel@tonic-gate * is not the same as that in inet_addr(). 18260Sstevel@tonic-gate */ 18270Sstevel@tonic-gate p = name; 18280Sstevel@tonic-gate addr = 0; 18290Sstevel@tonic-gate for (i = 0; i < 4; i++) { 18300Sstevel@tonic-gate addr |= atoi(p) << ((3-i) * 8); 18310Sstevel@tonic-gate p = strchr(p, '.'); 18320Sstevel@tonic-gate if (p == NULL) 18330Sstevel@tonic-gate break; 18340Sstevel@tonic-gate p++; 18350Sstevel@tonic-gate } 18360Sstevel@tonic-gate } else { 18370Sstevel@tonic-gate /* 18380Sstevel@tonic-gate * Turn the netname into 18390Sstevel@tonic-gate * an IP address. 18400Sstevel@tonic-gate */ 18410Sstevel@tonic-gate np = getnetbyname_r(name, &n, buff, sizeof (buff)); 18420Sstevel@tonic-gate if (np == NULL) { 18430Sstevel@tonic-gate syslog(LOG_DEBUG, "getnetbyname_r: %s: %m", name); 18440Sstevel@tonic-gate return (0); 18450Sstevel@tonic-gate } 18460Sstevel@tonic-gate addr = np->n_net; 18470Sstevel@tonic-gate } 18480Sstevel@tonic-gate 18490Sstevel@tonic-gate /* 18500Sstevel@tonic-gate * If the mask is specified explicitly then 18510Sstevel@tonic-gate * use that value, e.g. 18520Sstevel@tonic-gate * 18530Sstevel@tonic-gate * @109.104.56/28 18540Sstevel@tonic-gate * 18550Sstevel@tonic-gate * otherwise assume a mask from the zero octets 18560Sstevel@tonic-gate * in the least significant bits of the address, e.g. 18570Sstevel@tonic-gate * 18580Sstevel@tonic-gate * @109.104 or @109.104.0.0 18590Sstevel@tonic-gate */ 18600Sstevel@tonic-gate if (mp) { 18610Sstevel@tonic-gate bits = atoi(mp); 18620Sstevel@tonic-gate mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits) 18636859Sth199096 : 0; 18640Sstevel@tonic-gate addr &= mask; 18650Sstevel@tonic-gate } else { 18667997SThomas.Haynes@Sun.COM if ((addr & IN_CLASSA_HOST) == 0) 18677997SThomas.Haynes@Sun.COM mask = IN_CLASSA_NET; 18687997SThomas.Haynes@Sun.COM else if ((addr & IN_CLASSB_HOST) == 0) 18697997SThomas.Haynes@Sun.COM mask = IN_CLASSB_NET; 18707997SThomas.Haynes@Sun.COM else if ((addr & IN_CLASSC_HOST) == 0) 18717997SThomas.Haynes@Sun.COM mask = IN_CLASSC_NET; 18727997SThomas.Haynes@Sun.COM else 18737997SThomas.Haynes@Sun.COM mask = IN_CLASSE_NET; 18740Sstevel@tonic-gate } 18750Sstevel@tonic-gate 18760Sstevel@tonic-gate return ((claddr & mask) == addr); 18770Sstevel@tonic-gate } 18780Sstevel@tonic-gate 18790Sstevel@tonic-gate 18800Sstevel@tonic-gate static char *optlist[] = { 18810Sstevel@tonic-gate #define OPT_RO 0 18820Sstevel@tonic-gate SHOPT_RO, 18830Sstevel@tonic-gate #define OPT_RW 1 18840Sstevel@tonic-gate SHOPT_RW, 18850Sstevel@tonic-gate #define OPT_ROOT 2 18860Sstevel@tonic-gate SHOPT_ROOT, 18870Sstevel@tonic-gate #define OPT_SECURE 3 18880Sstevel@tonic-gate SHOPT_SECURE, 18890Sstevel@tonic-gate #define OPT_ANON 4 18900Sstevel@tonic-gate SHOPT_ANON, 18910Sstevel@tonic-gate #define OPT_WINDOW 5 18920Sstevel@tonic-gate SHOPT_WINDOW, 18930Sstevel@tonic-gate #define OPT_NOSUID 6 18940Sstevel@tonic-gate SHOPT_NOSUID, 18950Sstevel@tonic-gate #define OPT_ACLOK 7 18960Sstevel@tonic-gate SHOPT_ACLOK, 18970Sstevel@tonic-gate #define OPT_SEC 8 18980Sstevel@tonic-gate SHOPT_SEC, 18997961SNatalie.Li@Sun.COM #define OPT_NONE 9 19007961SNatalie.Li@Sun.COM SHOPT_NONE, 19010Sstevel@tonic-gate NULL 19020Sstevel@tonic-gate }; 19030Sstevel@tonic-gate 19040Sstevel@tonic-gate static int 19050Sstevel@tonic-gate map_flavor(char *str) 19060Sstevel@tonic-gate { 19070Sstevel@tonic-gate seconfig_t sec; 19080Sstevel@tonic-gate 19090Sstevel@tonic-gate if (nfs_getseconfig_byname(str, &sec)) 19100Sstevel@tonic-gate return (-1); 19110Sstevel@tonic-gate 19120Sstevel@tonic-gate return (sec.sc_nfsnum); 19130Sstevel@tonic-gate } 19140Sstevel@tonic-gate 19150Sstevel@tonic-gate /* 19160Sstevel@tonic-gate * If the option string contains a "sec=" 19170Sstevel@tonic-gate * option, then use new option syntax. 19180Sstevel@tonic-gate */ 19190Sstevel@tonic-gate static int 19200Sstevel@tonic-gate newopts(char *opts) 19210Sstevel@tonic-gate { 19220Sstevel@tonic-gate char *head, *p, *val; 19230Sstevel@tonic-gate 19240Sstevel@tonic-gate if (!opts || *opts == '\0') 19250Sstevel@tonic-gate return (0); 19260Sstevel@tonic-gate 19270Sstevel@tonic-gate head = strdup(opts); 19280Sstevel@tonic-gate if (head == NULL) { 19290Sstevel@tonic-gate syslog(LOG_ERR, "opts: no memory"); 19300Sstevel@tonic-gate return (0); 19310Sstevel@tonic-gate } 19320Sstevel@tonic-gate 19330Sstevel@tonic-gate p = head; 19340Sstevel@tonic-gate while (*p) { 19350Sstevel@tonic-gate if (getsubopt(&p, optlist, &val) == OPT_SEC) { 19360Sstevel@tonic-gate free(head); 19370Sstevel@tonic-gate return (1); 19380Sstevel@tonic-gate } 19390Sstevel@tonic-gate } 19400Sstevel@tonic-gate 19410Sstevel@tonic-gate free(head); 19420Sstevel@tonic-gate return (0); 19430Sstevel@tonic-gate } 19440Sstevel@tonic-gate 19450Sstevel@tonic-gate /* 19460Sstevel@tonic-gate * Given an export and the clients hostname(s) 19470Sstevel@tonic-gate * determine the security flavors that this 19480Sstevel@tonic-gate * client is permitted to use. 19490Sstevel@tonic-gate * 19500Sstevel@tonic-gate * This routine is called only for "old" syntax, i.e. 19510Sstevel@tonic-gate * only one security flavor is allowed. So we need 19520Sstevel@tonic-gate * to determine two things: the particular flavor, 19530Sstevel@tonic-gate * and whether the client is allowed to use this 19540Sstevel@tonic-gate * flavor, i.e. is in the access list. 19550Sstevel@tonic-gate * 19560Sstevel@tonic-gate * Note that if there is no access list, then the 19570Sstevel@tonic-gate * default is that access is granted. 19580Sstevel@tonic-gate */ 19590Sstevel@tonic-gate static int 1960*11211SThomas.Haynes@Sun.COM getclientsflavors_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, 1961*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int *flavors) 19620Sstevel@tonic-gate { 19630Sstevel@tonic-gate char *opts, *p, *val; 19647961SNatalie.Li@Sun.COM boolean_t ok = B_FALSE; 19650Sstevel@tonic-gate int defaultaccess = 1; 19667961SNatalie.Li@Sun.COM boolean_t reject = B_FALSE; 19670Sstevel@tonic-gate 19680Sstevel@tonic-gate opts = strdup(sh->sh_opts); 19690Sstevel@tonic-gate if (opts == NULL) { 19700Sstevel@tonic-gate syslog(LOG_ERR, "getclientsflavors: no memory"); 19710Sstevel@tonic-gate return (0); 19720Sstevel@tonic-gate } 19730Sstevel@tonic-gate 19740Sstevel@tonic-gate flavors[0] = AUTH_SYS; 19750Sstevel@tonic-gate p = opts; 19760Sstevel@tonic-gate 19770Sstevel@tonic-gate while (*p) { 19780Sstevel@tonic-gate 19790Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) { 19800Sstevel@tonic-gate case OPT_SECURE: 19810Sstevel@tonic-gate flavors[0] = AUTH_DES; 19820Sstevel@tonic-gate break; 19830Sstevel@tonic-gate 19840Sstevel@tonic-gate case OPT_RO: 19850Sstevel@tonic-gate case OPT_RW: 19860Sstevel@tonic-gate defaultaccess = 0; 1987*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 19880Sstevel@tonic-gate ok++; 19890Sstevel@tonic-gate break; 19907961SNatalie.Li@Sun.COM 19917961SNatalie.Li@Sun.COM case OPT_NONE: 19927961SNatalie.Li@Sun.COM defaultaccess = 0; 1993*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 19947961SNatalie.Li@Sun.COM reject = B_TRUE; 19950Sstevel@tonic-gate } 19960Sstevel@tonic-gate } 19970Sstevel@tonic-gate 19980Sstevel@tonic-gate free(opts); 19990Sstevel@tonic-gate 20007961SNatalie.Li@Sun.COM /* none takes precedence over everything else */ 20017961SNatalie.Li@Sun.COM if (reject) 20027961SNatalie.Li@Sun.COM ok = B_TRUE; 20037961SNatalie.Li@Sun.COM 20040Sstevel@tonic-gate return (defaultaccess || ok); 20050Sstevel@tonic-gate } 20060Sstevel@tonic-gate 20070Sstevel@tonic-gate /* 20080Sstevel@tonic-gate * Given an export and the clients hostname(s) 20090Sstevel@tonic-gate * determine the security flavors that this 20100Sstevel@tonic-gate * client is permitted to use. 20110Sstevel@tonic-gate * 20120Sstevel@tonic-gate * This is somewhat more complicated than the "old" 20130Sstevel@tonic-gate * routine because the options may contain multiple 20140Sstevel@tonic-gate * security flavors (sec=) each with its own access 20150Sstevel@tonic-gate * lists. So a client could be granted access based 20160Sstevel@tonic-gate * on a number of security flavors. Note that the 20170Sstevel@tonic-gate * type of access might not always be the same, the 20180Sstevel@tonic-gate * client may get readonly access with one flavor 20190Sstevel@tonic-gate * and readwrite with another, however the client 20200Sstevel@tonic-gate * is not told this detail, it gets only the list 20210Sstevel@tonic-gate * of flavors, and only if the client is using 20220Sstevel@tonic-gate * version 3 of the mount protocol. 20230Sstevel@tonic-gate */ 20240Sstevel@tonic-gate static int 2025*11211SThomas.Haynes@Sun.COM getclientsflavors_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, 2026*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int *flavors) 20270Sstevel@tonic-gate { 20280Sstevel@tonic-gate char *opts, *p, *val; 20290Sstevel@tonic-gate char *lasts; 20300Sstevel@tonic-gate char *f; 20317961SNatalie.Li@Sun.COM boolean_t access_ok; 20327961SNatalie.Li@Sun.COM int count, c, perm; 20337961SNatalie.Li@Sun.COM boolean_t reject = B_FALSE; 20340Sstevel@tonic-gate 20350Sstevel@tonic-gate opts = strdup(sh->sh_opts); 20360Sstevel@tonic-gate if (opts == NULL) { 20370Sstevel@tonic-gate syslog(LOG_ERR, "getclientsflavors: no memory"); 20380Sstevel@tonic-gate return (0); 20390Sstevel@tonic-gate } 20400Sstevel@tonic-gate 20410Sstevel@tonic-gate p = opts; 20427961SNatalie.Li@Sun.COM perm = count = c = 0; 20430Sstevel@tonic-gate /* default access is rw */ 20447961SNatalie.Li@Sun.COM access_ok = B_TRUE; 20450Sstevel@tonic-gate 20460Sstevel@tonic-gate while (*p) { 20470Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) { 20480Sstevel@tonic-gate case OPT_SEC: 20490Sstevel@tonic-gate /* 20500Sstevel@tonic-gate * Before a new sec=xxx option, check if we need 20510Sstevel@tonic-gate * to move the c index back to the previous count. 20520Sstevel@tonic-gate */ 20530Sstevel@tonic-gate if (!access_ok) { 20540Sstevel@tonic-gate c = count; 20550Sstevel@tonic-gate } 20560Sstevel@tonic-gate 20570Sstevel@tonic-gate /* get all the sec=f1[:f2] flavors */ 20580Sstevel@tonic-gate while ((f = strtok_r(val, ":", &lasts)) 20596859Sth199096 != NULL) { 20600Sstevel@tonic-gate flavors[c++] = map_flavor(f); 20610Sstevel@tonic-gate val = NULL; 20620Sstevel@tonic-gate } 2063*11211SThomas.Haynes@Sun.COM 20640Sstevel@tonic-gate /* for a new sec=xxx option, default is rw access */ 20657961SNatalie.Li@Sun.COM access_ok = B_TRUE; 20660Sstevel@tonic-gate break; 20670Sstevel@tonic-gate 20680Sstevel@tonic-gate case OPT_RO: 20690Sstevel@tonic-gate case OPT_RW: 2070*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) { 20710Sstevel@tonic-gate count = c; 20727961SNatalie.Li@Sun.COM access_ok = B_TRUE; 20730Sstevel@tonic-gate } else { 20747961SNatalie.Li@Sun.COM access_ok = B_FALSE; 20750Sstevel@tonic-gate } 20760Sstevel@tonic-gate break; 20777961SNatalie.Li@Sun.COM 20787961SNatalie.Li@Sun.COM case OPT_NONE: 2079*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 20807961SNatalie.Li@Sun.COM reject = B_TRUE; /* none overides rw/ro */ 20817961SNatalie.Li@Sun.COM break; 20820Sstevel@tonic-gate } 20830Sstevel@tonic-gate } 20840Sstevel@tonic-gate 20857961SNatalie.Li@Sun.COM if (reject) 20867961SNatalie.Li@Sun.COM access_ok = B_FALSE; 20877961SNatalie.Li@Sun.COM 20887961SNatalie.Li@Sun.COM if (!access_ok) 20890Sstevel@tonic-gate c = count; 20907961SNatalie.Li@Sun.COM 20910Sstevel@tonic-gate free(opts); 20920Sstevel@tonic-gate 20930Sstevel@tonic-gate return (c); 20940Sstevel@tonic-gate } 20950Sstevel@tonic-gate 20960Sstevel@tonic-gate /* 20970Sstevel@tonic-gate * This is a tricky piece of code that parses the 20980Sstevel@tonic-gate * share options looking for a match on the auth 20990Sstevel@tonic-gate * flavor that the client is using. If it finds 21000Sstevel@tonic-gate * a match, then the client is given ro, rw, or 21010Sstevel@tonic-gate * no access depending whether it is in the access 21020Sstevel@tonic-gate * list. There is a special case for "secure" 21030Sstevel@tonic-gate * flavor. Other flavors are values of the new "sec=" option. 21040Sstevel@tonic-gate */ 21050Sstevel@tonic-gate int 2106*11211SThomas.Haynes@Sun.COM check_client(share_t *sh, struct netbuf *nb, 21070Sstevel@tonic-gate struct nd_hostservlist *clnames, int flavor) 21080Sstevel@tonic-gate { 21090Sstevel@tonic-gate if (newopts(sh->sh_opts)) 2110*11211SThomas.Haynes@Sun.COM return (check_client_new(sh, NULL, &nb, &clnames, flavor)); 21110Sstevel@tonic-gate else 2112*11211SThomas.Haynes@Sun.COM return (check_client_old(sh, NULL, &nb, &clnames, flavor)); 21130Sstevel@tonic-gate } 21140Sstevel@tonic-gate 21150Sstevel@tonic-gate static int 2116*11211SThomas.Haynes@Sun.COM check_client_old(share_t *sh, SVCXPRT *transp, struct netbuf **nb, 2117*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int flavor) 21180Sstevel@tonic-gate { 21190Sstevel@tonic-gate char *opts, *p, *val; 21200Sstevel@tonic-gate int match; /* Set when a flavor is matched */ 21210Sstevel@tonic-gate int perm = 0; /* Set when "ro", "rw" or "root" is matched */ 21220Sstevel@tonic-gate int list = 0; /* Set when "ro", "rw" is found */ 21230Sstevel@tonic-gate int ro_val = 0; /* Set if ro option is 'ro=' */ 21240Sstevel@tonic-gate int rw_val = 0; /* Set if rw option is 'rw=' */ 21257961SNatalie.Li@Sun.COM boolean_t reject = B_FALSE; /* if none= contains the host */ 21260Sstevel@tonic-gate 21270Sstevel@tonic-gate opts = strdup(sh->sh_opts); 21280Sstevel@tonic-gate if (opts == NULL) { 21290Sstevel@tonic-gate syslog(LOG_ERR, "check_client: no memory"); 21300Sstevel@tonic-gate return (0); 21310Sstevel@tonic-gate } 21320Sstevel@tonic-gate 21330Sstevel@tonic-gate p = opts; 21340Sstevel@tonic-gate match = AUTH_UNIX; 21350Sstevel@tonic-gate 21360Sstevel@tonic-gate while (*p) { 21370Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) { 21380Sstevel@tonic-gate 21390Sstevel@tonic-gate case OPT_SECURE: 21400Sstevel@tonic-gate match = AUTH_DES; 21410Sstevel@tonic-gate break; 21420Sstevel@tonic-gate 21430Sstevel@tonic-gate case OPT_RO: 21440Sstevel@tonic-gate list++; 21450Sstevel@tonic-gate if (val) ro_val++; 2146*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 21470Sstevel@tonic-gate perm |= NFSAUTH_RO; 21480Sstevel@tonic-gate break; 21490Sstevel@tonic-gate 21500Sstevel@tonic-gate case OPT_RW: 21510Sstevel@tonic-gate list++; 21520Sstevel@tonic-gate if (val) rw_val++; 2153*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 21540Sstevel@tonic-gate perm |= NFSAUTH_RW; 21550Sstevel@tonic-gate break; 21560Sstevel@tonic-gate 21570Sstevel@tonic-gate case OPT_ROOT: 21580Sstevel@tonic-gate /* 21590Sstevel@tonic-gate * Check if the client is in 21600Sstevel@tonic-gate * the root list. Only valid 21610Sstevel@tonic-gate * for AUTH_SYS. 21620Sstevel@tonic-gate */ 21630Sstevel@tonic-gate if (flavor != AUTH_SYS) 21640Sstevel@tonic-gate break; 21650Sstevel@tonic-gate 21660Sstevel@tonic-gate if (val == NULL || *val == '\0') 21670Sstevel@tonic-gate break; 21680Sstevel@tonic-gate 2169*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 21700Sstevel@tonic-gate perm |= NFSAUTH_ROOT; 21710Sstevel@tonic-gate break; 21727961SNatalie.Li@Sun.COM 21737961SNatalie.Li@Sun.COM case OPT_NONE: 21747961SNatalie.Li@Sun.COM /* 21757961SNatalie.Li@Sun.COM * Check if the client should have no access 21767961SNatalie.Li@Sun.COM * to this share at all. This option behaves 21777961SNatalie.Li@Sun.COM * more like "root" than either "rw" or "ro". 21787961SNatalie.Li@Sun.COM */ 2179*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 21807961SNatalie.Li@Sun.COM reject = B_TRUE; 21817961SNatalie.Li@Sun.COM break; 21820Sstevel@tonic-gate } 21830Sstevel@tonic-gate } 21840Sstevel@tonic-gate 21850Sstevel@tonic-gate free(opts); 21860Sstevel@tonic-gate 21877961SNatalie.Li@Sun.COM if (flavor != match || reject) 21880Sstevel@tonic-gate return (NFSAUTH_DENIED); 21890Sstevel@tonic-gate 21900Sstevel@tonic-gate if (list) { 21910Sstevel@tonic-gate /* 21920Sstevel@tonic-gate * If the client doesn't match an "ro" or "rw" 21930Sstevel@tonic-gate * list then set no access. 21940Sstevel@tonic-gate */ 21950Sstevel@tonic-gate if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) 21960Sstevel@tonic-gate perm |= NFSAUTH_DENIED; 21970Sstevel@tonic-gate } else { 21980Sstevel@tonic-gate /* 21990Sstevel@tonic-gate * The client matched a flavor entry that 22000Sstevel@tonic-gate * has no explicit "rw" or "ro" determination. 22010Sstevel@tonic-gate * Default it to "rw". 22020Sstevel@tonic-gate */ 22030Sstevel@tonic-gate perm |= NFSAUTH_RW; 22040Sstevel@tonic-gate } 22050Sstevel@tonic-gate 22060Sstevel@tonic-gate 22070Sstevel@tonic-gate /* 22080Sstevel@tonic-gate * The client may show up in both ro= and rw= 22090Sstevel@tonic-gate * lists. If so, then turn off the RO access 22100Sstevel@tonic-gate * bit leaving RW access. 22110Sstevel@tonic-gate */ 22120Sstevel@tonic-gate if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) { 22130Sstevel@tonic-gate /* 22140Sstevel@tonic-gate * Logically cover all permutations of rw=,ro=. 22150Sstevel@tonic-gate * In the case where, rw,ro=<host> we would like 22160Sstevel@tonic-gate * to remove RW access for the host. In all other cases 22170Sstevel@tonic-gate * RW wins the precedence battle. 22180Sstevel@tonic-gate */ 22190Sstevel@tonic-gate if (!rw_val && ro_val) { 22200Sstevel@tonic-gate perm &= ~(NFSAUTH_RW); 22210Sstevel@tonic-gate } else { 22220Sstevel@tonic-gate perm &= ~(NFSAUTH_RO); 22230Sstevel@tonic-gate } 22240Sstevel@tonic-gate } 22250Sstevel@tonic-gate 22260Sstevel@tonic-gate return (perm); 22270Sstevel@tonic-gate } 22280Sstevel@tonic-gate 22290Sstevel@tonic-gate /* 22300Sstevel@tonic-gate * Check if the client has access by using a flavor different from 22310Sstevel@tonic-gate * the given "flavor". If "flavor" is not in the flavor list, 22320Sstevel@tonic-gate * return TRUE to indicate that this "flavor" is a wrong sec. 22330Sstevel@tonic-gate */ 22340Sstevel@tonic-gate static bool_t 2235*11211SThomas.Haynes@Sun.COM is_wrongsec(share_t *sh, SVCXPRT *transp, struct netbuf **nb, 2236*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int flavor) 22370Sstevel@tonic-gate { 22380Sstevel@tonic-gate int flavor_list[MAX_FLAVORS]; 22390Sstevel@tonic-gate int flavor_count, i; 22400Sstevel@tonic-gate 22410Sstevel@tonic-gate /* get the flavor list that the client has access with */ 2242*11211SThomas.Haynes@Sun.COM flavor_count = getclientsflavors_new(sh, transp, nb, 2243*11211SThomas.Haynes@Sun.COM clnames, flavor_list); 22440Sstevel@tonic-gate 22450Sstevel@tonic-gate if (flavor_count == 0) 22460Sstevel@tonic-gate return (FALSE); 22470Sstevel@tonic-gate 22480Sstevel@tonic-gate /* 22490Sstevel@tonic-gate * Check if the given "flavor" is in the flavor_list. 22500Sstevel@tonic-gate */ 22510Sstevel@tonic-gate for (i = 0; i < flavor_count; i++) { 22520Sstevel@tonic-gate if (flavor == flavor_list[i]) 22530Sstevel@tonic-gate return (FALSE); 22540Sstevel@tonic-gate } 22550Sstevel@tonic-gate 22560Sstevel@tonic-gate /* 22570Sstevel@tonic-gate * If "flavor" is not in the flavor_list, return TRUE to indicate 22580Sstevel@tonic-gate * that the client should have access by using a security flavor 22590Sstevel@tonic-gate * different from this "flavor". 22600Sstevel@tonic-gate */ 22610Sstevel@tonic-gate return (TRUE); 22620Sstevel@tonic-gate } 22630Sstevel@tonic-gate 22640Sstevel@tonic-gate /* 22650Sstevel@tonic-gate * Given an export and the client's hostname, we 22660Sstevel@tonic-gate * check the security options to see whether the 22670Sstevel@tonic-gate * client is allowed to use the given security flavor. 22680Sstevel@tonic-gate * 22690Sstevel@tonic-gate * The strategy is to proceed through the options looking 22700Sstevel@tonic-gate * for a flavor match, then pay attention to the ro, rw, 22710Sstevel@tonic-gate * and root options. 22720Sstevel@tonic-gate * 22730Sstevel@tonic-gate * Note that an entry may list several flavors in a 22740Sstevel@tonic-gate * single entry, e.g. 22750Sstevel@tonic-gate * 22760Sstevel@tonic-gate * sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro 22770Sstevel@tonic-gate * 22780Sstevel@tonic-gate */ 22790Sstevel@tonic-gate 22800Sstevel@tonic-gate static int 2281*11211SThomas.Haynes@Sun.COM check_client_new(share_t *sh, SVCXPRT *transp, struct netbuf **nb, 2282*11211SThomas.Haynes@Sun.COM struct nd_hostservlist **clnames, int flavor) 22830Sstevel@tonic-gate { 22840Sstevel@tonic-gate char *opts, *p, *val; 22850Sstevel@tonic-gate char *lasts; 22860Sstevel@tonic-gate char *f; 22870Sstevel@tonic-gate int match = 0; /* Set when a flavor is matched */ 22880Sstevel@tonic-gate int perm = 0; /* Set when "ro", "rw" or "root" is matched */ 22890Sstevel@tonic-gate int list = 0; /* Set when "ro", "rw" is found */ 22900Sstevel@tonic-gate int ro_val = 0; /* Set if ro option is 'ro=' */ 22910Sstevel@tonic-gate int rw_val = 0; /* Set if rw option is 'rw=' */ 22927961SNatalie.Li@Sun.COM boolean_t reject; 22930Sstevel@tonic-gate 22940Sstevel@tonic-gate opts = strdup(sh->sh_opts); 22950Sstevel@tonic-gate if (opts == NULL) { 22960Sstevel@tonic-gate syslog(LOG_ERR, "check_client: no memory"); 22970Sstevel@tonic-gate return (0); 22980Sstevel@tonic-gate } 22990Sstevel@tonic-gate 23000Sstevel@tonic-gate p = opts; 23010Sstevel@tonic-gate 23020Sstevel@tonic-gate while (*p) { 23030Sstevel@tonic-gate switch (getsubopt(&p, optlist, &val)) { 23040Sstevel@tonic-gate 23050Sstevel@tonic-gate case OPT_SEC: 23060Sstevel@tonic-gate if (match) 23070Sstevel@tonic-gate goto done; 23080Sstevel@tonic-gate 23090Sstevel@tonic-gate while ((f = strtok_r(val, ":", &lasts)) 23106859Sth199096 != NULL) { 23110Sstevel@tonic-gate if (flavor == map_flavor(f)) { 23120Sstevel@tonic-gate match = 1; 23130Sstevel@tonic-gate break; 23140Sstevel@tonic-gate } 23150Sstevel@tonic-gate val = NULL; 23160Sstevel@tonic-gate } 23170Sstevel@tonic-gate break; 23180Sstevel@tonic-gate 23190Sstevel@tonic-gate case OPT_RO: 23200Sstevel@tonic-gate if (!match) 23210Sstevel@tonic-gate break; 23220Sstevel@tonic-gate 23230Sstevel@tonic-gate list++; 23240Sstevel@tonic-gate if (val) ro_val++; 2325*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 23260Sstevel@tonic-gate perm |= NFSAUTH_RO; 23270Sstevel@tonic-gate break; 23280Sstevel@tonic-gate 23290Sstevel@tonic-gate case OPT_RW: 23300Sstevel@tonic-gate if (!match) 23310Sstevel@tonic-gate break; 23320Sstevel@tonic-gate 23330Sstevel@tonic-gate list++; 23340Sstevel@tonic-gate if (val) rw_val++; 2335*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 23360Sstevel@tonic-gate perm |= NFSAUTH_RW; 23370Sstevel@tonic-gate break; 23380Sstevel@tonic-gate 23390Sstevel@tonic-gate case OPT_ROOT: 23400Sstevel@tonic-gate /* 23410Sstevel@tonic-gate * Check if the client is in 23420Sstevel@tonic-gate * the root list. Only valid 23430Sstevel@tonic-gate * for AUTH_SYS. 23440Sstevel@tonic-gate */ 23450Sstevel@tonic-gate if (flavor != AUTH_SYS) 23460Sstevel@tonic-gate break; 23470Sstevel@tonic-gate 23480Sstevel@tonic-gate if (!match) 23490Sstevel@tonic-gate break; 23500Sstevel@tonic-gate 23510Sstevel@tonic-gate if (val == NULL || *val == '\0') 23520Sstevel@tonic-gate break; 23530Sstevel@tonic-gate 2354*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 23550Sstevel@tonic-gate perm |= NFSAUTH_ROOT; 23560Sstevel@tonic-gate break; 23577961SNatalie.Li@Sun.COM 23587961SNatalie.Li@Sun.COM case OPT_NONE: 23597961SNatalie.Li@Sun.COM /* 23607961SNatalie.Li@Sun.COM * Check if the client should have no access 23617961SNatalie.Li@Sun.COM * to this share at all. This option behaves 23627961SNatalie.Li@Sun.COM * more like "root" than either "rw" or "ro". 23637961SNatalie.Li@Sun.COM */ 2364*11211SThomas.Haynes@Sun.COM if (in_access_list(transp, nb, clnames, val)) 23657961SNatalie.Li@Sun.COM perm |= NFSAUTH_DENIED; 23667961SNatalie.Li@Sun.COM break; 23670Sstevel@tonic-gate } 23680Sstevel@tonic-gate } 23690Sstevel@tonic-gate 23700Sstevel@tonic-gate done: 23710Sstevel@tonic-gate /* 23720Sstevel@tonic-gate * If no match then set the perm accordingly 23730Sstevel@tonic-gate */ 23747961SNatalie.Li@Sun.COM if (!match || perm & NFSAUTH_DENIED) 23750Sstevel@tonic-gate return (NFSAUTH_DENIED); 23760Sstevel@tonic-gate 23770Sstevel@tonic-gate if (list) { 23780Sstevel@tonic-gate /* 23790Sstevel@tonic-gate * If the client doesn't match an "ro" or "rw" list then 23800Sstevel@tonic-gate * check if it may have access by using a different flavor. 23810Sstevel@tonic-gate * If so, return NFSAUTH_WRONGSEC. 23820Sstevel@tonic-gate * If not, return NFSAUTH_DENIED. 23830Sstevel@tonic-gate */ 23840Sstevel@tonic-gate if ((perm & (NFSAUTH_RO | NFSAUTH_RW)) == 0) { 2385*11211SThomas.Haynes@Sun.COM if (is_wrongsec(sh, transp, nb, clnames, flavor)) 23860Sstevel@tonic-gate perm |= NFSAUTH_WRONGSEC; 23870Sstevel@tonic-gate else 23880Sstevel@tonic-gate perm |= NFSAUTH_DENIED; 23890Sstevel@tonic-gate } 23900Sstevel@tonic-gate } else { 23910Sstevel@tonic-gate /* 23920Sstevel@tonic-gate * The client matched a flavor entry that 23930Sstevel@tonic-gate * has no explicit "rw" or "ro" determination. 23940Sstevel@tonic-gate * Make sure it defaults to "rw". 23950Sstevel@tonic-gate */ 23960Sstevel@tonic-gate perm |= NFSAUTH_RW; 23970Sstevel@tonic-gate } 23980Sstevel@tonic-gate 23990Sstevel@tonic-gate /* 24000Sstevel@tonic-gate * The client may show up in both ro= and rw= 24010Sstevel@tonic-gate * lists. If so, then turn off the RO access 24020Sstevel@tonic-gate * bit leaving RW access. 24030Sstevel@tonic-gate */ 24040Sstevel@tonic-gate if (perm & NFSAUTH_RO && perm & NFSAUTH_RW) { 24050Sstevel@tonic-gate /* 24060Sstevel@tonic-gate * Logically cover all permutations of rw=,ro=. 24070Sstevel@tonic-gate * In the case where, rw,ro=<host> we would like 24080Sstevel@tonic-gate * to remove RW access for the host. In all other cases 24090Sstevel@tonic-gate * RW wins the precedence battle. 24100Sstevel@tonic-gate */ 24110Sstevel@tonic-gate if (!rw_val && ro_val) { 24120Sstevel@tonic-gate perm &= ~(NFSAUTH_RW); 24130Sstevel@tonic-gate } else { 24140Sstevel@tonic-gate perm &= ~(NFSAUTH_RO); 24150Sstevel@tonic-gate } 24160Sstevel@tonic-gate } 24170Sstevel@tonic-gate 24180Sstevel@tonic-gate free(opts); 24190Sstevel@tonic-gate 24200Sstevel@tonic-gate return (perm); 24210Sstevel@tonic-gate } 24220Sstevel@tonic-gate 24230Sstevel@tonic-gate void 24240Sstevel@tonic-gate check_sharetab() 24250Sstevel@tonic-gate { 24260Sstevel@tonic-gate FILE *f; 24270Sstevel@tonic-gate struct stat st; 24280Sstevel@tonic-gate static timestruc_t last_sharetab_time; 24290Sstevel@tonic-gate timestruc_t prev_sharetab_time; 2430*11211SThomas.Haynes@Sun.COM share_t *sh; 24310Sstevel@tonic-gate struct sh_list *shp, *shp_prev; 24320Sstevel@tonic-gate int res, c = 0; 24330Sstevel@tonic-gate 24340Sstevel@tonic-gate /* 24350Sstevel@tonic-gate * read in /etc/dfs/sharetab if it has changed 24360Sstevel@tonic-gate */ 24370Sstevel@tonic-gate if (stat(SHARETAB, &st) != 0) { 24380Sstevel@tonic-gate syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB); 24390Sstevel@tonic-gate return; 24400Sstevel@tonic-gate } 24410Sstevel@tonic-gate 24420Sstevel@tonic-gate if (st.st_mtim.tv_sec == last_sharetab_time.tv_sec && 24430Sstevel@tonic-gate st.st_mtim.tv_nsec == last_sharetab_time.tv_nsec) { 24440Sstevel@tonic-gate /* 24450Sstevel@tonic-gate * No change. 24460Sstevel@tonic-gate */ 24470Sstevel@tonic-gate return; 24480Sstevel@tonic-gate } 24490Sstevel@tonic-gate 24500Sstevel@tonic-gate /* 24510Sstevel@tonic-gate * Remember the mod time, then after getting the 24520Sstevel@tonic-gate * write lock check again. If another thread 24530Sstevel@tonic-gate * already did the update, then there's no 24540Sstevel@tonic-gate * work to do. 24550Sstevel@tonic-gate */ 24560Sstevel@tonic-gate prev_sharetab_time = last_sharetab_time; 24570Sstevel@tonic-gate 24580Sstevel@tonic-gate (void) rw_wrlock(&sharetab_lock); 24590Sstevel@tonic-gate 24600Sstevel@tonic-gate if (prev_sharetab_time.tv_sec != last_sharetab_time.tv_sec || 24610Sstevel@tonic-gate prev_sharetab_time.tv_nsec != last_sharetab_time.tv_nsec) { 24620Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock); 24630Sstevel@tonic-gate return; 24640Sstevel@tonic-gate } 24650Sstevel@tonic-gate 24663957Sth199096 /* 24673957Sth199096 * Note that since the sharetab is now in memory 24683957Sth199096 * and a snapshot is taken, we no longer have to 24693957Sth199096 * lock the file. 24703957Sth199096 */ 24713957Sth199096 f = fopen(SHARETAB, "r"); 24720Sstevel@tonic-gate if (f == NULL) { 24730Sstevel@tonic-gate syslog(LOG_ERR, "Cannot open %s: %m", SHARETAB); 24740Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock); 24750Sstevel@tonic-gate return; 24760Sstevel@tonic-gate } 24770Sstevel@tonic-gate 24780Sstevel@tonic-gate /* 24790Sstevel@tonic-gate * Once we are sure /etc/dfs/sharetab has been 24800Sstevel@tonic-gate * modified, flush netgroup cache entries. 24810Sstevel@tonic-gate */ 24820Sstevel@tonic-gate netgrp_cache_flush(); 24830Sstevel@tonic-gate 24840Sstevel@tonic-gate sh_free(share_list); /* free old list */ 24850Sstevel@tonic-gate share_list = NULL; 24860Sstevel@tonic-gate 24870Sstevel@tonic-gate while ((res = getshare(f, &sh)) > 0) { 24880Sstevel@tonic-gate c++; 24890Sstevel@tonic-gate if (strcmp(sh->sh_fstype, "nfs") != 0) 24900Sstevel@tonic-gate continue; 24910Sstevel@tonic-gate 24920Sstevel@tonic-gate shp = malloc(sizeof (*shp)); 24930Sstevel@tonic-gate if (shp == NULL) 24940Sstevel@tonic-gate goto alloc_failed; 24950Sstevel@tonic-gate if (share_list == NULL) 24960Sstevel@tonic-gate share_list = shp; 24970Sstevel@tonic-gate else 24980Sstevel@tonic-gate /* LINTED not used before set */ 24990Sstevel@tonic-gate shp_prev->shl_next = shp; 25000Sstevel@tonic-gate shp_prev = shp; 25010Sstevel@tonic-gate shp->shl_next = NULL; 25020Sstevel@tonic-gate shp->shl_sh = sharedup(sh); 25030Sstevel@tonic-gate if (shp->shl_sh == NULL) 25040Sstevel@tonic-gate goto alloc_failed; 25050Sstevel@tonic-gate } 2506*11211SThomas.Haynes@Sun.COM 25070Sstevel@tonic-gate if (res < 0) 25080Sstevel@tonic-gate syslog(LOG_ERR, "%s: invalid at line %d\n", 25096859Sth199096 SHARETAB, c + 1); 25100Sstevel@tonic-gate 25110Sstevel@tonic-gate if (stat(SHARETAB, &st) != 0) { 25120Sstevel@tonic-gate syslog(LOG_ERR, "Cannot stat %s: %m", SHARETAB); 25133701Sth199096 (void) fclose(f); 25140Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock); 25150Sstevel@tonic-gate return; 25160Sstevel@tonic-gate } 2517*11211SThomas.Haynes@Sun.COM 25180Sstevel@tonic-gate last_sharetab_time = st.st_mtim; 25190Sstevel@tonic-gate (void) fclose(f); 25200Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock); 2521*11211SThomas.Haynes@Sun.COM 25220Sstevel@tonic-gate return; 25230Sstevel@tonic-gate 25240Sstevel@tonic-gate alloc_failed: 2525*11211SThomas.Haynes@Sun.COM 25260Sstevel@tonic-gate syslog(LOG_ERR, "check_sharetab: no memory"); 25270Sstevel@tonic-gate sh_free(share_list); 25280Sstevel@tonic-gate share_list = NULL; 25290Sstevel@tonic-gate (void) fclose(f); 25300Sstevel@tonic-gate (void) rw_unlock(&sharetab_lock); 25310Sstevel@tonic-gate } 25320Sstevel@tonic-gate 25330Sstevel@tonic-gate static void 25340Sstevel@tonic-gate sh_free(struct sh_list *shp) 25350Sstevel@tonic-gate { 25360Sstevel@tonic-gate register struct sh_list *next; 25370Sstevel@tonic-gate 25380Sstevel@tonic-gate while (shp) { 25390Sstevel@tonic-gate sharefree(shp->shl_sh); 25400Sstevel@tonic-gate next = shp->shl_next; 25410Sstevel@tonic-gate free(shp); 25420Sstevel@tonic-gate shp = next; 25430Sstevel@tonic-gate } 25440Sstevel@tonic-gate } 25450Sstevel@tonic-gate 25460Sstevel@tonic-gate 25470Sstevel@tonic-gate /* 25480Sstevel@tonic-gate * Remove an entry from mounted list 25490Sstevel@tonic-gate */ 25500Sstevel@tonic-gate static void 25510Sstevel@tonic-gate umount(struct svc_req *rqstp) 25520Sstevel@tonic-gate { 25530Sstevel@tonic-gate char *host, *path, *remove_path; 25540Sstevel@tonic-gate char rpath[MAXPATHLEN]; 25550Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL; 25560Sstevel@tonic-gate SVCXPRT *transp; 25570Sstevel@tonic-gate struct netbuf *nb; 25580Sstevel@tonic-gate 25590Sstevel@tonic-gate transp = rqstp->rq_xprt; 25600Sstevel@tonic-gate path = NULL; 25610Sstevel@tonic-gate if (!svc_getargs(transp, xdr_dirpath, (caddr_t)&path)) { 25620Sstevel@tonic-gate svcerr_decode(transp); 25630Sstevel@tonic-gate return; 25640Sstevel@tonic-gate } 25650Sstevel@tonic-gate errno = 0; 25660Sstevel@tonic-gate if (!svc_sendreply(transp, xdr_void, (char *)NULL)) 25670Sstevel@tonic-gate log_cant_reply(transp); 25680Sstevel@tonic-gate 25690Sstevel@tonic-gate getclientsnames(transp, &nb, &clnames); 25700Sstevel@tonic-gate if (clnames == NULL) { 25710Sstevel@tonic-gate /* 25720Sstevel@tonic-gate * Without the hostname we can't do audit or delete 25730Sstevel@tonic-gate * this host from the mount entries. 25740Sstevel@tonic-gate */ 25750Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); 25760Sstevel@tonic-gate return; 25770Sstevel@tonic-gate } 25780Sstevel@tonic-gate host = clnames->h_hostservs[0].h_host; 25790Sstevel@tonic-gate 25800Sstevel@tonic-gate if (verbose) 25810Sstevel@tonic-gate syslog(LOG_NOTICE, "UNMOUNT: %s unmounted %s", host, path); 25820Sstevel@tonic-gate 25830Sstevel@tonic-gate audit_mountd_umount(host, path); 25840Sstevel@tonic-gate 25850Sstevel@tonic-gate remove_path = rpath; /* assume we will use the cannonical path */ 25860Sstevel@tonic-gate if (realpath(path, rpath) == NULL) { 25870Sstevel@tonic-gate if (verbose) 25880Sstevel@tonic-gate syslog(LOG_WARNING, "UNMOUNT: realpath: %s: %m ", path); 25890Sstevel@tonic-gate remove_path = path; /* use path provided instead */ 25900Sstevel@tonic-gate } 25910Sstevel@tonic-gate 25920Sstevel@tonic-gate mntlist_delete(host, remove_path); /* remove from mount list */ 25930Sstevel@tonic-gate 25940Sstevel@tonic-gate svc_freeargs(transp, xdr_dirpath, (caddr_t)&path); 25950Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST); 25960Sstevel@tonic-gate } 25970Sstevel@tonic-gate 25980Sstevel@tonic-gate /* 25990Sstevel@tonic-gate * Remove all entries for one machine from mounted list 26000Sstevel@tonic-gate */ 26010Sstevel@tonic-gate static void 26020Sstevel@tonic-gate umountall(struct svc_req *rqstp) 26030Sstevel@tonic-gate { 26040Sstevel@tonic-gate struct nd_hostservlist *clnames = NULL; 26050Sstevel@tonic-gate SVCXPRT *transp; 26060Sstevel@tonic-gate char *host; 26070Sstevel@tonic-gate struct netbuf *nb; 26080Sstevel@tonic-gate 26090Sstevel@tonic-gate transp = rqstp->rq_xprt; 26100Sstevel@tonic-gate if (!svc_getargs(transp, xdr_void, NULL)) { 26110Sstevel@tonic-gate svcerr_decode(transp); 26120Sstevel@tonic-gate return; 26130Sstevel@tonic-gate } 26140Sstevel@tonic-gate /* 26150Sstevel@tonic-gate * We assume that this call is asynchronous and made via rpcbind 26160Sstevel@tonic-gate * callit routine. Therefore return control immediately. The error 26170Sstevel@tonic-gate * causes rpcbind to remain silent, as opposed to every machine 26180Sstevel@tonic-gate * on the net blasting the requester with a response. 26190Sstevel@tonic-gate */ 26200Sstevel@tonic-gate svcerr_systemerr(transp); 26210Sstevel@tonic-gate getclientsnames(transp, &nb, &clnames); 26220Sstevel@tonic-gate if (clnames == NULL) { 26230Sstevel@tonic-gate /* Can't do anything without the name of the client */ 26240Sstevel@tonic-gate return; 26250Sstevel@tonic-gate } 26260Sstevel@tonic-gate 26270Sstevel@tonic-gate host = clnames->h_hostservs[0].h_host; 26280Sstevel@tonic-gate 26290Sstevel@tonic-gate /* 26300Sstevel@tonic-gate * Remove all hosts entries from mount list 26310Sstevel@tonic-gate */ 26320Sstevel@tonic-gate mntlist_delete_all(host); 26330Sstevel@tonic-gate 26340Sstevel@tonic-gate if (verbose) 26350Sstevel@tonic-gate syslog(LOG_NOTICE, "UNMOUNTALL: from %s", host); 26360Sstevel@tonic-gate 26370Sstevel@tonic-gate netdir_free(clnames, ND_HOSTSERVLIST); 26380Sstevel@tonic-gate } 26390Sstevel@tonic-gate 26400Sstevel@tonic-gate void * 26410Sstevel@tonic-gate exmalloc(size_t size) 26420Sstevel@tonic-gate { 26430Sstevel@tonic-gate void *ret; 26440Sstevel@tonic-gate 26450Sstevel@tonic-gate if ((ret = malloc(size)) == NULL) { 26460Sstevel@tonic-gate syslog(LOG_ERR, "Out of memory"); 26470Sstevel@tonic-gate exit(1); 26480Sstevel@tonic-gate } 26490Sstevel@tonic-gate return (ret); 26500Sstevel@tonic-gate } 26510Sstevel@tonic-gate 26520Sstevel@tonic-gate static void 26530Sstevel@tonic-gate sigexit(int signum) 26540Sstevel@tonic-gate { 26550Sstevel@tonic-gate 26560Sstevel@tonic-gate if (signum == SIGHUP) 2657199Sgt29601 _exit(0); 2658199Sgt29601 _exit(1); 26590Sstevel@tonic-gate } 26604971Sjarrett 26614971Sjarrett static tsol_tpent_t * 26624971Sjarrett get_client_template(struct sockaddr *sock) 26634971Sjarrett { 26644971Sjarrett in_addr_t v4client; 26654971Sjarrett in6_addr_t v6client; 26664971Sjarrett char v4_addr[INET_ADDRSTRLEN]; 26674971Sjarrett char v6_addr[INET6_ADDRSTRLEN]; 26684971Sjarrett tsol_rhent_t *rh; 26694971Sjarrett tsol_tpent_t *tp; 26704971Sjarrett 26714971Sjarrett switch (sock->sa_family) { 26724971Sjarrett case AF_INET: 26734971Sjarrett v4client = ((struct sockaddr_in *)(void *)sock)-> 26744971Sjarrett sin_addr.s_addr; 26754971Sjarrett if (inet_ntop(AF_INET, &v4client, v4_addr, INET_ADDRSTRLEN) == 26764971Sjarrett NULL) 26774971Sjarrett return (NULL); 26784971Sjarrett rh = tsol_getrhbyaddr(v4_addr, sizeof (v4_addr), AF_INET); 26794971Sjarrett if (rh == NULL) 26804971Sjarrett return (NULL); 26814971Sjarrett tp = tsol_gettpbyname(rh->rh_template); 26824971Sjarrett tsol_freerhent(rh); 26834971Sjarrett return (tp); 26844971Sjarrett break; 26854971Sjarrett case AF_INET6: 26864971Sjarrett v6client = ((struct sockaddr_in6 *)(void *)sock)->sin6_addr; 26874971Sjarrett if (inet_ntop(AF_INET6, &v6client, v6_addr, INET6_ADDRSTRLEN) == 26884971Sjarrett NULL) 26894971Sjarrett return (NULL); 26904971Sjarrett rh = tsol_getrhbyaddr(v6_addr, sizeof (v6_addr), AF_INET6); 26914971Sjarrett if (rh == NULL) 26924971Sjarrett return (NULL); 26934971Sjarrett tp = tsol_gettpbyname(rh->rh_template); 26944971Sjarrett tsol_freerhent(rh); 26954971Sjarrett return (tp); 26964971Sjarrett break; 26974971Sjarrett default: 26984971Sjarrett return (NULL); 26994971Sjarrett } 27004971Sjarrett } 2701