mountd.c revision 109363
167754Smsmith/* 267754Smsmith * Copyright (c) 1989, 1993 377424Smsmith * The Regents of the University of California. All rights reserved. 4102550Siwasaki * 567754Smsmith * This code is derived from software contributed to Berkeley by 667754Smsmith * Herb Hasler and Rick Macklem at The University of Guelph. 767754Smsmith * 867754Smsmith * Redistribution and use in source and binary forms, with or without 967754Smsmith * modification, are permitted provided that the following conditions 1067754Smsmith * are met: 1167754Smsmith * 1. Redistributions of source code must retain the above copyright 1291116Smsmith * notice, this list of conditions and the following disclaimer. 1370243Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1467754Smsmith * notice, this list of conditions and the following disclaimer in the 1567754Smsmith * documentation and/or other materials provided with the distribution. 1667754Smsmith * 3. All advertising materials mentioning features or use of this software 1767754Smsmith * must display the following acknowledgement: 1867754Smsmith * This product includes software developed by the University of 1967754Smsmith * California, Berkeley and its contributors. 2067754Smsmith * 4. Neither the name of the University nor the names of its contributors 2167754Smsmith * may be used to endorse or promote products derived from this software 2267754Smsmith * without specific prior written permission. 2367754Smsmith * 2467754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2567754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2667754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2767754Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2867754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2967754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3067754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3167754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3267754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3367754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3467754Smsmith * SUCH DAMAGE. 3567754Smsmith */ 3667754Smsmith 3767754Smsmith#ifndef lint 3867754Smsmithstatic const char copyright[] = 3967754Smsmith"@(#) Copyright (c) 1989, 1993\n\ 4067754Smsmith The Regents of the University of California. All rights reserved.\n"; 4167754Smsmith#endif /*not lint*/ 4267754Smsmith 4367754Smsmith#if 0 4467754Smsmith#ifndef lint 4567754Smsmithstatic char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; 4667754Smsmith#endif /*not lint*/ 4767754Smsmith#endif 4867754Smsmith 4967754Smsmith#include <sys/cdefs.h> 5067754Smsmith__FBSDID("$FreeBSD: head/usr.sbin/mountd/mountd.c 109363 2003-01-16 07:27:30Z mbr $"); 5167754Smsmith 5267754Smsmith#include <sys/param.h> 5367754Smsmith#include <sys/mount.h> 5467754Smsmith#include <sys/fcntl.h> 5567754Smsmith#include <sys/stat.h> 5667754Smsmith#include <sys/syslog.h> 5767754Smsmith#include <sys/sysctl.h> 5867754Smsmith#include <sys/linker.h> 5967754Smsmith#include <sys/module.h> 6067754Smsmith 6167754Smsmith#include <rpc/rpc.h> 6267754Smsmith#include <rpc/rpc_com.h> 6367754Smsmith#include <rpc/pmap_clnt.h> 6467754Smsmith#include <rpc/pmap_prot.h> 6567754Smsmith#include <rpcsvc/mount.h> 6667754Smsmith#include <nfs/rpcv2.h> 6767754Smsmith#include <nfs/nfsproto.h> 6867754Smsmith#include <nfsserver/nfs.h> 6967754Smsmith#include <ufs/ufs/ufsmount.h> 7067754Smsmith#include <fs/msdosfs/msdosfsmount.h> 7167754Smsmith#include <fs/ntfs/ntfsmount.h> 7267754Smsmith#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */ 7367754Smsmith 7467754Smsmith#include <arpa/inet.h> 7567754Smsmith 7667754Smsmith#include <ctype.h> 7767754Smsmith#include <err.h> 7867754Smsmith#include <errno.h> 7967754Smsmith#include <grp.h> 8067754Smsmith#include <limits.h> 8167754Smsmith#include <netdb.h> 8267754Smsmith#include <pwd.h> 8367754Smsmith#include <signal.h> 8467754Smsmith#include <stdio.h> 8567754Smsmith#include <stdlib.h> 8667754Smsmith#include <string.h> 8767754Smsmith#include <unistd.h> 8867754Smsmith#include "pathnames.h" 8967754Smsmith 9067754Smsmith#ifdef DEBUG 9167754Smsmith#include <stdarg.h> 9267754Smsmith#endif 9367754Smsmith 9467754Smsmith#ifndef MOUNTDLOCK 9567754Smsmith#define MOUNTDLOCK "/var/run/mountd.lock" 9667754Smsmith#endif 9767754Smsmith 9867754Smsmith/* 9967754Smsmith * Structures for keeping the mount list and export list 10067754Smsmith */ 10167754Smsmithstruct mountlist { 10267754Smsmith struct mountlist *ml_next; 10367754Smsmith char ml_host[RPCMNT_NAMELEN+1]; 10467754Smsmith char ml_dirp[RPCMNT_PATHLEN+1]; 10567754Smsmith}; 10667754Smsmith 10767754Smsmithstruct dirlist { 10867754Smsmith struct dirlist *dp_left; 10967754Smsmith struct dirlist *dp_right; 11067754Smsmith int dp_flag; 11167754Smsmith struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 11267754Smsmith char dp_dirp[1]; /* Actually malloc'd to size of dir */ 11367754Smsmith}; 11467754Smsmith/* dp_flag bits */ 11567754Smsmith#define DP_DEFSET 0x1 11667754Smsmith#define DP_HOSTSET 0x2 11777424Smsmith 11867754Smsmithstruct exportlist { 11967754Smsmith struct exportlist *ex_next; 12067754Smsmith struct dirlist *ex_dirl; 12177424Smsmith struct dirlist *ex_defdir; 12291116Smsmith int ex_flag; 12367754Smsmith fsid_t ex_fs; 12467754Smsmith char *ex_fsdir; 125102550Siwasaki char *ex_indexfile; 12677424Smsmith}; 12799679Siwasaki/* ex_flag bits */ 12899679Siwasaki#define EX_LINKED 0x1 12999679Siwasaki 13083174Smsmithstruct netmsk { 13183174Smsmith struct sockaddr_storage nt_net; 13267754Smsmith struct sockaddr_storage nt_mask; 13367754Smsmith char *nt_name; 13483174Smsmith}; 13567754Smsmith 13683174Smsmithunion grouptypes { 13767754Smsmith struct addrinfo *gt_addrinfo; 13883174Smsmith struct netmsk gt_net; 13967754Smsmith}; 14083174Smsmith 14183174Smsmithstruct grouplist { 14267754Smsmith int gr_type; 14367754Smsmith union grouptypes gr_ptr; 14483174Smsmith struct grouplist *gr_next; 14583174Smsmith}; 14683174Smsmith/* Group types */ 14767754Smsmith#define GT_NULL 0x0 14883174Smsmith#define GT_HOST 0x1 14967754Smsmith#define GT_NET 0x2 15083174Smsmith#define GT_DEFAULT 0x3 15191116Smsmith#define GT_IGNORE 0x5 15267754Smsmith 15367754Smsmithstruct hostlist { 15483174Smsmith int ht_flag; /* Uses DP_xx bits */ 15583174Smsmith struct grouplist *ht_grp; 15683174Smsmith struct hostlist *ht_next; 15783174Smsmith}; 15883174Smsmith 15983174Smsmithstruct fhreturn { 16083174Smsmith int fhr_flag; 16183174Smsmith int fhr_vers; 16283174Smsmith nfsfh_t fhr_fh; 16383174Smsmith}; 16483174Smsmith 16583174Smsmith/* Global defs */ 16683174Smsmithchar *add_expdir(struct dirlist **, char *, int); 16767754Smsmithvoid add_dlist(struct dirlist **, struct dirlist *, 16883174Smsmith struct grouplist *, int); 16983174Smsmithvoid add_mlist(char *, char *); 17067754Smsmithint check_dirpath(char *); 17191116Smsmithint check_options(struct dirlist *); 17267754Smsmithint checkmask(struct sockaddr *sa); 17383174Smsmithint chk_host(struct dirlist *, struct sockaddr *, int *, int *); 17491116Smsmithvoid del_mlist(char *hostp, char *dirp); 17591116Smsmithstruct dirlist *dirp_search(struct dirlist *, char *); 17683174Smsmithint do_mount(struct exportlist *, struct grouplist *, int, 17783174Smsmith struct xucred *, char *, int, struct statfs *); 17883174Smsmithint do_opt(char **, char **, struct exportlist *, struct grouplist *, 17983174Smsmith int *, int *, struct xucred *); 18083174Smsmithstruct exportlist *ex_search(fsid_t *); 18183174Smsmithstruct exportlist *get_exp(void); 18283174Smsmithvoid free_dir(struct dirlist *); 18383174Smsmithvoid free_exp(struct exportlist *); 18483174Smsmithvoid free_grp(struct grouplist *); 18567754Smsmithvoid free_host(struct hostlist *); 18667754Smsmithvoid get_exportlist(void); 18767754Smsmithint get_host(char *, struct grouplist *, struct grouplist *); 18867754Smsmithstruct hostlist *get_ht(void); 18967754Smsmithint get_line(void); 19082367Smsmithvoid get_mountlist(void); 19182367Smsmithint get_net(char *, struct netmsk *, int); 19282367Smsmithvoid getexp_err(struct exportlist *, struct grouplist *); 19382367Smsmithstruct grouplist *get_grp(void); 19482367Smsmithvoid hang_dirp(struct dirlist *, struct grouplist *, 19582367Smsmith struct exportlist *, int); 19682367Smsmithvoid huphandler(int sig); 19783174Smsmithint makemask(struct sockaddr_storage *ssp, int bitlen); 19882367Smsmithvoid mntsrv(struct svc_req *, SVCXPRT *); 19982367Smsmithvoid nextfield(char **, char **); 20082367Smsmithvoid out_of_mem(void); 20182367Smsmithvoid parsecred(char *, struct xucred *); 20282367Smsmithint put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int); 20382367Smsmithvoid *sa_rawaddr(struct sockaddr *sa, int *nbytes); 20482367Smsmithint sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 20582367Smsmith struct sockaddr *samask); 20682367Smsmithint scan_tree(struct dirlist *, struct sockaddr *); 20782367Smsmithstatic void usage(void); 20892388Smsmithint xdr_dir(XDR *, char *); 20982367Smsmithint xdr_explist(XDR *, caddr_t); 21082367Smsmithint xdr_explist_brief(XDR *, caddr_t); 21182367Smsmithint xdr_fhs(XDR *, caddr_t); 21283174Smsmithint xdr_mlist(XDR *, caddr_t); 21382367Smsmithvoid terminate(int); 21482367Smsmith 21582367Smsmithstruct exportlist *exphead; 21682367Smsmithstruct mountlist *mlhead; 21782367Smsmithstruct grouplist *grphead; 21882367Smsmithchar exname[MAXPATHLEN]; 21982367Smsmithstruct xucred def_anon = { 22083174Smsmith XUCRED_VERSION, 22183174Smsmith (uid_t)-2, 22283174Smsmith 1, 22382367Smsmith { (gid_t)-2 }, 22483174Smsmith NULL 22582367Smsmith}; 22682367Smsmithint force_v2 = 0; 22782367Smsmithint resvport_only = 1; 22882367Smsmithint dir_only = 1; 22983174Smsmithint log = 0; 23083174Smsmithint got_sighup = 0; 23183174Smsmith 23282367Smsmithint opt_flags; 23382367Smsmithstatic int have_v6 = 1; 23483174Smsmith#ifdef NI_WITHSCOPEID 23582367Smsmithstatic const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID; 23682367Smsmith#else 23782367Smsmithstatic const int ninumeric = NI_NUMERICHOST; 23882367Smsmith#endif 23983174Smsmith 24082367Smsmithint mountdlockfd; 24182367Smsmith/* Bits for opt_flags above */ 24283174Smsmith#define OP_MAPROOT 0x01 24382367Smsmith#define OP_MAPALL 0x02 24482367Smsmith/* 0x4 free */ 24583174Smsmith#define OP_MASK 0x08 24683174Smsmith#define OP_NET 0x10 24783174Smsmith#define OP_ALLDIRS 0x40 24883174Smsmith#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 24999679Siwasaki#define OP_QUIET 0x100 25083174Smsmith#define OP_MASKLEN 0x200 25182367Smsmith 25282367Smsmith#ifdef DEBUG 25399679Siwasakiint debug = 1; 25482367Smsmithvoid SYSLOG(int, const char *, ...) __printflike(2, 3); 25582367Smsmith#define syslog SYSLOG 25699679Siwasaki#else 25782367Smsmithint debug = 0; 25882367Smsmith#endif 25982367Smsmith 26082367Smsmith/* 26182367Smsmith * Mountd server for NFS mount protocol as described in: 26282367Smsmith * NFS: Network File System Protocol Specification, RFC1094, Appendix A 26382367Smsmith * The optional arguments are the exports file name 26482367Smsmith * default: _PATH_EXPORTS 26582367Smsmith * and "-n" to allow nonroot mount. 26682367Smsmith */ 26783174Smsmithint 26883174Smsmithmain(argc, argv) 26983174Smsmith int argc; 27083174Smsmith char **argv; 27183174Smsmith{ 27283174Smsmith fd_set readfds; 27382367Smsmith SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp; 27482367Smsmith struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf; 27582367Smsmith int udpsock, tcpsock, udp6sock, tcp6sock; 27682367Smsmith int xcreated = 0, s; 27782367Smsmith int maxrec = RPC_MAXDATASIZE; 27883174Smsmith int one = 1; 27982367Smsmith int c; 28082367Smsmith 28182367Smsmith udp6conf = tcp6conf = NULL; 28282367Smsmith udp6sock = tcp6sock = NULL; 28392388Smsmith 28482367Smsmith /* Check that another mountd isn't already running. */ 28582367Smsmith if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) 28682367Smsmith err(1, "%s", MOUNTDLOCK); 28783174Smsmith 28882367Smsmith if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) 28982367Smsmith errx(1, "another mountd is already running. Aborting"); 29082367Smsmith s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 29182367Smsmith if (s < 0) 29282367Smsmith have_v6 = 0; 29382367Smsmith else 29482367Smsmith close(s); 29583174Smsmith if (modfind("nfsserver") < 0) { 29682367Smsmith /* Not present in kernel, try loading it */ 29782367Smsmith if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0) 29882367Smsmith errx(1, "NFS server is not available or loadable"); 29982367Smsmith } 30082367Smsmith 30182367Smsmith while ((c = getopt(argc, argv, "2dlnr")) != -1) 30282367Smsmith switch (c) { 30382367Smsmith case '2': 30482367Smsmith force_v2 = 1; 30582367Smsmith break; 30682367Smsmith case 'n': 30783174Smsmith resvport_only = 0; 30867754Smsmith break; 30983174Smsmith case 'r': 31083174Smsmith dir_only = 0; 31183174Smsmith break; 31283174Smsmith case 'd': 31383174Smsmith debug = debug ? 0 : 1; 31467754Smsmith break; 31567754Smsmith case 'l': 31667754Smsmith log = 1; 31767754Smsmith break; 31867754Smsmith default: 31967754Smsmith usage(); 32067754Smsmith }; 32167754Smsmith argc -= optind; 32267754Smsmith argv += optind; 32383174Smsmith grphead = (struct grouplist *)NULL; 32467754Smsmith exphead = (struct exportlist *)NULL; 32583174Smsmith mlhead = (struct mountlist *)NULL; 32667754Smsmith if (argc == 1) { 32767754Smsmith strncpy(exname, *argv, MAXPATHLEN-1); 32867754Smsmith exname[MAXPATHLEN-1] = '\0'; 32983174Smsmith } else 33067754Smsmith strcpy(exname, _PATH_EXPORTS); 33183174Smsmith openlog("mountd", LOG_PID, LOG_DAEMON); 33283174Smsmith if (debug) 33367754Smsmith warnx("getting export list"); 33467754Smsmith get_exportlist(); 33567754Smsmith if (debug) 33667754Smsmith warnx("getting mount list"); 33767754Smsmith get_mountlist(); 33883174Smsmith if (debug) 33967754Smsmith warnx("here we go"); 34083174Smsmith if (debug == 0) { 34183174Smsmith daemon(0, 0); 34283174Smsmith signal(SIGINT, SIG_IGN); 34383174Smsmith signal(SIGQUIT, SIG_IGN); 34483174Smsmith } 34567754Smsmith signal(SIGHUP, huphandler); 34667754Smsmith signal(SIGTERM, terminate); 34767754Smsmith { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); 34867754Smsmith if (pidfile != NULL) { 34967754Smsmith fprintf(pidfile, "%d\n", getpid()); 35067754Smsmith fclose(pidfile); 35167754Smsmith } 35267754Smsmith } 35367754Smsmith rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); 35467754Smsmith rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); 35583174Smsmith udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 35667754Smsmith tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 35783174Smsmith udpconf = getnetconfigent("udp"); 35867754Smsmith tcpconf = getnetconfigent("tcp"); 35967754Smsmith 36067754Smsmith rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 36183174Smsmith 36283174Smsmith if (!have_v6) 36383174Smsmith goto skip_v6; 36483174Smsmith udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 36567754Smsmith tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 36667754Smsmith /* 36767754Smsmith * We're doing host-based access checks here, so don't allow 36867754Smsmith * v4-in-v6 to confuse things. The kernel will disable it 36967754Smsmith * by default on NFS sockets too. 37083174Smsmith */ 37167754Smsmith if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6, 37283174Smsmith IPV6_V6ONLY, &one, sizeof one) < 0){ 37383174Smsmith syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); 37483174Smsmith exit(1); 37583174Smsmith } 37683174Smsmith if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6, 37767754Smsmith IPV6_V6ONLY, &one, sizeof one) < 0){ 37867754Smsmith syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); 37967754Smsmith exit(1); 38067754Smsmith } 38167754Smsmith udp6conf = getnetconfigent("udp6"); 38267754Smsmith tcp6conf = getnetconfigent("tcp6"); 38367754Smsmith 38467754Smsmithskip_v6: 38567754Smsmith if (!resvport_only) { 38667754Smsmith if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL, 38783174Smsmith &resvport_only, sizeof(resvport_only)) != 0 && 38867754Smsmith errno != ENOENT) { 38983174Smsmith syslog(LOG_ERR, "sysctl: %m"); 39067754Smsmith exit(1); 39167754Smsmith } 39267754Smsmith } 39367754Smsmith if (udpsock != -1 && udpconf != NULL) { 39483174Smsmith bindresvport(udpsock, NULL); 39583174Smsmith udptransp = svc_dg_create(udpsock, 0, 0); 39683174Smsmith if (udptransp != NULL) { 39783174Smsmith if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1, 39867754Smsmith mntsrv, udpconf)) 39967754Smsmith syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service"); 40067754Smsmith else 40167754Smsmith xcreated++; 40267754Smsmith if (!force_v2) { 40383174Smsmith if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3, 40467754Smsmith mntsrv, udpconf)) 40583174Smsmith syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service"); 40683174Smsmith else 40783174Smsmith xcreated++; 40883174Smsmith } 40983174Smsmith } else 41067754Smsmith syslog(LOG_WARNING, "can't create UDP services"); 41167754Smsmith 41267754Smsmith } 41367754Smsmith if (tcpsock != -1 && tcpconf != NULL) { 41467754Smsmith bindresvport(tcpsock, NULL); 41567754Smsmith listen(tcpsock, SOMAXCONN); 41667754Smsmith tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 41767754Smsmith if (tcptransp != NULL) { 41867754Smsmith if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1, 41967754Smsmith mntsrv, tcpconf)) 42083174Smsmith syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service"); 42167754Smsmith else 42283174Smsmith xcreated++; 42367754Smsmith if (!force_v2) { 42467754Smsmith if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3, 42567754Smsmith mntsrv, tcpconf)) 42667754Smsmith syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service"); 42783174Smsmith else 42883174Smsmith xcreated++; 42983174Smsmith } 43083174Smsmith } else 43167754Smsmith syslog(LOG_WARNING, "can't create TCP service"); 43267754Smsmith 43367754Smsmith } 43467754Smsmith if (have_v6 && udp6sock != -1 && udp6conf != NULL) { 43567754Smsmith bindresvport(udp6sock, NULL); 43683174Smsmith udp6transp = svc_dg_create(udp6sock, 0, 0); 43767754Smsmith if (udp6transp != NULL) { 43883174Smsmith if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1, 43983174Smsmith mntsrv, udp6conf)) 44083174Smsmith syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service"); 44183174Smsmith else 44283174Smsmith xcreated++; 44367754Smsmith if (!force_v2) { 44467754Smsmith if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3, 44567754Smsmith mntsrv, udp6conf)) 44667754Smsmith syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service"); 44767754Smsmith else 44867754Smsmith xcreated++; 44967754Smsmith } 45067754Smsmith } else 45167754Smsmith syslog(LOG_WARNING, "can't create UDP6 service"); 45283174Smsmith 45367754Smsmith } 45483174Smsmith if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) { 45567754Smsmith bindresvport(tcp6sock, NULL); 45667754Smsmith listen(tcp6sock, SOMAXCONN); 45783174Smsmith tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 45883174Smsmith if (tcp6transp != NULL) { 45967754Smsmith if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1, 46067754Smsmith mntsrv, tcp6conf)) 46167754Smsmith syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service"); 46267754Smsmith else 46367754Smsmith xcreated++; 46467754Smsmith if (!force_v2) { 46567754Smsmith if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3, 46683174Smsmith mntsrv, tcp6conf)) 46767754Smsmith syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service"); 46883174Smsmith else 46983174Smsmith xcreated++; 47083174Smsmith } 47183174Smsmith } else 47283174Smsmith syslog(LOG_WARNING, "can't create TCP6 service"); 47367754Smsmith 47467754Smsmith } 47567754Smsmith if (xcreated == 0) { 47667754Smsmith syslog(LOG_ERR, "could not create any services"); 47767754Smsmith exit(1); 47867754Smsmith } 47967754Smsmith 48067754Smsmith /* Expand svc_run() here so that we can call get_exportlist(). */ 48167754Smsmith for (;;) { 48267754Smsmith if (got_sighup) { 48383174Smsmith get_exportlist(); 48467754Smsmith got_sighup = 0; 48583174Smsmith } 48667754Smsmith readfds = svc_fdset; 48767754Smsmith switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) { 48867754Smsmith case -1: 48983174Smsmith if (errno == EINTR) 49083174Smsmith continue; 49183174Smsmith syslog(LOG_ERR, "mountd died: select: %m"); 49283174Smsmith exit(1); 49383174Smsmith case 0: 49483174Smsmith continue; 49583174Smsmith default: 49683174Smsmith svc_getreqset(&readfds); 49783174Smsmith } 49883174Smsmith } 49983174Smsmith} 50083174Smsmith 50167754Smsmithstatic void 50267754Smsmithusage() 50367754Smsmith{ 50467754Smsmith fprintf(stderr, 50567754Smsmith "usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n"); 50667754Smsmith exit(1); 50767754Smsmith} 50883174Smsmith 50967754Smsmith/* 51083174Smsmith * The mount rpc service 51183174Smsmith */ 51283174Smsmithvoid 51383174Smsmithmntsrv(rqstp, transp) 51483174Smsmith struct svc_req *rqstp; 51567754Smsmith SVCXPRT *transp; 51667754Smsmith{ 51767754Smsmith struct exportlist *ep; 51867754Smsmith struct dirlist *dp; 51967754Smsmith struct fhreturn fhr; 52067754Smsmith struct stat stb; 52167754Smsmith struct statfs fsb; 52267754Smsmith struct addrinfo *ai; 52367754Smsmith char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 52467754Smsmith int lookup_failed = 1; 52583174Smsmith struct sockaddr *saddr; 52667754Smsmith u_short sport; 52783174Smsmith char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; 52871867Smsmith int bad = 0, defset, hostset; 52967754Smsmith sigset_t sighup_mask; 53067754Smsmith 53183174Smsmith sigemptyset(&sighup_mask); 53291116Smsmith sigaddset(&sighup_mask, SIGHUP); 53391116Smsmith saddr = svc_getrpccaller(transp)->buf; 53467754Smsmith switch (saddr->sa_family) { 53567754Smsmith case AF_INET6: 53667754Smsmith sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 53767754Smsmith break; 53867754Smsmith case AF_INET: 53967754Smsmith sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 54067754Smsmith break; 54183174Smsmith default: 54267754Smsmith syslog(LOG_ERR, "request from unknown address family"); 54383174Smsmith return; 54483174Smsmith } 54583174Smsmith lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 54683174Smsmith NULL, 0, 0); 54783174Smsmith getnameinfo(saddr, saddr->sa_len, numerichost, 54867754Smsmith sizeof numerichost, NULL, 0, NI_NUMERICHOST); 54967754Smsmith ai = NULL; 55067754Smsmith switch (rqstp->rq_proc) { 55167754Smsmith case NULLPROC: 55267754Smsmith if (!svc_sendreply(transp, xdr_void, NULL)) 55367754Smsmith syslog(LOG_ERR, "can't send reply"); 55467754Smsmith return; 55567754Smsmith case RPCMNT_MOUNT: 55667754Smsmith if (sport >= IPPORT_RESERVED && resvport_only) { 55767754Smsmith syslog(LOG_NOTICE, 55883174Smsmith "mount request from %s from unprivileged port", 55967754Smsmith numerichost); 56083174Smsmith svcerr_weakauth(transp); 56167754Smsmith return; 56267754Smsmith } 56367754Smsmith if (!svc_getargs(transp, xdr_dir, rpcpath)) { 56483174Smsmith syslog(LOG_NOTICE, "undecodable mount request from %s", 56583174Smsmith numerichost); 56667754Smsmith svcerr_decode(transp); 56767754Smsmith return; 56867754Smsmith } 56967754Smsmith 57083174Smsmith /* 57167754Smsmith * Get the real pathname and make sure it is a directory 57283174Smsmith * or a regular file if the -r option was specified 57367754Smsmith * and it exists. 57467754Smsmith */ 57577424Smsmith if (realpath(rpcpath, dirpath) == NULL || 57667754Smsmith stat(dirpath, &stb) < 0 || 57767754Smsmith (!S_ISDIR(stb.st_mode) && 57867754Smsmith (dir_only || !S_ISREG(stb.st_mode))) || 57983174Smsmith statfs(dirpath, &fsb) < 0) { 58067754Smsmith chdir("/"); /* Just in case realpath doesn't */ 58167754Smsmith syslog(LOG_NOTICE, 58267754Smsmith "mount request from %s for non existent path %s", 58367754Smsmith numerichost, dirpath); 58467754Smsmith if (debug) 58567754Smsmith warnx("stat failed on %s", dirpath); 58667754Smsmith bad = ENOENT; /* We will send error reply later */ 58767754Smsmith } 58867754Smsmith 58977424Smsmith /* Check in the exports list */ 59067754Smsmith sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 59167754Smsmith ep = ex_search(&fsb.f_fsid); 59267754Smsmith hostset = defset = 0; 59367754Smsmith if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || 59467754Smsmith ((dp = dirp_search(ep->ex_dirl, dirpath)) && 59599679Siwasaki chk_host(dp, saddr, &defset, &hostset)) || 59699679Siwasaki (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 59767754Smsmith scan_tree(ep->ex_dirl, saddr) == 0))) { 59867754Smsmith if (bad) { 59967754Smsmith if (!svc_sendreply(transp, xdr_long, 60067754Smsmith (caddr_t)&bad)) 60167754Smsmith syslog(LOG_ERR, "can't send reply"); 60267754Smsmith sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 60382367Smsmith return; 60467754Smsmith } 60567754Smsmith if (hostset & DP_HOSTSET) 60667754Smsmith fhr.fhr_flag = hostset; 60767754Smsmith else 60867754Smsmith fhr.fhr_flag = defset; 60999146Siwasaki fhr.fhr_vers = rqstp->rq_vers; 61099146Siwasaki /* Get the file handle */ 61199146Siwasaki memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 61299146Siwasaki if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 61367754Smsmith bad = errno; 61499146Siwasaki syslog(LOG_ERR, "can't get fh for %s", dirpath); 61599146Siwasaki if (!svc_sendreply(transp, xdr_long, 61667754Smsmith (caddr_t)&bad)) 61767754Smsmith syslog(LOG_ERR, "can't send reply"); 61867754Smsmith sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 61967754Smsmith return; 62067754Smsmith } 62167754Smsmith if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) 62267754Smsmith syslog(LOG_ERR, "can't send reply"); 62367754Smsmith if (!lookup_failed) 62467754Smsmith add_mlist(host, dirpath); 62567754Smsmith else 62667754Smsmith add_mlist(numerichost, dirpath); 62767754Smsmith if (debug) 62867754Smsmith warnx("mount successful"); 62967754Smsmith if (log) 63067754Smsmith syslog(LOG_NOTICE, 63167754Smsmith "mount request succeeded from %s for %s", 63267754Smsmith numerichost, dirpath); 63367754Smsmith } else { 63467754Smsmith bad = EACCES; 63567754Smsmith syslog(LOG_NOTICE, 63667754Smsmith "mount request denied from %s for %s", 63767754Smsmith numerichost, dirpath); 63867754Smsmith } 63967754Smsmith 64067754Smsmith if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad)) 64167754Smsmith syslog(LOG_ERR, "can't send reply"); 64267754Smsmith sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 64367754Smsmith return; 64467754Smsmith case RPCMNT_DUMP: 64567754Smsmith if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL)) 64667754Smsmith syslog(LOG_ERR, "can't send reply"); 64767754Smsmith else if (log) 64867754Smsmith syslog(LOG_NOTICE, 64967754Smsmith "dump request succeeded from %s", 65067754Smsmith numerichost); 65191116Smsmith return; 65291116Smsmith case RPCMNT_UMOUNT: 65367754Smsmith if (sport >= IPPORT_RESERVED && resvport_only) { 65467754Smsmith syslog(LOG_NOTICE, 65567754Smsmith "umount request from %s from unprivileged port", 65667754Smsmith numerichost); 65767754Smsmith svcerr_weakauth(transp); 65867754Smsmith return; 65967754Smsmith } 66091116Smsmith if (!svc_getargs(transp, xdr_dir, rpcpath)) { 66191116Smsmith syslog(LOG_NOTICE, "undecodable umount request from %s", 66267754Smsmith numerichost); 66367754Smsmith svcerr_decode(transp); 66467754Smsmith return; 66567754Smsmith } 66667754Smsmith if (realpath(rpcpath, dirpath) == NULL) { 66767754Smsmith syslog(LOG_NOTICE, "umount request from %s " 66867754Smsmith "for non existent path %s", 66991116Smsmith numerichost, dirpath); 67091116Smsmith } 67167754Smsmith if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 67267754Smsmith syslog(LOG_ERR, "can't send reply"); 67391116Smsmith if (!lookup_failed) 67491116Smsmith del_mlist(host, dirpath); 67567754Smsmith del_mlist(numerichost, dirpath); 67667754Smsmith if (log) 67767754Smsmith syslog(LOG_NOTICE, 67867754Smsmith "umount request succeeded from %s for %s", 67967754Smsmith numerichost, dirpath); 68067754Smsmith return; 68167754Smsmith case RPCMNT_UMNTALL: 68267754Smsmith if (sport >= IPPORT_RESERVED && resvport_only) { 68367754Smsmith syslog(LOG_NOTICE, 68467754Smsmith "umountall request from %s from unprivileged port", 68567754Smsmith numerichost); 68667754Smsmith svcerr_weakauth(transp); 68767754Smsmith return; 68867754Smsmith } 68967754Smsmith if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 69067754Smsmith syslog(LOG_ERR, "can't send reply"); 69167754Smsmith if (!lookup_failed) 69267754Smsmith del_mlist(host, NULL); 69367754Smsmith del_mlist(numerichost, NULL); 69467754Smsmith if (log) 69567754Smsmith syslog(LOG_NOTICE, 69667754Smsmith "umountall request succeeded from %s", 69767754Smsmith numerichost); 69867754Smsmith return; 69967754Smsmith case RPCMNT_EXPORT: 70067754Smsmith if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) 70167754Smsmith if (!svc_sendreply(transp, xdr_explist_brief, (caddr_t)NULL)) 70267754Smsmith syslog(LOG_ERR, "can't send reply"); 70367754Smsmith if (log) 70467754Smsmith syslog(LOG_NOTICE, 70567754Smsmith "export request succeeded from %s", 70667754Smsmith numerichost); 70767754Smsmith return; 70867754Smsmith default: 70967754Smsmith svcerr_noproc(transp); 71067754Smsmith return; 71167754Smsmith } 71267754Smsmith} 71367754Smsmith 71467754Smsmith/* 715 * Xdr conversion for a dirpath string 716 */ 717int 718xdr_dir(xdrsp, dirp) 719 XDR *xdrsp; 720 char *dirp; 721{ 722 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 723} 724 725/* 726 * Xdr routine to generate file handle reply 727 */ 728int 729xdr_fhs(xdrsp, cp) 730 XDR *xdrsp; 731 caddr_t cp; 732{ 733 struct fhreturn *fhrp = (struct fhreturn *)cp; 734 u_long ok = 0, len, auth; 735 736 if (!xdr_long(xdrsp, &ok)) 737 return (0); 738 switch (fhrp->fhr_vers) { 739 case 1: 740 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 741 case 3: 742 len = NFSX_V3FH; 743 if (!xdr_long(xdrsp, &len)) 744 return (0); 745 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 746 return (0); 747 auth = RPCAUTH_UNIX; 748 len = 1; 749 if (!xdr_long(xdrsp, &len)) 750 return (0); 751 return (xdr_long(xdrsp, &auth)); 752 }; 753 return (0); 754} 755 756int 757xdr_mlist(xdrsp, cp) 758 XDR *xdrsp; 759 caddr_t cp; 760{ 761 struct mountlist *mlp; 762 int true = 1; 763 int false = 0; 764 char *strp; 765 766 mlp = mlhead; 767 while (mlp) { 768 if (!xdr_bool(xdrsp, &true)) 769 return (0); 770 strp = &mlp->ml_host[0]; 771 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 772 return (0); 773 strp = &mlp->ml_dirp[0]; 774 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 775 return (0); 776 mlp = mlp->ml_next; 777 } 778 if (!xdr_bool(xdrsp, &false)) 779 return (0); 780 return (1); 781} 782 783/* 784 * Xdr conversion for export list 785 */ 786int 787xdr_explist_common(xdrsp, cp, brief) 788 XDR *xdrsp; 789 caddr_t cp; 790 int brief; 791{ 792 struct exportlist *ep; 793 int false = 0; 794 int putdef; 795 sigset_t sighup_mask; 796 797 sigemptyset(&sighup_mask); 798 sigaddset(&sighup_mask, SIGHUP); 799 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 800 ep = exphead; 801 while (ep) { 802 putdef = 0; 803 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, 804 &putdef, brief)) 805 goto errout; 806 if (ep->ex_defdir && putdef == 0 && 807 put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, 808 &putdef, brief)) 809 goto errout; 810 ep = ep->ex_next; 811 } 812 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 813 if (!xdr_bool(xdrsp, &false)) 814 return (0); 815 return (1); 816errout: 817 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 818 return (0); 819} 820 821/* 822 * Called from xdr_explist() to traverse the tree and export the 823 * directory paths. 824 */ 825int 826put_exlist(dp, xdrsp, adp, putdefp, brief) 827 struct dirlist *dp; 828 XDR *xdrsp; 829 struct dirlist *adp; 830 int *putdefp; 831 int brief; 832{ 833 struct grouplist *grp; 834 struct hostlist *hp; 835 int true = 1; 836 int false = 0; 837 int gotalldir = 0; 838 char *strp; 839 840 if (dp) { 841 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) 842 return (1); 843 if (!xdr_bool(xdrsp, &true)) 844 return (1); 845 strp = dp->dp_dirp; 846 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 847 return (1); 848 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 849 gotalldir = 1; 850 *putdefp = 1; 851 } 852 if (brief) { 853 if (!xdr_bool(xdrsp, &true)) 854 return (1); 855 strp = "(...)"; 856 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 857 return (1); 858 } else if ((dp->dp_flag & DP_DEFSET) == 0 && 859 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 860 hp = dp->dp_hosts; 861 while (hp) { 862 grp = hp->ht_grp; 863 if (grp->gr_type == GT_HOST) { 864 if (!xdr_bool(xdrsp, &true)) 865 return (1); 866 strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 867 if (!xdr_string(xdrsp, &strp, 868 RPCMNT_NAMELEN)) 869 return (1); 870 } else if (grp->gr_type == GT_NET) { 871 if (!xdr_bool(xdrsp, &true)) 872 return (1); 873 strp = grp->gr_ptr.gt_net.nt_name; 874 if (!xdr_string(xdrsp, &strp, 875 RPCMNT_NAMELEN)) 876 return (1); 877 } 878 hp = hp->ht_next; 879 if (gotalldir && hp == (struct hostlist *)NULL) { 880 hp = adp->dp_hosts; 881 gotalldir = 0; 882 } 883 } 884 } 885 if (!xdr_bool(xdrsp, &false)) 886 return (1); 887 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) 888 return (1); 889 } 890 return (0); 891} 892 893int 894xdr_explist(xdrsp, cp) 895 XDR *xdrsp; 896 caddr_t cp; 897{ 898 899 return xdr_explist_common(xdrsp, cp, 0); 900} 901 902int 903xdr_explist_brief(xdrsp, cp) 904 XDR *xdrsp; 905 caddr_t cp; 906{ 907 908 return xdr_explist_common(xdrsp, cp, 1); 909} 910 911char *line; 912int linesize; 913FILE *exp_file; 914 915/* 916 * Get the export list 917 */ 918void 919get_exportlist() 920{ 921 struct exportlist *ep, *ep2; 922 struct grouplist *grp, *tgrp; 923 struct exportlist **epp; 924 struct dirlist *dirhead; 925 struct statfs fsb, *fsp; 926 struct xucred anon; 927 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 928 int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; 929 930 dirp = NULL; 931 dirplen = 0; 932 933 /* 934 * First, get rid of the old list 935 */ 936 ep = exphead; 937 while (ep) { 938 ep2 = ep; 939 ep = ep->ex_next; 940 free_exp(ep2); 941 } 942 exphead = (struct exportlist *)NULL; 943 944 grp = grphead; 945 while (grp) { 946 tgrp = grp; 947 grp = grp->gr_next; 948 free_grp(tgrp); 949 } 950 grphead = (struct grouplist *)NULL; 951 952 /* 953 * And delete exports that are in the kernel for all local 954 * filesystems. 955 * XXX: Should know how to handle all local exportable filesystems 956 * instead of just "ufs". 957 */ 958 num = getmntinfo(&fsp, MNT_NOWAIT); 959 for (i = 0; i < num; i++) { 960 union { 961 struct ufs_args ua; 962 struct iso_args ia; 963 struct msdosfs_args da; 964 struct ntfs_args na; 965 } targs; 966 967 if (!strcmp(fsp->f_fstypename, "ufs") || 968 !strcmp(fsp->f_fstypename, "msdosfs") || 969 !strcmp(fsp->f_fstypename, "ntfs") || 970 !strcmp(fsp->f_fstypename, "cd9660")) { 971 targs.ua.fspec = NULL; 972 targs.ua.export.ex_flags = MNT_DELEXPORT; 973 if (mount(fsp->f_fstypename, fsp->f_mntonname, 974 fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 && 975 errno != ENOENT) 976 syslog(LOG_ERR, 977 "can't delete exports for %s: %m", 978 fsp->f_mntonname); 979 } 980 fsp++; 981 } 982 983 /* 984 * Read in the exports file and build the list, calling 985 * mount() as we go along to push the export rules into the kernel. 986 */ 987 if ((exp_file = fopen(exname, "r")) == NULL) { 988 syslog(LOG_ERR, "can't open %s", exname); 989 exit(2); 990 } 991 dirhead = (struct dirlist *)NULL; 992 while (get_line()) { 993 if (debug) 994 warnx("got line %s", line); 995 cp = line; 996 nextfield(&cp, &endcp); 997 if (*cp == '#') 998 goto nextline; 999 1000 /* 1001 * Set defaults. 1002 */ 1003 has_host = FALSE; 1004 anon = def_anon; 1005 exflags = MNT_EXPORTED; 1006 got_nondir = 0; 1007 opt_flags = 0; 1008 ep = (struct exportlist *)NULL; 1009 1010 /* 1011 * Create new exports list entry 1012 */ 1013 len = endcp-cp; 1014 tgrp = grp = get_grp(); 1015 while (len > 0) { 1016 if (len > RPCMNT_NAMELEN) { 1017 getexp_err(ep, tgrp); 1018 goto nextline; 1019 } 1020 if (*cp == '-') { 1021 if (ep == (struct exportlist *)NULL) { 1022 getexp_err(ep, tgrp); 1023 goto nextline; 1024 } 1025 if (debug) 1026 warnx("doing opt %s", cp); 1027 got_nondir = 1; 1028 if (do_opt(&cp, &endcp, ep, grp, &has_host, 1029 &exflags, &anon)) { 1030 getexp_err(ep, tgrp); 1031 goto nextline; 1032 } 1033 } else if (*cp == '/') { 1034 savedc = *endcp; 1035 *endcp = '\0'; 1036 if (check_dirpath(cp) && 1037 statfs(cp, &fsb) >= 0) { 1038 if (got_nondir) { 1039 syslog(LOG_ERR, "dirs must be first"); 1040 getexp_err(ep, tgrp); 1041 goto nextline; 1042 } 1043 if (ep) { 1044 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || 1045 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { 1046 getexp_err(ep, tgrp); 1047 goto nextline; 1048 } 1049 } else { 1050 /* 1051 * See if this directory is already 1052 * in the list. 1053 */ 1054 ep = ex_search(&fsb.f_fsid); 1055 if (ep == (struct exportlist *)NULL) { 1056 ep = get_exp(); 1057 ep->ex_fs = fsb.f_fsid; 1058 ep->ex_fsdir = (char *) 1059 malloc(strlen(fsb.f_mntonname) + 1); 1060 if (ep->ex_fsdir) 1061 strcpy(ep->ex_fsdir, 1062 fsb.f_mntonname); 1063 else 1064 out_of_mem(); 1065 if (debug) 1066 warnx("making new ep fs=0x%x,0x%x", 1067 fsb.f_fsid.val[0], 1068 fsb.f_fsid.val[1]); 1069 } else if (debug) 1070 warnx("found ep fs=0x%x,0x%x", 1071 fsb.f_fsid.val[0], 1072 fsb.f_fsid.val[1]); 1073 } 1074 1075 /* 1076 * Add dirpath to export mount point. 1077 */ 1078 dirp = add_expdir(&dirhead, cp, len); 1079 dirplen = len; 1080 } else { 1081 getexp_err(ep, tgrp); 1082 goto nextline; 1083 } 1084 *endcp = savedc; 1085 } else { 1086 savedc = *endcp; 1087 *endcp = '\0'; 1088 got_nondir = 1; 1089 if (ep == (struct exportlist *)NULL) { 1090 getexp_err(ep, tgrp); 1091 goto nextline; 1092 } 1093 1094 /* 1095 * Get the host or netgroup. 1096 */ 1097 setnetgrent(cp); 1098 netgrp = getnetgrent(&hst, &usr, &dom); 1099 do { 1100 if (has_host) { 1101 grp->gr_next = get_grp(); 1102 grp = grp->gr_next; 1103 } 1104 if (netgrp) { 1105 if (hst == 0) { 1106 syslog(LOG_ERR, 1107 "null hostname in netgroup %s, skipping", cp); 1108 grp->gr_type = GT_IGNORE; 1109 } else if (get_host(hst, grp, tgrp)) { 1110 syslog(LOG_ERR, 1111 "bad host %s in netgroup %s, skipping", hst, cp); 1112 grp->gr_type = GT_IGNORE; 1113 } 1114 } else if (get_host(cp, grp, tgrp)) { 1115 syslog(LOG_ERR, "bad host %s, skipping", cp); 1116 grp->gr_type = GT_IGNORE; 1117 } 1118 has_host = TRUE; 1119 } while (netgrp && getnetgrent(&hst, &usr, &dom)); 1120 endnetgrent(); 1121 *endcp = savedc; 1122 } 1123 cp = endcp; 1124 nextfield(&cp, &endcp); 1125 len = endcp - cp; 1126 } 1127 if (check_options(dirhead)) { 1128 getexp_err(ep, tgrp); 1129 goto nextline; 1130 } 1131 if (!has_host) { 1132 grp->gr_type = GT_DEFAULT; 1133 if (debug) 1134 warnx("adding a default entry"); 1135 1136 /* 1137 * Don't allow a network export coincide with a list of 1138 * host(s) on the same line. 1139 */ 1140 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1141 syslog(LOG_ERR, "network/host conflict"); 1142 getexp_err(ep, tgrp); 1143 goto nextline; 1144 1145 /* 1146 * If an export list was specified on this line, make sure 1147 * that we have at least one valid entry, otherwise skip it. 1148 */ 1149 } else { 1150 grp = tgrp; 1151 while (grp && grp->gr_type == GT_IGNORE) 1152 grp = grp->gr_next; 1153 if (! grp) { 1154 getexp_err(ep, tgrp); 1155 goto nextline; 1156 } 1157 } 1158 1159 /* 1160 * Loop through hosts, pushing the exports into the kernel. 1161 * After loop, tgrp points to the start of the list and 1162 * grp points to the last entry in the list. 1163 */ 1164 grp = tgrp; 1165 do { 1166 if (do_mount(ep, grp, exflags, &anon, dirp, dirplen, 1167 &fsb)) { 1168 getexp_err(ep, tgrp); 1169 goto nextline; 1170 } 1171 } while (grp->gr_next && (grp = grp->gr_next)); 1172 1173 /* 1174 * Success. Update the data structures. 1175 */ 1176 if (has_host) { 1177 hang_dirp(dirhead, tgrp, ep, opt_flags); 1178 grp->gr_next = grphead; 1179 grphead = tgrp; 1180 } else { 1181 hang_dirp(dirhead, (struct grouplist *)NULL, ep, 1182 opt_flags); 1183 free_grp(grp); 1184 } 1185 dirhead = (struct dirlist *)NULL; 1186 if ((ep->ex_flag & EX_LINKED) == 0) { 1187 ep2 = exphead; 1188 epp = &exphead; 1189 1190 /* 1191 * Insert in the list in alphabetical order. 1192 */ 1193 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { 1194 epp = &ep2->ex_next; 1195 ep2 = ep2->ex_next; 1196 } 1197 if (ep2) 1198 ep->ex_next = ep2; 1199 *epp = ep; 1200 ep->ex_flag |= EX_LINKED; 1201 } 1202nextline: 1203 if (dirhead) { 1204 free_dir(dirhead); 1205 dirhead = (struct dirlist *)NULL; 1206 } 1207 } 1208 fclose(exp_file); 1209} 1210 1211/* 1212 * Allocate an export list element 1213 */ 1214struct exportlist * 1215get_exp() 1216{ 1217 struct exportlist *ep; 1218 1219 ep = (struct exportlist *)malloc(sizeof (struct exportlist)); 1220 if (ep == (struct exportlist *)NULL) 1221 out_of_mem(); 1222 memset(ep, 0, sizeof(struct exportlist)); 1223 return (ep); 1224} 1225 1226/* 1227 * Allocate a group list element 1228 */ 1229struct grouplist * 1230get_grp() 1231{ 1232 struct grouplist *gp; 1233 1234 gp = (struct grouplist *)malloc(sizeof (struct grouplist)); 1235 if (gp == (struct grouplist *)NULL) 1236 out_of_mem(); 1237 memset(gp, 0, sizeof(struct grouplist)); 1238 return (gp); 1239} 1240 1241/* 1242 * Clean up upon an error in get_exportlist(). 1243 */ 1244void 1245getexp_err(ep, grp) 1246 struct exportlist *ep; 1247 struct grouplist *grp; 1248{ 1249 struct grouplist *tgrp; 1250 1251 if (!(opt_flags & OP_QUIET)) 1252 syslog(LOG_ERR, "bad exports list line %s", line); 1253 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1254 free_exp(ep); 1255 while (grp) { 1256 tgrp = grp; 1257 grp = grp->gr_next; 1258 free_grp(tgrp); 1259 } 1260} 1261 1262/* 1263 * Search the export list for a matching fs. 1264 */ 1265struct exportlist * 1266ex_search(fsid) 1267 fsid_t *fsid; 1268{ 1269 struct exportlist *ep; 1270 1271 ep = exphead; 1272 while (ep) { 1273 if (ep->ex_fs.val[0] == fsid->val[0] && 1274 ep->ex_fs.val[1] == fsid->val[1]) 1275 return (ep); 1276 ep = ep->ex_next; 1277 } 1278 return (ep); 1279} 1280 1281/* 1282 * Add a directory path to the list. 1283 */ 1284char * 1285add_expdir(dpp, cp, len) 1286 struct dirlist **dpp; 1287 char *cp; 1288 int len; 1289{ 1290 struct dirlist *dp; 1291 1292 dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); 1293 if (dp == (struct dirlist *)NULL) 1294 out_of_mem(); 1295 dp->dp_left = *dpp; 1296 dp->dp_right = (struct dirlist *)NULL; 1297 dp->dp_flag = 0; 1298 dp->dp_hosts = (struct hostlist *)NULL; 1299 strcpy(dp->dp_dirp, cp); 1300 *dpp = dp; 1301 return (dp->dp_dirp); 1302} 1303 1304/* 1305 * Hang the dir list element off the dirpath binary tree as required 1306 * and update the entry for host. 1307 */ 1308void 1309hang_dirp(dp, grp, ep, flags) 1310 struct dirlist *dp; 1311 struct grouplist *grp; 1312 struct exportlist *ep; 1313 int flags; 1314{ 1315 struct hostlist *hp; 1316 struct dirlist *dp2; 1317 1318 if (flags & OP_ALLDIRS) { 1319 if (ep->ex_defdir) 1320 free((caddr_t)dp); 1321 else 1322 ep->ex_defdir = dp; 1323 if (grp == (struct grouplist *)NULL) { 1324 ep->ex_defdir->dp_flag |= DP_DEFSET; 1325 } else while (grp) { 1326 hp = get_ht(); 1327 hp->ht_grp = grp; 1328 hp->ht_next = ep->ex_defdir->dp_hosts; 1329 ep->ex_defdir->dp_hosts = hp; 1330 grp = grp->gr_next; 1331 } 1332 } else { 1333 1334 /* 1335 * Loop through the directories adding them to the tree. 1336 */ 1337 while (dp) { 1338 dp2 = dp->dp_left; 1339 add_dlist(&ep->ex_dirl, dp, grp, flags); 1340 dp = dp2; 1341 } 1342 } 1343} 1344 1345/* 1346 * Traverse the binary tree either updating a node that is already there 1347 * for the new directory or adding the new node. 1348 */ 1349void 1350add_dlist(dpp, newdp, grp, flags) 1351 struct dirlist **dpp; 1352 struct dirlist *newdp; 1353 struct grouplist *grp; 1354 int flags; 1355{ 1356 struct dirlist *dp; 1357 struct hostlist *hp; 1358 int cmp; 1359 1360 dp = *dpp; 1361 if (dp) { 1362 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 1363 if (cmp > 0) { 1364 add_dlist(&dp->dp_left, newdp, grp, flags); 1365 return; 1366 } else if (cmp < 0) { 1367 add_dlist(&dp->dp_right, newdp, grp, flags); 1368 return; 1369 } else 1370 free((caddr_t)newdp); 1371 } else { 1372 dp = newdp; 1373 dp->dp_left = (struct dirlist *)NULL; 1374 *dpp = dp; 1375 } 1376 if (grp) { 1377 1378 /* 1379 * Hang all of the host(s) off of the directory point. 1380 */ 1381 do { 1382 hp = get_ht(); 1383 hp->ht_grp = grp; 1384 hp->ht_next = dp->dp_hosts; 1385 dp->dp_hosts = hp; 1386 grp = grp->gr_next; 1387 } while (grp); 1388 } else { 1389 dp->dp_flag |= DP_DEFSET; 1390 } 1391} 1392 1393/* 1394 * Search for a dirpath on the export point. 1395 */ 1396struct dirlist * 1397dirp_search(dp, dirp) 1398 struct dirlist *dp; 1399 char *dirp; 1400{ 1401 int cmp; 1402 1403 if (dp) { 1404 cmp = strcmp(dp->dp_dirp, dirp); 1405 if (cmp > 0) 1406 return (dirp_search(dp->dp_left, dirp)); 1407 else if (cmp < 0) 1408 return (dirp_search(dp->dp_right, dirp)); 1409 else 1410 return (dp); 1411 } 1412 return (dp); 1413} 1414 1415/* 1416 * Scan for a host match in a directory tree. 1417 */ 1418int 1419chk_host(dp, saddr, defsetp, hostsetp) 1420 struct dirlist *dp; 1421 struct sockaddr *saddr; 1422 int *defsetp; 1423 int *hostsetp; 1424{ 1425 struct hostlist *hp; 1426 struct grouplist *grp; 1427 struct addrinfo *ai; 1428 1429 if (dp) { 1430 if (dp->dp_flag & DP_DEFSET) 1431 *defsetp = dp->dp_flag; 1432 hp = dp->dp_hosts; 1433 while (hp) { 1434 grp = hp->ht_grp; 1435 switch (grp->gr_type) { 1436 case GT_HOST: 1437 ai = grp->gr_ptr.gt_addrinfo; 1438 for (; ai; ai = ai->ai_next) { 1439 if (!sacmp(ai->ai_addr, saddr, NULL)) { 1440 *hostsetp = 1441 (hp->ht_flag | DP_HOSTSET); 1442 return (1); 1443 } 1444 } 1445 break; 1446 case GT_NET: 1447 if (!sacmp(saddr, (struct sockaddr *) 1448 &grp->gr_ptr.gt_net.nt_net, 1449 (struct sockaddr *) 1450 &grp->gr_ptr.gt_net.nt_mask)) { 1451 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1452 return (1); 1453 } 1454 break; 1455 } 1456 hp = hp->ht_next; 1457 } 1458 } 1459 return (0); 1460} 1461 1462/* 1463 * Scan tree for a host that matches the address. 1464 */ 1465int 1466scan_tree(dp, saddr) 1467 struct dirlist *dp; 1468 struct sockaddr *saddr; 1469{ 1470 int defset, hostset; 1471 1472 if (dp) { 1473 if (scan_tree(dp->dp_left, saddr)) 1474 return (1); 1475 if (chk_host(dp, saddr, &defset, &hostset)) 1476 return (1); 1477 if (scan_tree(dp->dp_right, saddr)) 1478 return (1); 1479 } 1480 return (0); 1481} 1482 1483/* 1484 * Traverse the dirlist tree and free it up. 1485 */ 1486void 1487free_dir(dp) 1488 struct dirlist *dp; 1489{ 1490 1491 if (dp) { 1492 free_dir(dp->dp_left); 1493 free_dir(dp->dp_right); 1494 free_host(dp->dp_hosts); 1495 free((caddr_t)dp); 1496 } 1497} 1498 1499/* 1500 * Parse the option string and update fields. 1501 * Option arguments may either be -<option>=<value> or 1502 * -<option> <value> 1503 */ 1504int 1505do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) 1506 char **cpp, **endcpp; 1507 struct exportlist *ep; 1508 struct grouplist *grp; 1509 int *has_hostp; 1510 int *exflagsp; 1511 struct xucred *cr; 1512{ 1513 char *cpoptarg, *cpoptend; 1514 char *cp, *endcp, *cpopt, savedc, savedc2; 1515 int allflag, usedarg; 1516 1517 savedc2 = '\0'; 1518 cpopt = *cpp; 1519 cpopt++; 1520 cp = *endcpp; 1521 savedc = *cp; 1522 *cp = '\0'; 1523 while (cpopt && *cpopt) { 1524 allflag = 1; 1525 usedarg = -2; 1526 if ((cpoptend = strchr(cpopt, ','))) { 1527 *cpoptend++ = '\0'; 1528 if ((cpoptarg = strchr(cpopt, '='))) 1529 *cpoptarg++ = '\0'; 1530 } else { 1531 if ((cpoptarg = strchr(cpopt, '='))) 1532 *cpoptarg++ = '\0'; 1533 else { 1534 *cp = savedc; 1535 nextfield(&cp, &endcp); 1536 **endcpp = '\0'; 1537 if (endcp > cp && *cp != '-') { 1538 cpoptarg = cp; 1539 savedc2 = *endcp; 1540 *endcp = '\0'; 1541 usedarg = 0; 1542 } 1543 } 1544 } 1545 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 1546 *exflagsp |= MNT_EXRDONLY; 1547 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 1548 !(allflag = strcmp(cpopt, "mapall")) || 1549 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 1550 usedarg++; 1551 parsecred(cpoptarg, cr); 1552 if (allflag == 0) { 1553 *exflagsp |= MNT_EXPORTANON; 1554 opt_flags |= OP_MAPALL; 1555 } else 1556 opt_flags |= OP_MAPROOT; 1557 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 1558 !strcmp(cpopt, "m"))) { 1559 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 1560 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 1561 return (1); 1562 } 1563 usedarg++; 1564 opt_flags |= OP_MASK; 1565 } else if (cpoptarg && (!strcmp(cpopt, "network") || 1566 !strcmp(cpopt, "n"))) { 1567 if (strchr(cpoptarg, '/') != NULL) { 1568 if (debug) 1569 fprintf(stderr, "setting OP_MASKLEN\n"); 1570 opt_flags |= OP_MASKLEN; 1571 } 1572 if (grp->gr_type != GT_NULL) { 1573 syslog(LOG_ERR, "network/host conflict"); 1574 return (1); 1575 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 1576 syslog(LOG_ERR, "bad net: %s", cpoptarg); 1577 return (1); 1578 } 1579 grp->gr_type = GT_NET; 1580 *has_hostp = 1; 1581 usedarg++; 1582 opt_flags |= OP_NET; 1583 } else if (!strcmp(cpopt, "alldirs")) { 1584 opt_flags |= OP_ALLDIRS; 1585 } else if (!strcmp(cpopt, "public")) { 1586 *exflagsp |= MNT_EXPUBLIC; 1587 } else if (!strcmp(cpopt, "webnfs")) { 1588 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 1589 opt_flags |= OP_MAPALL; 1590 } else if (cpoptarg && !strcmp(cpopt, "index")) { 1591 ep->ex_indexfile = strdup(cpoptarg); 1592 } else if (!strcmp(cpopt, "quiet")) { 1593 opt_flags |= OP_QUIET; 1594 } else { 1595 syslog(LOG_ERR, "bad opt %s", cpopt); 1596 return (1); 1597 } 1598 if (usedarg >= 0) { 1599 *endcp = savedc2; 1600 **endcpp = savedc; 1601 if (usedarg > 0) { 1602 *cpp = cp; 1603 *endcpp = endcp; 1604 } 1605 return (0); 1606 } 1607 cpopt = cpoptend; 1608 } 1609 **endcpp = savedc; 1610 return (0); 1611} 1612 1613/* 1614 * Translate a character string to the corresponding list of network 1615 * addresses for a hostname. 1616 */ 1617int 1618get_host(cp, grp, tgrp) 1619 char *cp; 1620 struct grouplist *grp; 1621 struct grouplist *tgrp; 1622{ 1623 struct grouplist *checkgrp; 1624 struct addrinfo *ai, *tai, hints; 1625 int ecode; 1626 char host[NI_MAXHOST]; 1627 1628 if (grp->gr_type != GT_NULL) { 1629 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 1630 return (1); 1631 } 1632 memset(&hints, 0, sizeof hints); 1633 hints.ai_flags = AI_CANONNAME; 1634 hints.ai_protocol = IPPROTO_UDP; 1635 ecode = getaddrinfo(cp, NULL, &hints, &ai); 1636 if (ecode != 0) { 1637 syslog(LOG_ERR,"can't get address info for host %s", cp); 1638 return 1; 1639 } 1640 grp->gr_ptr.gt_addrinfo = ai; 1641 while (ai != NULL) { 1642 if (ai->ai_canonname == NULL) { 1643 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 1644 sizeof host, NULL, 0, ninumeric) != 0) 1645 strlcpy(host, "?", sizeof(host)); 1646 ai->ai_canonname = strdup(host); 1647 ai->ai_flags |= AI_CANONNAME; 1648 } 1649 if (debug) 1650 fprintf(stderr, "got host %s\n", ai->ai_canonname); 1651 /* 1652 * Sanity check: make sure we don't already have an entry 1653 * for this host in the grouplist. 1654 */ 1655 for (checkgrp = tgrp; checkgrp != NULL; 1656 checkgrp = checkgrp->gr_next) { 1657 if (checkgrp->gr_type != GT_HOST) 1658 continue; 1659 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 1660 tai = tai->ai_next) { 1661 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 1662 continue; 1663 if (debug) 1664 fprintf(stderr, 1665 "ignoring duplicate host %s\n", 1666 ai->ai_canonname); 1667 grp->gr_type = GT_IGNORE; 1668 return (0); 1669 } 1670 } 1671 ai = ai->ai_next; 1672 } 1673 grp->gr_type = GT_HOST; 1674 return (0); 1675} 1676 1677/* 1678 * Free up an exports list component 1679 */ 1680void 1681free_exp(ep) 1682 struct exportlist *ep; 1683{ 1684 1685 if (ep->ex_defdir) { 1686 free_host(ep->ex_defdir->dp_hosts); 1687 free((caddr_t)ep->ex_defdir); 1688 } 1689 if (ep->ex_fsdir) 1690 free(ep->ex_fsdir); 1691 if (ep->ex_indexfile) 1692 free(ep->ex_indexfile); 1693 free_dir(ep->ex_dirl); 1694 free((caddr_t)ep); 1695} 1696 1697/* 1698 * Free hosts. 1699 */ 1700void 1701free_host(hp) 1702 struct hostlist *hp; 1703{ 1704 struct hostlist *hp2; 1705 1706 while (hp) { 1707 hp2 = hp; 1708 hp = hp->ht_next; 1709 free((caddr_t)hp2); 1710 } 1711} 1712 1713struct hostlist * 1714get_ht() 1715{ 1716 struct hostlist *hp; 1717 1718 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 1719 if (hp == (struct hostlist *)NULL) 1720 out_of_mem(); 1721 hp->ht_next = (struct hostlist *)NULL; 1722 hp->ht_flag = 0; 1723 return (hp); 1724} 1725 1726/* 1727 * Out of memory, fatal 1728 */ 1729void 1730out_of_mem() 1731{ 1732 1733 syslog(LOG_ERR, "out of memory"); 1734 exit(2); 1735} 1736 1737/* 1738 * Do the mount syscall with the update flag to push the export info into 1739 * the kernel. 1740 */ 1741int 1742do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) 1743 struct exportlist *ep; 1744 struct grouplist *grp; 1745 int exflags; 1746 struct xucred *anoncrp; 1747 char *dirp; 1748 int dirplen; 1749 struct statfs *fsb; 1750{ 1751 struct statfs fsb1; 1752 struct addrinfo *ai; 1753 struct export_args *eap; 1754 char *cp = NULL; 1755 int done; 1756 char savedc = '\0'; 1757 union { 1758 struct ufs_args ua; 1759 struct iso_args ia; 1760 struct msdosfs_args da; 1761 struct ntfs_args na; 1762 } args; 1763 1764 bzero(&args, sizeof args); 1765 /* XXX, we assume that all xx_args look like ufs_args. */ 1766 args.ua.fspec = 0; 1767 eap = &args.ua.export; 1768 1769 eap->ex_flags = exflags; 1770 eap->ex_anon = *anoncrp; 1771 eap->ex_indexfile = ep->ex_indexfile; 1772 if (grp->gr_type == GT_HOST) 1773 ai = grp->gr_ptr.gt_addrinfo; 1774 else 1775 ai = NULL; 1776 done = FALSE; 1777 while (!done) { 1778 switch (grp->gr_type) { 1779 case GT_HOST: 1780 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 1781 goto skip; 1782 eap->ex_addr = ai->ai_addr; 1783 eap->ex_addrlen = ai->ai_addrlen; 1784 eap->ex_masklen = 0; 1785 break; 1786 case GT_NET: 1787 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 1788 have_v6 == 0) 1789 goto skip; 1790 eap->ex_addr = 1791 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 1792 eap->ex_addrlen = args.ua.export.ex_addr->sa_len; 1793 eap->ex_mask = 1794 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 1795 eap->ex_masklen = args.ua.export.ex_mask->sa_len; 1796 break; 1797 case GT_DEFAULT: 1798 eap->ex_addr = NULL; 1799 eap->ex_addrlen = 0; 1800 eap->ex_mask = NULL; 1801 eap->ex_masklen = 0; 1802 break; 1803 case GT_IGNORE: 1804 return(0); 1805 break; 1806 default: 1807 syslog(LOG_ERR, "bad grouptype"); 1808 if (cp) 1809 *cp = savedc; 1810 return (1); 1811 }; 1812 1813 /* 1814 * XXX: 1815 * Maybe I should just use the fsb->f_mntonname path instead 1816 * of looping back up the dirp to the mount point?? 1817 * Also, needs to know how to export all types of local 1818 * exportable filesystems and not just "ufs". 1819 */ 1820 while (mount(fsb->f_fstypename, dirp, 1821 fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { 1822 if (cp) 1823 *cp-- = savedc; 1824 else 1825 cp = dirp + dirplen - 1; 1826 if (opt_flags & OP_QUIET) 1827 return (1); 1828 if (errno == EPERM) { 1829 if (debug) 1830 warnx("can't change attributes for %s", 1831 dirp); 1832 syslog(LOG_ERR, 1833 "can't change attributes for %s", dirp); 1834 return (1); 1835 } 1836 if (opt_flags & OP_ALLDIRS) { 1837 if (errno == EINVAL) 1838 syslog(LOG_ERR, 1839 "-alldirs requested but %s is not a filesystem mountpoint", 1840 dirp); 1841 else 1842 syslog(LOG_ERR, 1843 "could not remount %s: %m", 1844 dirp); 1845 return (1); 1846 } 1847 /* back up over the last component */ 1848 while (*cp == '/' && cp > dirp) 1849 cp--; 1850 while (*(cp - 1) != '/' && cp > dirp) 1851 cp--; 1852 if (cp == dirp) { 1853 if (debug) 1854 warnx("mnt unsucc"); 1855 syslog(LOG_ERR, "can't export %s", dirp); 1856 return (1); 1857 } 1858 savedc = *cp; 1859 *cp = '\0'; 1860 /* Check that we're still on the same filesystem. */ 1861 if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid, 1862 &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) { 1863 *cp = savedc; 1864 syslog(LOG_ERR, "can't export %s", dirp); 1865 return (1); 1866 } 1867 } 1868skip: 1869 if (ai != NULL) 1870 ai = ai->ai_next; 1871 if (ai == NULL) 1872 done = TRUE; 1873 } 1874 if (cp) 1875 *cp = savedc; 1876 return (0); 1877} 1878 1879/* 1880 * Translate a net address. 1881 * 1882 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 1883 */ 1884int 1885get_net(cp, net, maskflg) 1886 char *cp; 1887 struct netmsk *net; 1888 int maskflg; 1889{ 1890 struct netent *np = NULL; 1891 char *name, *p, *prefp; 1892 struct sockaddr_in sin; 1893 struct sockaddr *sa = NULL; 1894 struct addrinfo hints, *ai = NULL; 1895 char netname[NI_MAXHOST]; 1896 long preflen; 1897 1898 p = prefp = NULL; 1899 if ((opt_flags & OP_MASKLEN) && !maskflg) { 1900 p = strchr(cp, '/'); 1901 *p = '\0'; 1902 prefp = p + 1; 1903 } 1904 1905 /* 1906 * Check for a numeric address first. We wish to avoid 1907 * possible DNS lookups in getnetbyname(). 1908 */ 1909 if (isxdigit(*cp) || *cp == ':') { 1910 memset(&hints, 0, sizeof hints); 1911 /* Ensure the mask and the network have the same family. */ 1912 if (maskflg && (opt_flags & OP_NET)) 1913 hints.ai_family = net->nt_net.ss_family; 1914 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 1915 hints.ai_family = net->nt_mask.ss_family; 1916 else 1917 hints.ai_family = AF_UNSPEC; 1918 hints.ai_flags = AI_NUMERICHOST; 1919 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 1920 sa = ai->ai_addr; 1921 if (sa != NULL && ai->ai_family == AF_INET) { 1922 /* 1923 * The address in `cp' is really a network address, so 1924 * use inet_network() to re-interpret this correctly. 1925 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 1926 */ 1927 bzero(&sin, sizeof sin); 1928 sin.sin_family = AF_INET; 1929 sin.sin_len = sizeof sin; 1930 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 1931 if (debug) 1932 fprintf(stderr, "get_net: v4 addr %s\n", 1933 inet_ntoa(sin.sin_addr)); 1934 sa = (struct sockaddr *)&sin; 1935 } 1936 } 1937 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 1938 bzero(&sin, sizeof sin); 1939 sin.sin_family = AF_INET; 1940 sin.sin_len = sizeof sin; 1941 sin.sin_addr = inet_makeaddr(np->n_net, 0); 1942 sa = (struct sockaddr *)&sin; 1943 } 1944 if (sa == NULL) 1945 goto fail; 1946 1947 if (maskflg) { 1948 /* The specified sockaddr is a mask. */ 1949 if (checkmask(sa) != 0) 1950 goto fail; 1951 bcopy(sa, &net->nt_mask, sa->sa_len); 1952 opt_flags |= OP_HAVEMASK; 1953 } else { 1954 /* The specified sockaddr is a network address. */ 1955 bcopy(sa, &net->nt_net, sa->sa_len); 1956 1957 /* Get a network name for the export list. */ 1958 if (np) { 1959 name = np->n_name; 1960 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 1961 NULL, 0, ninumeric) == 0) { 1962 name = netname; 1963 } else { 1964 goto fail; 1965 } 1966 if ((net->nt_name = strdup(name)) == NULL) 1967 out_of_mem(); 1968 1969 /* 1970 * Extract a mask from either a "/<masklen>" suffix, or 1971 * from the class of an IPv4 address. 1972 */ 1973 if (opt_flags & OP_MASKLEN) { 1974 preflen = strtol(prefp, NULL, 10); 1975 if (preflen < 0L || preflen == LONG_MAX) 1976 goto fail; 1977 bcopy(sa, &net->nt_mask, sa->sa_len); 1978 if (makemask(&net->nt_mask, (int)preflen) != 0) 1979 goto fail; 1980 opt_flags |= OP_HAVEMASK; 1981 *p = '/'; 1982 } else if (sa->sa_family == AF_INET && 1983 (opt_flags & OP_MASK) == 0) { 1984 in_addr_t addr; 1985 1986 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 1987 if (IN_CLASSA(addr)) 1988 preflen = 8; 1989 else if (IN_CLASSB(addr)) 1990 preflen = 16; 1991 else if (IN_CLASSC(addr)) 1992 preflen = 24; 1993 else if (IN_CLASSD(addr)) 1994 preflen = 28; 1995 else 1996 preflen = 32; /* XXX */ 1997 1998 bcopy(sa, &net->nt_mask, sa->sa_len); 1999 makemask(&net->nt_mask, (int)preflen); 2000 opt_flags |= OP_HAVEMASK; 2001 } 2002 } 2003 2004 if (ai) 2005 freeaddrinfo(ai); 2006 return 0; 2007 2008fail: 2009 if (ai) 2010 freeaddrinfo(ai); 2011 return 1; 2012} 2013 2014/* 2015 * Parse out the next white space separated field 2016 */ 2017void 2018nextfield(cp, endcp) 2019 char **cp; 2020 char **endcp; 2021{ 2022 char *p; 2023 2024 p = *cp; 2025 while (*p == ' ' || *p == '\t') 2026 p++; 2027 if (*p == '\n' || *p == '\0') 2028 *cp = *endcp = p; 2029 else { 2030 *cp = p++; 2031 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 2032 p++; 2033 *endcp = p; 2034 } 2035} 2036 2037/* 2038 * Get an exports file line. Skip over blank lines and handle line 2039 * continuations. 2040 */ 2041int 2042get_line() 2043{ 2044 char *p, *cp; 2045 size_t len; 2046 int totlen, cont_line; 2047 2048 /* 2049 * Loop around ignoring blank lines and getting all continuation lines. 2050 */ 2051 p = line; 2052 totlen = 0; 2053 do { 2054 if ((p = fgetln(exp_file, &len)) == NULL) 2055 return (0); 2056 cp = p + len - 1; 2057 cont_line = 0; 2058 while (cp >= p && 2059 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 2060 if (*cp == '\\') 2061 cont_line = 1; 2062 cp--; 2063 len--; 2064 } 2065 if (cont_line) { 2066 *++cp = ' '; 2067 len++; 2068 } 2069 if (linesize < len + totlen + 1) { 2070 linesize = len + totlen + 1; 2071 line = realloc(line, linesize); 2072 if (line == NULL) 2073 out_of_mem(); 2074 } 2075 memcpy(line + totlen, p, len); 2076 totlen += len; 2077 line[totlen] = '\0'; 2078 } while (totlen == 0 || cont_line); 2079 return (1); 2080} 2081 2082/* 2083 * Parse a description of a credential. 2084 */ 2085void 2086parsecred(namelist, cr) 2087 char *namelist; 2088 struct xucred *cr; 2089{ 2090 char *name; 2091 int cnt; 2092 char *names; 2093 struct passwd *pw; 2094 struct group *gr; 2095 int ngroups, groups[NGROUPS + 1]; 2096 2097 cr->cr_version = XUCRED_VERSION; 2098 /* 2099 * Set up the unprivileged user. 2100 */ 2101 cr->cr_uid = -2; 2102 cr->cr_groups[0] = -2; 2103 cr->cr_ngroups = 1; 2104 /* 2105 * Get the user's password table entry. 2106 */ 2107 names = strsep(&namelist, " \t\n"); 2108 name = strsep(&names, ":"); 2109 if (isdigit(*name) || *name == '-') 2110 pw = getpwuid(atoi(name)); 2111 else 2112 pw = getpwnam(name); 2113 /* 2114 * Credentials specified as those of a user. 2115 */ 2116 if (names == NULL) { 2117 if (pw == NULL) { 2118 syslog(LOG_ERR, "unknown user: %s", name); 2119 return; 2120 } 2121 cr->cr_uid = pw->pw_uid; 2122 ngroups = NGROUPS + 1; 2123 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 2124 syslog(LOG_ERR, "too many groups"); 2125 /* 2126 * Convert from int's to gid_t's and compress out duplicate 2127 */ 2128 cr->cr_ngroups = ngroups - 1; 2129 cr->cr_groups[0] = groups[0]; 2130 for (cnt = 2; cnt < ngroups; cnt++) 2131 cr->cr_groups[cnt - 1] = groups[cnt]; 2132 return; 2133 } 2134 /* 2135 * Explicit credential specified as a colon separated list: 2136 * uid:gid:gid:... 2137 */ 2138 if (pw != NULL) 2139 cr->cr_uid = pw->pw_uid; 2140 else if (isdigit(*name) || *name == '-') 2141 cr->cr_uid = atoi(name); 2142 else { 2143 syslog(LOG_ERR, "unknown user: %s", name); 2144 return; 2145 } 2146 cr->cr_ngroups = 0; 2147 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 2148 name = strsep(&names, ":"); 2149 if (isdigit(*name) || *name == '-') { 2150 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 2151 } else { 2152 if ((gr = getgrnam(name)) == NULL) { 2153 syslog(LOG_ERR, "unknown group: %s", name); 2154 continue; 2155 } 2156 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 2157 } 2158 } 2159 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 2160 syslog(LOG_ERR, "too many groups"); 2161} 2162 2163#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 2164/* 2165 * Routines that maintain the remote mounttab 2166 */ 2167void 2168get_mountlist() 2169{ 2170 struct mountlist *mlp, **mlpp; 2171 char *host, *dirp, *cp; 2172 char str[STRSIZ]; 2173 FILE *mlfile; 2174 2175 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 2176 if (errno == ENOENT) 2177 return; 2178 else { 2179 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 2180 return; 2181 } 2182 } 2183 mlpp = &mlhead; 2184 while (fgets(str, STRSIZ, mlfile) != NULL) { 2185 cp = str; 2186 host = strsep(&cp, " \t\n"); 2187 dirp = strsep(&cp, " \t\n"); 2188 if (host == NULL || dirp == NULL) 2189 continue; 2190 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2191 if (mlp == (struct mountlist *)NULL) 2192 out_of_mem(); 2193 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN); 2194 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2195 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2196 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2197 mlp->ml_next = (struct mountlist *)NULL; 2198 *mlpp = mlp; 2199 mlpp = &mlp->ml_next; 2200 } 2201 fclose(mlfile); 2202} 2203 2204void 2205del_mlist(char *hostp, char *dirp) 2206{ 2207 struct mountlist *mlp, **mlpp; 2208 struct mountlist *mlp2; 2209 FILE *mlfile; 2210 int fnd = 0; 2211 2212 mlpp = &mlhead; 2213 mlp = mlhead; 2214 while (mlp) { 2215 if (!strcmp(mlp->ml_host, hostp) && 2216 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 2217 fnd = 1; 2218 mlp2 = mlp; 2219 *mlpp = mlp = mlp->ml_next; 2220 free((caddr_t)mlp2); 2221 } else { 2222 mlpp = &mlp->ml_next; 2223 mlp = mlp->ml_next; 2224 } 2225 } 2226 if (fnd) { 2227 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 2228 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 2229 return; 2230 } 2231 mlp = mlhead; 2232 while (mlp) { 2233 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2234 mlp = mlp->ml_next; 2235 } 2236 fclose(mlfile); 2237 } 2238} 2239 2240void 2241add_mlist(hostp, dirp) 2242 char *hostp, *dirp; 2243{ 2244 struct mountlist *mlp, **mlpp; 2245 FILE *mlfile; 2246 2247 mlpp = &mlhead; 2248 mlp = mlhead; 2249 while (mlp) { 2250 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 2251 return; 2252 mlpp = &mlp->ml_next; 2253 mlp = mlp->ml_next; 2254 } 2255 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2256 if (mlp == (struct mountlist *)NULL) 2257 out_of_mem(); 2258 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN); 2259 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2260 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2261 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2262 mlp->ml_next = (struct mountlist *)NULL; 2263 *mlpp = mlp; 2264 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 2265 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 2266 return; 2267 } 2268 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2269 fclose(mlfile); 2270} 2271 2272/* 2273 * Free up a group list. 2274 */ 2275void 2276free_grp(grp) 2277 struct grouplist *grp; 2278{ 2279 if (grp->gr_type == GT_HOST) { 2280 if (grp->gr_ptr.gt_addrinfo != NULL) 2281 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 2282 } else if (grp->gr_type == GT_NET) { 2283 if (grp->gr_ptr.gt_net.nt_name) 2284 free(grp->gr_ptr.gt_net.nt_name); 2285 } 2286 free((caddr_t)grp); 2287} 2288 2289#ifdef DEBUG 2290void 2291SYSLOG(int pri, const char *fmt, ...) 2292{ 2293 va_list ap; 2294 2295 va_start(ap, fmt); 2296 vfprintf(stderr, fmt, ap); 2297 va_end(ap); 2298} 2299#endif /* DEBUG */ 2300 2301/* 2302 * Check options for consistency. 2303 */ 2304int 2305check_options(dp) 2306 struct dirlist *dp; 2307{ 2308 2309 if (dp == (struct dirlist *)NULL) 2310 return (1); 2311 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 2312 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 2313 return (1); 2314 } 2315 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 2316 syslog(LOG_ERR, "-mask requires -network"); 2317 return (1); 2318 } 2319 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 2320 syslog(LOG_ERR, "-network requires mask specification"); 2321 return (1); 2322 } 2323 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 2324 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 2325 return (1); 2326 } 2327 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 2328 syslog(LOG_ERR, "-alldirs has multiple directories"); 2329 return (1); 2330 } 2331 return (0); 2332} 2333 2334/* 2335 * Check an absolute directory path for any symbolic links. Return true 2336 */ 2337int 2338check_dirpath(dirp) 2339 char *dirp; 2340{ 2341 char *cp; 2342 int ret = 1; 2343 struct stat sb; 2344 2345 cp = dirp + 1; 2346 while (*cp && ret) { 2347 if (*cp == '/') { 2348 *cp = '\0'; 2349 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2350 ret = 0; 2351 *cp = '/'; 2352 } 2353 cp++; 2354 } 2355 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2356 ret = 0; 2357 return (ret); 2358} 2359 2360/* 2361 * Make a netmask according to the specified prefix length. The ss_family 2362 * and other non-address fields must be initialised before calling this. 2363 */ 2364int 2365makemask(struct sockaddr_storage *ssp, int bitlen) 2366{ 2367 u_char *p; 2368 int bits, i, len; 2369 2370 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 2371 return (-1); 2372 if (bitlen > len * CHAR_BIT) 2373 return (-1); 2374 2375 for (i = 0; i < len; i++) { 2376 bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen; 2377 *p++ = (1 << bits) - 1; 2378 bitlen -= bits; 2379 } 2380 return 0; 2381} 2382 2383/* 2384 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 2385 * is acceptable (i.e. of the form 1...10....0). 2386 */ 2387int 2388checkmask(struct sockaddr *sa) 2389{ 2390 u_char *mask; 2391 int i, len; 2392 2393 if ((mask = sa_rawaddr(sa, &len)) == NULL) 2394 return (-1); 2395 2396 for (i = 0; i < len; i++) 2397 if (mask[i] != 0xff) 2398 break; 2399 if (i < len) { 2400 if (~mask[i] & (u_char)(~mask[i] + 1)) 2401 return (-1); 2402 i++; 2403 } 2404 for (; i < len; i++) 2405 if (mask[i] != 0) 2406 return (-1); 2407 return (0); 2408} 2409 2410/* 2411 * Compare two sockaddrs according to a specified mask. Return zero if 2412 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 2413 * If samask is NULL, perform a full comparision. 2414 */ 2415int 2416sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 2417{ 2418 unsigned char *p1, *p2, *mask; 2419 int len, i; 2420 2421 if (sa1->sa_family != sa2->sa_family || 2422 (p1 = sa_rawaddr(sa1, &len)) == NULL || 2423 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 2424 return (1); 2425 2426 switch (sa1->sa_family) { 2427 case AF_INET6: 2428 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 2429 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 2430 return (1); 2431 break; 2432 } 2433 2434 /* Simple binary comparison if no mask specified. */ 2435 if (samask == NULL) 2436 return (memcmp(p1, p2, len)); 2437 2438 /* Set up the mask, and do a mask-based comparison. */ 2439 if (sa1->sa_family != samask->sa_family || 2440 (mask = sa_rawaddr(samask, NULL)) == NULL) 2441 return (1); 2442 2443 for (i = 0; i < len; i++) 2444 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 2445 return (1); 2446 return (0); 2447} 2448 2449/* 2450 * Return a pointer to the part of the sockaddr that contains the 2451 * raw address, and set *nbytes to its length in bytes. Returns 2452 * NULL if the address family is unknown. 2453 */ 2454void * 2455sa_rawaddr(struct sockaddr *sa, int *nbytes) { 2456 void *p; 2457 int len; 2458 2459 switch (sa->sa_family) { 2460 case AF_INET: 2461 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 2462 p = &((struct sockaddr_in *)sa)->sin_addr; 2463 break; 2464 case AF_INET6: 2465 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 2466 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 2467 break; 2468 default: 2469 p = NULL; 2470 len = 0; 2471 } 2472 2473 if (nbytes != NULL) 2474 *nbytes = len; 2475 return (p); 2476} 2477 2478void 2479huphandler(int sig) 2480{ 2481 got_sighup = 1; 2482} 2483 2484void terminate(sig) 2485int sig; 2486{ 2487 close(mountdlockfd); 2488 unlink(MOUNTDLOCK); 2489 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); 2490 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); 2491 exit (0); 2492} 2493