mountd.c revision 349124
1331722Seadler/* 21558Srgrimes * Copyright (c) 1989, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * This code is derived from software contributed to Berkeley by 61558Srgrimes * Herb Hasler and Rick Macklem at The University of Guelph. 71558Srgrimes * 81558Srgrimes * Redistribution and use in source and binary forms, with or without 91558Srgrimes * modification, are permitted provided that the following conditions 101558Srgrimes * are met: 111558Srgrimes * 1. Redistributions of source code must retain the above copyright 121558Srgrimes * notice, this list of conditions and the following disclaimer. 131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141558Srgrimes * notice, this list of conditions and the following disclaimer in the 151558Srgrimes * documentation and/or other materials provided with the distribution. 161558Srgrimes * 4. Neither the name of the University nor the names of its contributors 171558Srgrimes * may be used to endorse or promote products derived from this software 181558Srgrimes * without specific prior written permission. 191558Srgrimes * 201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301558Srgrimes * SUCH DAMAGE. 311558Srgrimes */ 321558Srgrimes 331558Srgrimes#ifndef lint 3437663Scharnierstatic const char copyright[] = 351558Srgrimes"@(#) Copyright (c) 1989, 1993\n\ 361558Srgrimes The Regents of the University of California. All rights reserved.\n"; 372999Swollman#endif /*not lint*/ 381558Srgrimes 39105267Scharnier#if 0 401558Srgrimes#ifndef lint 4137663Scharnierstatic char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; 42105267Scharnier#endif /*not lint*/ 4337663Scharnier#endif 441558Srgrimes 45105267Scharnier#include <sys/cdefs.h> 46105267Scharnier__FBSDID("$FreeBSD: stable/11/usr.sbin/mountd/mountd.c 349124 2019-06-17 00:11:46Z rmacklem $"); 47105267Scharnier 481558Srgrimes#include <sys/param.h> 49192934Srmacklem#include <sys/fcntl.h> 50192934Srmacklem#include <sys/linker.h> 51192934Srmacklem#include <sys/module.h> 521558Srgrimes#include <sys/mount.h> 53324955Smanu#include <sys/queue.h> 541558Srgrimes#include <sys/stat.h> 55192934Srmacklem#include <sys/sysctl.h> 561558Srgrimes#include <sys/syslog.h> 571558Srgrimes 581558Srgrimes#include <rpc/rpc.h> 59109363Smbr#include <rpc/rpc_com.h> 601558Srgrimes#include <rpc/pmap_clnt.h> 6174462Salfred#include <rpc/pmap_prot.h> 6274462Salfred#include <rpcsvc/mount.h> 639336Sdfr#include <nfs/nfsproto.h> 64192934Srmacklem#include <nfs/nfssvc.h> 6583653Speter#include <nfsserver/nfs.h> 661558Srgrimes 67192934Srmacklem#include <fs/nfs/nfsport.h> 68192934Srmacklem 691558Srgrimes#include <arpa/inet.h> 701558Srgrimes 711558Srgrimes#include <ctype.h> 7237663Scharnier#include <err.h> 731558Srgrimes#include <errno.h> 741558Srgrimes#include <grp.h> 75149433Spjd#include <libutil.h> 76103949Smike#include <limits.h> 771558Srgrimes#include <netdb.h> 781558Srgrimes#include <pwd.h> 791558Srgrimes#include <signal.h> 801558Srgrimes#include <stdio.h> 811558Srgrimes#include <stdlib.h> 821558Srgrimes#include <string.h> 831558Srgrimes#include <unistd.h> 841558Srgrimes#include "pathnames.h" 85158857Srodrigc#include "mntopts.h" 861558Srgrimes 871558Srgrimes#ifdef DEBUG 881558Srgrimes#include <stdarg.h> 891558Srgrimes#endif 901558Srgrimes 911558Srgrimes/* 921558Srgrimes * Structures for keeping the mount list and export list 931558Srgrimes */ 941558Srgrimesstruct mountlist { 95194880Sdfr char ml_host[MNTNAMLEN+1]; 96194880Sdfr char ml_dirp[MNTPATHLEN+1]; 97324955Smanu 98324955Smanu SLIST_ENTRY(mountlist) next; 991558Srgrimes}; 1001558Srgrimes 1011558Srgrimesstruct dirlist { 1021558Srgrimes struct dirlist *dp_left; 1031558Srgrimes struct dirlist *dp_right; 1041558Srgrimes int dp_flag; 1051558Srgrimes struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 106324234Smanu char *dp_dirp; 1071558Srgrimes}; 1081558Srgrimes/* dp_flag bits */ 1091558Srgrimes#define DP_DEFSET 0x1 1109336Sdfr#define DP_HOSTSET 0x2 1111558Srgrimes 1121558Srgrimesstruct exportlist { 1131558Srgrimes struct dirlist *ex_dirl; 1141558Srgrimes struct dirlist *ex_defdir; 1151558Srgrimes int ex_flag; 1161558Srgrimes fsid_t ex_fs; 1171558Srgrimes char *ex_fsdir; 11827447Sdfr char *ex_indexfile; 119184588Sdfr int ex_numsecflavors; 120184588Sdfr int ex_secflavors[MAXSECFLAVORS]; 121240902Srmacklem int ex_defnumsecflavors; 122240902Srmacklem int ex_defsecflavors[MAXSECFLAVORS]; 123324955Smanu 124324955Smanu SLIST_ENTRY(exportlist) entries; 1251558Srgrimes}; 1261558Srgrimes/* ex_flag bits */ 1271558Srgrimes#define EX_LINKED 0x1 1281558Srgrimes 129349124SrmacklemSLIST_HEAD(exportlisthead, exportlist); 130349124Srmacklem 1311558Srgrimesstruct netmsk { 13274462Salfred struct sockaddr_storage nt_net; 13375801Siedowse struct sockaddr_storage nt_mask; 13442144Sdfr char *nt_name; 1351558Srgrimes}; 1361558Srgrimes 1371558Srgrimesunion grouptypes { 13874462Salfred struct addrinfo *gt_addrinfo; 1391558Srgrimes struct netmsk gt_net; 1401558Srgrimes}; 1411558Srgrimes 1421558Srgrimesstruct grouplist { 1431558Srgrimes int gr_type; 1441558Srgrimes union grouptypes gr_ptr; 1451558Srgrimes struct grouplist *gr_next; 146240902Srmacklem int gr_numsecflavors; 147240902Srmacklem int gr_secflavors[MAXSECFLAVORS]; 1481558Srgrimes}; 1491558Srgrimes/* Group types */ 1501558Srgrimes#define GT_NULL 0x0 1511558Srgrimes#define GT_HOST 0x1 1521558Srgrimes#define GT_NET 0x2 15375641Siedowse#define GT_DEFAULT 0x3 1547401Swpaul#define GT_IGNORE 0x5 1551558Srgrimes 1561558Srgrimesstruct hostlist { 1579336Sdfr int ht_flag; /* Uses DP_xx bits */ 1581558Srgrimes struct grouplist *ht_grp; 1591558Srgrimes struct hostlist *ht_next; 1601558Srgrimes}; 1611558Srgrimes 1629336Sdfrstruct fhreturn { 1639336Sdfr int fhr_flag; 1649336Sdfr int fhr_vers; 1659336Sdfr nfsfh_t fhr_fh; 166184588Sdfr int fhr_numsecflavors; 167184588Sdfr int *fhr_secflavors; 1689336Sdfr}; 1699336Sdfr 170222623Srmacklem#define GETPORT_MAXTRY 20 /* Max tries to get a port # */ 171222623Srmacklem 1721558Srgrimes/* Global defs */ 173285128Straszstatic char *add_expdir(struct dirlist **, char *, int); 174285128Straszstatic void add_dlist(struct dirlist **, struct dirlist *, 175285128Strasz struct grouplist *, int, struct exportlist *); 176285128Straszstatic void add_mlist(char *, char *); 177285128Straszstatic int check_dirpath(char *); 178285128Straszstatic int check_options(struct dirlist *); 179285128Straszstatic int checkmask(struct sockaddr *sa); 180285128Straszstatic int chk_host(struct dirlist *, struct sockaddr *, int *, int *, 181285128Strasz int *, int **); 182293305Sjpaetzelstatic char *strsep_quote(char **stringp, const char *delim); 183222623Srmacklemstatic int create_service(struct netconfig *nconf); 184222623Srmacklemstatic void complete_service(struct netconfig *nconf, char *port_str); 185222623Srmacklemstatic void clearout_service(void); 186285128Straszstatic void del_mlist(char *hostp, char *dirp); 187285128Straszstatic struct dirlist *dirp_search(struct dirlist *, char *); 188285128Straszstatic int do_mount(struct exportlist *, struct grouplist *, int, 189285128Strasz struct xucred *, char *, int, struct statfs *); 190285128Straszstatic int do_opt(char **, char **, struct exportlist *, 191285128Strasz struct grouplist *, int *, int *, struct xucred *); 192349124Srmacklemstatic struct exportlist *ex_search(fsid_t *, struct exportlisthead *); 193285128Straszstatic struct exportlist *get_exp(void); 194285128Straszstatic void free_dir(struct dirlist *); 195285128Straszstatic void free_exp(struct exportlist *); 196285128Straszstatic void free_grp(struct grouplist *); 197285128Straszstatic void free_host(struct hostlist *); 198285128Straszstatic void get_exportlist(void); 199349124Srmacklemstatic void insert_exports(struct exportlist *, struct exportlisthead *); 200349124Srmacklemstatic void free_exports(struct exportlisthead *); 201285128Straszstatic int get_host(char *, struct grouplist *, struct grouplist *); 202285128Straszstatic struct hostlist *get_ht(void); 203285128Straszstatic int get_line(void); 204285128Straszstatic void get_mountlist(void); 205285128Straszstatic int get_net(char *, struct netmsk *, int); 206329392Sbrdstatic void getexp_err(struct exportlist *, struct grouplist *, const char *); 207285128Straszstatic struct grouplist *get_grp(void); 208285128Straszstatic void hang_dirp(struct dirlist *, struct grouplist *, 20992882Simp struct exportlist *, int); 210285128Straszstatic void huphandler(int sig); 211285128Straszstatic int makemask(struct sockaddr_storage *ssp, int bitlen); 212285128Straszstatic void mntsrv(struct svc_req *, SVCXPRT *); 213285128Straszstatic void nextfield(char **, char **); 214285128Straszstatic void out_of_mem(void); 215285128Straszstatic void parsecred(char *, struct xucred *); 216285128Straszstatic int parsesec(char *, struct exportlist *); 217285128Straszstatic int put_exlist(struct dirlist *, XDR *, struct dirlist *, 218285128Strasz int *, int); 219285128Straszstatic void *sa_rawaddr(struct sockaddr *sa, int *nbytes); 220285128Straszstatic int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 221285128Strasz struct sockaddr *samask); 222285128Straszstatic int scan_tree(struct dirlist *, struct sockaddr *); 223285128Straszstatic void usage(void); 224285128Straszstatic int xdr_dir(XDR *, char *); 225285128Straszstatic int xdr_explist(XDR *, caddr_t); 226285128Straszstatic int xdr_explist_brief(XDR *, caddr_t); 227285128Straszstatic int xdr_explist_common(XDR *, caddr_t, int); 228285128Straszstatic int xdr_fhs(XDR *, caddr_t); 229285128Straszstatic int xdr_mlist(XDR *, caddr_t); 230285128Straszstatic void terminate(int); 2311558Srgrimes 232349124Srmacklemstatic struct exportlisthead exphead = SLIST_HEAD_INITIALIZER(&exphead); 233349124Srmacklemstatic SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead); 234285128Straszstatic struct grouplist *grphead; 235285128Straszstatic char *exnames_default[2] = { _PATH_EXPORTS, NULL }; 236285128Straszstatic char **exnames; 237285128Straszstatic char **hosts = NULL; 238285128Straszstatic struct xucred def_anon = { 23991354Sdd XUCRED_VERSION, 24072650Sgreen (uid_t)-2, 2411558Srgrimes 1, 24272650Sgreen { (gid_t)-2 }, 24372650Sgreen NULL 2441558Srgrimes}; 245285128Straszstatic int force_v2 = 0; 246285128Straszstatic int resvport_only = 1; 247285128Straszstatic int nhosts = 0; 248285128Straszstatic int dir_only = 1; 249285128Straszstatic int dolog = 0; 250285128Straszstatic int got_sighup = 0; 251285128Straszstatic int xcreated = 0; 25274462Salfred 253285128Straszstatic char *svcport_str = NULL; 254285128Straszstatic int mallocd_svcport = 0; 255285128Straszstatic int *sock_fd; 256285128Straszstatic int sock_fdcnt; 257285128Straszstatic int sock_fdpos; 258285128Straszstatic int suspend_nfsd = 0; 259172827Smatteo 260285128Straszstatic int opt_flags; 26174462Salfredstatic int have_v6 = 1; 26274462Salfred 263285128Straszstatic int v4root_phase = 0; 264285128Straszstatic char v4root_dirpath[PATH_MAX + 1]; 265285128Straszstatic int has_publicfh = 0; 266192934Srmacklem 267285128Straszstatic struct pidfh *pfh = NULL; 26875801Siedowse/* Bits for opt_flags above */ 2691558Srgrimes#define OP_MAPROOT 0x01 2701558Srgrimes#define OP_MAPALL 0x02 27183653Speter/* 0x4 free */ 2721558Srgrimes#define OP_MASK 0x08 2731558Srgrimes#define OP_NET 0x10 2741558Srgrimes#define OP_ALLDIRS 0x40 27575801Siedowse#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 276100336Sjoerg#define OP_QUIET 0x100 27774462Salfred#define OP_MASKLEN 0x200 278184588Sdfr#define OP_SEC 0x400 2791558Srgrimes 2801558Srgrimes#ifdef DEBUG 281285128Straszstatic int debug = 1; 282285128Straszstatic void SYSLOG(int, const char *, ...) __printflike(2, 3); 2831558Srgrimes#define syslog SYSLOG 2841558Srgrimes#else 285285128Straszstatic int debug = 0; 2861558Srgrimes#endif 2871558Srgrimes 2881558Srgrimes/* 289293305Sjpaetzel * Similar to strsep(), but it allows for quoted strings 290293305Sjpaetzel * and escaped characters. 291293305Sjpaetzel * 292293305Sjpaetzel * It returns the string (or NULL, if *stringp is NULL), 293293305Sjpaetzel * which is a de-quoted version of the string if necessary. 294293305Sjpaetzel * 295293305Sjpaetzel * It modifies *stringp in place. 296293305Sjpaetzel */ 297293305Sjpaetzelstatic char * 298293305Sjpaetzelstrsep_quote(char **stringp, const char *delim) 299293305Sjpaetzel{ 300293305Sjpaetzel char *srcptr, *dstptr, *retval; 301293305Sjpaetzel char quot = 0; 302293305Sjpaetzel 303293305Sjpaetzel if (stringp == NULL || *stringp == NULL) 304293305Sjpaetzel return (NULL); 305293305Sjpaetzel 306293305Sjpaetzel srcptr = dstptr = retval = *stringp; 307293305Sjpaetzel 308293305Sjpaetzel while (*srcptr) { 309293305Sjpaetzel /* 310293305Sjpaetzel * We're looking for several edge cases here. 311293305Sjpaetzel * First: if we're in quote state (quot != 0), 312293305Sjpaetzel * then we ignore the delim characters, but otherwise 313293305Sjpaetzel * process as normal, unless it is the quote character. 314293305Sjpaetzel * Second: if the current character is a backslash, 315293305Sjpaetzel * we take the next character as-is, without checking 316293305Sjpaetzel * for delim, quote, or backslash. Exception: if the 317293305Sjpaetzel * next character is a NUL, that's the end of the string. 318293305Sjpaetzel * Third: if the character is a quote character, we toggle 319293305Sjpaetzel * quote state. 320293305Sjpaetzel * Otherwise: check the current character for NUL, or 321293305Sjpaetzel * being in delim, and end the string if either is true. 322293305Sjpaetzel */ 323293305Sjpaetzel if (*srcptr == '\\') { 324293305Sjpaetzel srcptr++; 325293305Sjpaetzel /* 326293305Sjpaetzel * The edge case here is if the next character 327293305Sjpaetzel * is NUL, we want to stop processing. But if 328293305Sjpaetzel * it's not NUL, then we simply want to copy it. 329293305Sjpaetzel */ 330293305Sjpaetzel if (*srcptr) { 331293305Sjpaetzel *dstptr++ = *srcptr++; 332293305Sjpaetzel } 333293305Sjpaetzel continue; 334293305Sjpaetzel } 335293305Sjpaetzel if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) { 336293305Sjpaetzel quot = *srcptr++; 337293305Sjpaetzel continue; 338293305Sjpaetzel } 339293305Sjpaetzel if (quot && *srcptr == quot) { 340293305Sjpaetzel /* End of the quoted part */ 341293305Sjpaetzel quot = 0; 342293305Sjpaetzel srcptr++; 343293305Sjpaetzel continue; 344293305Sjpaetzel } 345293305Sjpaetzel if (!quot && strchr(delim, *srcptr)) 346293305Sjpaetzel break; 347293305Sjpaetzel *dstptr++ = *srcptr++; 348293305Sjpaetzel } 349293305Sjpaetzel 350293305Sjpaetzel *dstptr = 0; /* Terminate the string */ 351293305Sjpaetzel *stringp = (*srcptr == '\0') ? NULL : srcptr + 1; 352293305Sjpaetzel return (retval); 353293305Sjpaetzel} 354293305Sjpaetzel 355293305Sjpaetzel/* 3561558Srgrimes * Mountd server for NFS mount protocol as described in: 3571558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A 3581558Srgrimes * The optional arguments are the exports file name 3591558Srgrimes * default: _PATH_EXPORTS 3601558Srgrimes * and "-n" to allow nonroot mount. 3611558Srgrimes */ 3621558Srgrimesint 363216587Scharniermain(int argc, char **argv) 3641558Srgrimes{ 36575754Siedowse fd_set readfds; 366172827Smatteo struct netconfig *nconf; 367172827Smatteo char *endptr, **hosts_bak; 368172827Smatteo void *nc_handle; 369149433Spjd pid_t otherpid; 370172827Smatteo in_port_t svcport; 371172827Smatteo int c, k, s; 372109363Smbr int maxrec = RPC_MAXDATASIZE; 373222623Srmacklem int attempt_cnt, port_len, port_pos, ret; 374222623Srmacklem char **port_list; 3751558Srgrimes 37674462Salfred /* Check that another mountd isn't already running. */ 377150214Spjd pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); 378149433Spjd if (pfh == NULL) { 379149433Spjd if (errno == EEXIST) 380149433Spjd errx(1, "mountd already running, pid: %d.", otherpid); 381149433Spjd warn("cannot open or create pidfile"); 382149433Spjd } 38374462Salfred 38474462Salfred s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 38574462Salfred if (s < 0) 38674462Salfred have_v6 = 0; 38774462Salfred else 38874462Salfred close(s); 3892999Swollman 390282214Strasz while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1) 3911558Srgrimes switch (c) { 39225087Sdfr case '2': 39325087Sdfr force_v2 = 1; 39425087Sdfr break; 395192993Srmacklem case 'e': 396220980Srmacklem /* now a no-op, since this is the default */ 397192934Srmacklem break; 3989336Sdfr case 'n': 3999336Sdfr resvport_only = 0; 4009336Sdfr break; 4019336Sdfr case 'r': 4029336Sdfr dir_only = 0; 4039336Sdfr break; 4048688Sphk case 'd': 4058688Sphk debug = debug ? 0 : 1; 4068688Sphk break; 40731656Sguido case 'l': 408121767Speter dolog = 1; 40931656Sguido break; 410126572Sbms case 'p': 411126572Sbms endptr = NULL; 412126572Sbms svcport = (in_port_t)strtoul(optarg, &endptr, 10); 413126572Sbms if (endptr == NULL || *endptr != '\0' || 414126572Sbms svcport == 0 || svcport >= IPPORT_MAX) 415126572Sbms usage(); 416172827Smatteo svcport_str = strdup(optarg); 417126572Sbms break; 418172827Smatteo case 'h': 419172827Smatteo ++nhosts; 420172827Smatteo hosts_bak = hosts; 421172827Smatteo hosts_bak = realloc(hosts, nhosts * sizeof(char *)); 422172827Smatteo if (hosts_bak == NULL) { 423172827Smatteo if (hosts != NULL) { 424172827Smatteo for (k = 0; k < nhosts; k++) 425172827Smatteo free(hosts[k]); 426172827Smatteo free(hosts); 427172827Smatteo out_of_mem(); 428172827Smatteo } 429172827Smatteo } 430172827Smatteo hosts = hosts_bak; 431172827Smatteo hosts[nhosts - 1] = strdup(optarg); 432172827Smatteo if (hosts[nhosts - 1] == NULL) { 433172827Smatteo for (k = 0; k < (nhosts - 1); k++) 434172827Smatteo free(hosts[k]); 435172827Smatteo free(hosts); 436172827Smatteo out_of_mem(); 437172827Smatteo } 438172827Smatteo break; 439241568Srmacklem case 'S': 440241568Srmacklem suspend_nfsd = 1; 441241568Srmacklem break; 4421558Srgrimes default: 44337663Scharnier usage(); 444298089Spfg } 445192934Srmacklem 446282214Strasz if (modfind("nfsd") < 0) { 447192934Srmacklem /* Not present in kernel, try loading it */ 448282214Strasz if (kldload("nfsd") < 0 || modfind("nfsd") < 0) 449192934Srmacklem errx(1, "NFS server is not available"); 450192934Srmacklem } 451192934Srmacklem 4521558Srgrimes argc -= optind; 4531558Srgrimes argv += optind; 4541558Srgrimes grphead = (struct grouplist *)NULL; 455166440Spjd if (argc > 0) 456166440Spjd exnames = argv; 457166440Spjd else 458166440Spjd exnames = exnames_default; 4591558Srgrimes openlog("mountd", LOG_PID, LOG_DAEMON); 4601558Srgrimes if (debug) 46137663Scharnier warnx("getting export list"); 4621558Srgrimes get_exportlist(); 4631558Srgrimes if (debug) 46437663Scharnier warnx("getting mount list"); 4651558Srgrimes get_mountlist(); 4661558Srgrimes if (debug) 46737663Scharnier warnx("here we go"); 4681558Srgrimes if (debug == 0) { 4691558Srgrimes daemon(0, 0); 4701558Srgrimes signal(SIGINT, SIG_IGN); 4711558Srgrimes signal(SIGQUIT, SIG_IGN); 4721558Srgrimes } 47375754Siedowse signal(SIGHUP, huphandler); 47474462Salfred signal(SIGTERM, terminate); 475164394Srodrigc signal(SIGPIPE, SIG_IGN); 476149433Spjd 477149433Spjd pidfile_write(pfh); 478149433Spjd 479194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 480194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 481109363Smbr rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 482109363Smbr 48324759Sguido if (!resvport_only) { 484308449Srmacklem if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL, 48583687Speter &resvport_only, sizeof(resvport_only)) != 0 && 48683687Speter errno != ENOENT) { 48724759Sguido syslog(LOG_ERR, "sysctl: %m"); 48824759Sguido exit(1); 48924759Sguido } 49024330Sguido } 491126572Sbms 492172827Smatteo /* 493172827Smatteo * If no hosts were specified, add a wildcard entry to bind to 494172827Smatteo * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the 495172827Smatteo * list. 496172827Smatteo */ 497172827Smatteo if (nhosts == 0) { 498292864Suqs hosts = malloc(sizeof(char *)); 499172827Smatteo if (hosts == NULL) 500172827Smatteo out_of_mem(); 501172827Smatteo hosts[0] = "*"; 502172827Smatteo nhosts = 1; 503172827Smatteo } else { 504172827Smatteo hosts_bak = hosts; 505172827Smatteo if (have_v6) { 506172827Smatteo hosts_bak = realloc(hosts, (nhosts + 2) * 507172827Smatteo sizeof(char *)); 508172827Smatteo if (hosts_bak == NULL) { 509172827Smatteo for (k = 0; k < nhosts; k++) 510172827Smatteo free(hosts[k]); 511172827Smatteo free(hosts); 512172827Smatteo out_of_mem(); 513172827Smatteo } else 514172827Smatteo hosts = hosts_bak; 515172827Smatteo nhosts += 2; 516172827Smatteo hosts[nhosts - 2] = "::1"; 517172827Smatteo } else { 518172827Smatteo hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); 519172827Smatteo if (hosts_bak == NULL) { 520172827Smatteo for (k = 0; k < nhosts; k++) 521172827Smatteo free(hosts[k]); 522172827Smatteo free(hosts); 523172827Smatteo out_of_mem(); 524172827Smatteo } else { 525172827Smatteo nhosts += 1; 526172827Smatteo hosts = hosts_bak; 527126572Sbms } 528172827Smatteo } 52974462Salfred 530172827Smatteo hosts[nhosts - 1] = "127.0.0.1"; 53174462Salfred } 53274462Salfred 533222623Srmacklem attempt_cnt = 1; 534222623Srmacklem sock_fdcnt = 0; 535222623Srmacklem sock_fd = NULL; 536222623Srmacklem port_list = NULL; 537222623Srmacklem port_len = 0; 538172827Smatteo nc_handle = setnetconfig(); 539172827Smatteo while ((nconf = getnetconfig(nc_handle))) { 540172827Smatteo if (nconf->nc_flag & NC_VISIBLE) { 541172827Smatteo if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 542172827Smatteo "inet6") == 0) { 543172827Smatteo /* DO NOTHING */ 544222623Srmacklem } else { 545222623Srmacklem ret = create_service(nconf); 546222623Srmacklem if (ret == 1) 547222623Srmacklem /* Ignore this call */ 548222623Srmacklem continue; 549222623Srmacklem if (ret < 0) { 550222623Srmacklem /* 551222623Srmacklem * Failed to bind port, so close off 552222623Srmacklem * all sockets created and try again 553222623Srmacklem * if the port# was dynamically 554222623Srmacklem * assigned via bind(2). 555222623Srmacklem */ 556222623Srmacklem clearout_service(); 557222623Srmacklem if (mallocd_svcport != 0 && 558222623Srmacklem attempt_cnt < GETPORT_MAXTRY) { 559222623Srmacklem free(svcport_str); 560222623Srmacklem svcport_str = NULL; 561222623Srmacklem mallocd_svcport = 0; 562222623Srmacklem } else { 563222623Srmacklem errno = EADDRINUSE; 564222623Srmacklem syslog(LOG_ERR, 565222623Srmacklem "bindresvport_sa: %m"); 566222623Srmacklem exit(1); 567222623Srmacklem } 568222623Srmacklem 569222623Srmacklem /* Start over at the first service. */ 570222623Srmacklem free(sock_fd); 571222623Srmacklem sock_fdcnt = 0; 572222623Srmacklem sock_fd = NULL; 573222623Srmacklem nc_handle = setnetconfig(); 574222623Srmacklem attempt_cnt++; 575222623Srmacklem } else if (mallocd_svcport != 0 && 576222623Srmacklem attempt_cnt == GETPORT_MAXTRY) { 577222623Srmacklem /* 578222623Srmacklem * For the last attempt, allow 579222623Srmacklem * different port #s for each nconf 580222623Srmacklem * by saving the svcport_str and 581222623Srmacklem * setting it back to NULL. 582222623Srmacklem */ 583222623Srmacklem port_list = realloc(port_list, 584222623Srmacklem (port_len + 1) * sizeof(char *)); 585222623Srmacklem if (port_list == NULL) 586222623Srmacklem out_of_mem(); 587222623Srmacklem port_list[port_len++] = svcport_str; 588222623Srmacklem svcport_str = NULL; 589222623Srmacklem mallocd_svcport = 0; 590222623Srmacklem } 591222623Srmacklem } 592222623Srmacklem } 593222623Srmacklem } 594222623Srmacklem 595222623Srmacklem /* 596222623Srmacklem * Successfully bound the ports, so call complete_service() to 597222623Srmacklem * do the rest of the setup on the service(s). 598222623Srmacklem */ 599222623Srmacklem sock_fdpos = 0; 600222623Srmacklem port_pos = 0; 601222623Srmacklem nc_handle = setnetconfig(); 602222623Srmacklem while ((nconf = getnetconfig(nc_handle))) { 603222623Srmacklem if (nconf->nc_flag & NC_VISIBLE) { 604222623Srmacklem if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 605222623Srmacklem "inet6") == 0) { 606222623Srmacklem /* DO NOTHING */ 607222623Srmacklem } else if (port_list != NULL) { 608222623Srmacklem if (port_pos >= port_len) { 609222623Srmacklem syslog(LOG_ERR, "too many port#s"); 610222623Srmacklem exit(1); 611222623Srmacklem } 612222623Srmacklem complete_service(nconf, port_list[port_pos++]); 613172827Smatteo } else 614222623Srmacklem complete_service(nconf, svcport_str); 615172827Smatteo } 61674462Salfred } 617172827Smatteo endnetconfig(nc_handle); 618222623Srmacklem free(sock_fd); 619222623Srmacklem if (port_list != NULL) { 620222623Srmacklem for (port_pos = 0; port_pos < port_len; port_pos++) 621222623Srmacklem free(port_list[port_pos]); 622222623Srmacklem free(port_list); 623222623Srmacklem } 62474462Salfred 62574462Salfred if (xcreated == 0) { 62674462Salfred syslog(LOG_ERR, "could not create any services"); 6271558Srgrimes exit(1); 6281558Srgrimes } 62975754Siedowse 63075754Siedowse /* Expand svc_run() here so that we can call get_exportlist(). */ 63175754Siedowse for (;;) { 63275754Siedowse if (got_sighup) { 63375754Siedowse get_exportlist(); 63475754Siedowse got_sighup = 0; 63575754Siedowse } 63675754Siedowse readfds = svc_fdset; 63775754Siedowse switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) { 63875754Siedowse case -1: 63975754Siedowse if (errno == EINTR) 64075754Siedowse continue; 64175754Siedowse syslog(LOG_ERR, "mountd died: select: %m"); 64275754Siedowse exit(1); 64375754Siedowse case 0: 64475754Siedowse continue; 64575754Siedowse default: 64675754Siedowse svc_getreqset(&readfds); 64775754Siedowse } 64875754Siedowse } 649172827Smatteo} 650172827Smatteo 651172827Smatteo/* 652172827Smatteo * This routine creates and binds sockets on the appropriate 653222623Srmacklem * addresses. It gets called one time for each transport. 654222623Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate 655222623Srmacklem * bind failed with EADDRINUSE. 656222623Srmacklem * Any file descriptors that have been created are stored in sock_fd and 657222623Srmacklem * the total count of them is maintained in sock_fdcnt. 658172827Smatteo */ 659222623Srmacklemstatic int 660172827Smatteocreate_service(struct netconfig *nconf) 661172827Smatteo{ 662172827Smatteo struct addrinfo hints, *res = NULL; 663172827Smatteo struct sockaddr_in *sin; 664172827Smatteo struct sockaddr_in6 *sin6; 665172827Smatteo struct __rpc_sockinfo si; 666172827Smatteo int aicode; 667172827Smatteo int fd; 668172827Smatteo int nhostsbak; 669172827Smatteo int one = 1; 670172827Smatteo int r; 671172827Smatteo u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 672222623Srmacklem int mallocd_res; 673172827Smatteo 674172827Smatteo if ((nconf->nc_semantics != NC_TPI_CLTS) && 675172827Smatteo (nconf->nc_semantics != NC_TPI_COTS) && 676172827Smatteo (nconf->nc_semantics != NC_TPI_COTS_ORD)) 677222623Srmacklem return (1); /* not my type */ 678172827Smatteo 679172827Smatteo /* 680172827Smatteo * XXX - using RPC library internal functions. 681172827Smatteo */ 682172827Smatteo if (!__rpc_nconf2sockinfo(nconf, &si)) { 683172827Smatteo syslog(LOG_ERR, "cannot get information for %s", 684172827Smatteo nconf->nc_netid); 685222623Srmacklem return (1); 686172827Smatteo } 687172827Smatteo 688172827Smatteo /* Get mountd's address on this transport */ 689172827Smatteo memset(&hints, 0, sizeof hints); 690172827Smatteo hints.ai_family = si.si_af; 691172827Smatteo hints.ai_socktype = si.si_socktype; 692172827Smatteo hints.ai_protocol = si.si_proto; 693172827Smatteo 694172827Smatteo /* 695172827Smatteo * Bind to specific IPs if asked to 696172827Smatteo */ 697172827Smatteo nhostsbak = nhosts; 698172827Smatteo while (nhostsbak > 0) { 699172827Smatteo --nhostsbak; 700222623Srmacklem sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int)); 701222623Srmacklem if (sock_fd == NULL) 702222623Srmacklem out_of_mem(); 703222623Srmacklem sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */ 704222623Srmacklem mallocd_res = 0; 705222623Srmacklem 706277352Srstone hints.ai_flags = AI_PASSIVE; 707277352Srstone 708172827Smatteo /* 709172827Smatteo * XXX - using RPC library internal functions. 710172827Smatteo */ 711172827Smatteo if ((fd = __rpc_nconf2fd(nconf)) < 0) { 712172827Smatteo int non_fatal = 0; 713244538Skevlo if (errno == EAFNOSUPPORT && 714172827Smatteo nconf->nc_semantics != NC_TPI_CLTS) 715172827Smatteo non_fatal = 1; 716172827Smatteo 717172827Smatteo syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 718172827Smatteo "cannot create socket for %s", nconf->nc_netid); 719222623Srmacklem if (non_fatal != 0) 720222623Srmacklem continue; 721222623Srmacklem exit(1); 722172827Smatteo } 723172827Smatteo 724172827Smatteo switch (hints.ai_family) { 725172827Smatteo case AF_INET: 726172827Smatteo if (inet_pton(AF_INET, hosts[nhostsbak], 727172827Smatteo host_addr) == 1) { 728222623Srmacklem hints.ai_flags |= AI_NUMERICHOST; 729172827Smatteo } else { 730172827Smatteo /* 731172827Smatteo * Skip if we have an AF_INET6 address. 732172827Smatteo */ 733172827Smatteo if (inet_pton(AF_INET6, hosts[nhostsbak], 734172827Smatteo host_addr) == 1) { 735172827Smatteo close(fd); 736172827Smatteo continue; 737172827Smatteo } 738172827Smatteo } 739172827Smatteo break; 740172827Smatteo case AF_INET6: 741172827Smatteo if (inet_pton(AF_INET6, hosts[nhostsbak], 742172827Smatteo host_addr) == 1) { 743222623Srmacklem hints.ai_flags |= AI_NUMERICHOST; 744172827Smatteo } else { 745172827Smatteo /* 746172827Smatteo * Skip if we have an AF_INET address. 747172827Smatteo */ 748172827Smatteo if (inet_pton(AF_INET, hosts[nhostsbak], 749172827Smatteo host_addr) == 1) { 750172827Smatteo close(fd); 751172827Smatteo continue; 752172827Smatteo } 753172827Smatteo } 754172827Smatteo 755172827Smatteo /* 756172827Smatteo * We're doing host-based access checks here, so don't 757172827Smatteo * allow v4-in-v6 to confuse things. The kernel will 758172827Smatteo * disable it by default on NFS sockets too. 759172827Smatteo */ 760172827Smatteo if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, 761172827Smatteo sizeof one) < 0) { 762172827Smatteo syslog(LOG_ERR, 763172827Smatteo "can't disable v4-in-v6 on IPv6 socket"); 764172827Smatteo exit(1); 765172827Smatteo } 766172827Smatteo break; 767172827Smatteo default: 768172827Smatteo break; 769172827Smatteo } 770172827Smatteo 771172827Smatteo /* 772172827Smatteo * If no hosts were specified, just bind to INADDR_ANY 773172827Smatteo */ 774172827Smatteo if (strcmp("*", hosts[nhostsbak]) == 0) { 775172827Smatteo if (svcport_str == NULL) { 776172827Smatteo res = malloc(sizeof(struct addrinfo)); 777172827Smatteo if (res == NULL) 778172827Smatteo out_of_mem(); 779222623Srmacklem mallocd_res = 1; 780172827Smatteo res->ai_flags = hints.ai_flags; 781172827Smatteo res->ai_family = hints.ai_family; 782172827Smatteo res->ai_protocol = hints.ai_protocol; 783172827Smatteo switch (res->ai_family) { 784172827Smatteo case AF_INET: 785172827Smatteo sin = malloc(sizeof(struct sockaddr_in)); 786172827Smatteo if (sin == NULL) 787172827Smatteo out_of_mem(); 788172827Smatteo sin->sin_family = AF_INET; 789172827Smatteo sin->sin_port = htons(0); 790172827Smatteo sin->sin_addr.s_addr = htonl(INADDR_ANY); 791172827Smatteo res->ai_addr = (struct sockaddr*) sin; 792172827Smatteo res->ai_addrlen = (socklen_t) 793222623Srmacklem sizeof(struct sockaddr_in); 794172827Smatteo break; 795172827Smatteo case AF_INET6: 796172827Smatteo sin6 = malloc(sizeof(struct sockaddr_in6)); 797173056Ssimon if (sin6 == NULL) 798172827Smatteo out_of_mem(); 799172827Smatteo sin6->sin6_family = AF_INET6; 800172827Smatteo sin6->sin6_port = htons(0); 801172827Smatteo sin6->sin6_addr = in6addr_any; 802172827Smatteo res->ai_addr = (struct sockaddr*) sin6; 803172827Smatteo res->ai_addrlen = (socklen_t) 804222623Srmacklem sizeof(struct sockaddr_in6); 805222623Srmacklem break; 806172827Smatteo default: 807222623Srmacklem syslog(LOG_ERR, "bad addr fam %d", 808222623Srmacklem res->ai_family); 809222623Srmacklem exit(1); 810172827Smatteo } 811172827Smatteo } else { 812172827Smatteo if ((aicode = getaddrinfo(NULL, svcport_str, 813172827Smatteo &hints, &res)) != 0) { 814172827Smatteo syslog(LOG_ERR, 815172827Smatteo "cannot get local address for %s: %s", 816172827Smatteo nconf->nc_netid, 817172827Smatteo gai_strerror(aicode)); 818222623Srmacklem close(fd); 819172827Smatteo continue; 820172827Smatteo } 821172827Smatteo } 822172827Smatteo } else { 823172827Smatteo if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, 824172827Smatteo &hints, &res)) != 0) { 825172827Smatteo syslog(LOG_ERR, 826172827Smatteo "cannot get local address for %s: %s", 827172827Smatteo nconf->nc_netid, gai_strerror(aicode)); 828222623Srmacklem close(fd); 829172827Smatteo continue; 830172827Smatteo } 831172827Smatteo } 832172827Smatteo 833222623Srmacklem /* Store the fd. */ 834222623Srmacklem sock_fd[sock_fdcnt - 1] = fd; 835222623Srmacklem 836222623Srmacklem /* Now, attempt the bind. */ 837172827Smatteo r = bindresvport_sa(fd, res->ai_addr); 838172827Smatteo if (r != 0) { 839222623Srmacklem if (errno == EADDRINUSE && mallocd_svcport != 0) { 840222623Srmacklem if (mallocd_res != 0) { 841222623Srmacklem free(res->ai_addr); 842222623Srmacklem free(res); 843222623Srmacklem } else 844222623Srmacklem freeaddrinfo(res); 845222623Srmacklem return (-1); 846222623Srmacklem } 847172827Smatteo syslog(LOG_ERR, "bindresvport_sa: %m"); 848172827Smatteo exit(1); 849172827Smatteo } 850172827Smatteo 851222623Srmacklem if (svcport_str == NULL) { 852222623Srmacklem svcport_str = malloc(NI_MAXSERV * sizeof(char)); 853222623Srmacklem if (svcport_str == NULL) 854222623Srmacklem out_of_mem(); 855222623Srmacklem mallocd_svcport = 1; 856222623Srmacklem 857222623Srmacklem if (getnameinfo(res->ai_addr, 858222623Srmacklem res->ai_addr->sa_len, NULL, NI_MAXHOST, 859222623Srmacklem svcport_str, NI_MAXSERV * sizeof(char), 860222623Srmacklem NI_NUMERICHOST | NI_NUMERICSERV)) 861222623Srmacklem errx(1, "Cannot get port number"); 862222623Srmacklem } 863222623Srmacklem if (mallocd_res != 0) { 864222623Srmacklem free(res->ai_addr); 865222623Srmacklem free(res); 866222623Srmacklem } else 867222623Srmacklem freeaddrinfo(res); 868222623Srmacklem res = NULL; 869222623Srmacklem } 870222623Srmacklem return (0); 871222623Srmacklem} 872222623Srmacklem 873222623Srmacklem/* 874222623Srmacklem * Called after all the create_service() calls have succeeded, to complete 875222623Srmacklem * the setup and registration. 876222623Srmacklem */ 877222623Srmacklemstatic void 878222623Srmacklemcomplete_service(struct netconfig *nconf, char *port_str) 879222623Srmacklem{ 880222623Srmacklem struct addrinfo hints, *res = NULL; 881222623Srmacklem struct __rpc_sockinfo si; 882222623Srmacklem struct netbuf servaddr; 883222623Srmacklem SVCXPRT *transp = NULL; 884222623Srmacklem int aicode, fd, nhostsbak; 885222623Srmacklem int registered = 0; 886222623Srmacklem 887222623Srmacklem if ((nconf->nc_semantics != NC_TPI_CLTS) && 888222623Srmacklem (nconf->nc_semantics != NC_TPI_COTS) && 889222623Srmacklem (nconf->nc_semantics != NC_TPI_COTS_ORD)) 890222623Srmacklem return; /* not my type */ 891222623Srmacklem 892222623Srmacklem /* 893222623Srmacklem * XXX - using RPC library internal functions. 894222623Srmacklem */ 895222623Srmacklem if (!__rpc_nconf2sockinfo(nconf, &si)) { 896222623Srmacklem syslog(LOG_ERR, "cannot get information for %s", 897222623Srmacklem nconf->nc_netid); 898222623Srmacklem return; 899222623Srmacklem } 900222623Srmacklem 901222623Srmacklem nhostsbak = nhosts; 902222623Srmacklem while (nhostsbak > 0) { 903222623Srmacklem --nhostsbak; 904222623Srmacklem if (sock_fdpos >= sock_fdcnt) { 905222623Srmacklem /* Should never happen. */ 906222623Srmacklem syslog(LOG_ERR, "Ran out of socket fd's"); 907222623Srmacklem return; 908222623Srmacklem } 909222623Srmacklem fd = sock_fd[sock_fdpos++]; 910222623Srmacklem if (fd < 0) 911222623Srmacklem continue; 912222623Srmacklem 913341171Ssef /* 914341171Ssef * Using -1 tells listen(2) to use 915341171Ssef * kern.ipc.soacceptqueue for the backlog. 916341171Ssef */ 917172827Smatteo if (nconf->nc_semantics != NC_TPI_CLTS) 918341171Ssef listen(fd, -1); 919172827Smatteo 920172827Smatteo if (nconf->nc_semantics == NC_TPI_CLTS ) 921172827Smatteo transp = svc_dg_create(fd, 0, 0); 922172827Smatteo else 923172827Smatteo transp = svc_vc_create(fd, RPC_MAXDATASIZE, 924172827Smatteo RPC_MAXDATASIZE); 925172827Smatteo 926172827Smatteo if (transp != (SVCXPRT *) NULL) { 927194880Sdfr if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv, 928172827Smatteo NULL)) 929172827Smatteo syslog(LOG_ERR, 930194880Sdfr "can't register %s MOUNTVERS service", 931172827Smatteo nconf->nc_netid); 932172827Smatteo if (!force_v2) { 933194880Sdfr if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3, 934172827Smatteo mntsrv, NULL)) 935172827Smatteo syslog(LOG_ERR, 936194880Sdfr "can't register %s MOUNTVERS3 service", 937172827Smatteo nconf->nc_netid); 938172827Smatteo } 939172827Smatteo } else 940172827Smatteo syslog(LOG_WARNING, "can't create %s services", 941172827Smatteo nconf->nc_netid); 942172827Smatteo 943172827Smatteo if (registered == 0) { 944172827Smatteo registered = 1; 945172827Smatteo memset(&hints, 0, sizeof hints); 946172827Smatteo hints.ai_flags = AI_PASSIVE; 947172827Smatteo hints.ai_family = si.si_af; 948172827Smatteo hints.ai_socktype = si.si_socktype; 949172827Smatteo hints.ai_protocol = si.si_proto; 950172827Smatteo 951222623Srmacklem if ((aicode = getaddrinfo(NULL, port_str, &hints, 952172827Smatteo &res)) != 0) { 953172827Smatteo syslog(LOG_ERR, "cannot get local address: %s", 954172827Smatteo gai_strerror(aicode)); 955172827Smatteo exit(1); 956172827Smatteo } 957172827Smatteo 958172827Smatteo servaddr.buf = malloc(res->ai_addrlen); 959172827Smatteo memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 960172827Smatteo servaddr.len = res->ai_addrlen; 961172827Smatteo 962194880Sdfr rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr); 963194880Sdfr rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr); 964172827Smatteo 965172827Smatteo xcreated++; 966172827Smatteo freeaddrinfo(res); 967172827Smatteo } 968172827Smatteo } /* end while */ 9691558Srgrimes} 9701558Srgrimes 971222623Srmacklem/* 972222623Srmacklem * Clear out sockets after a failure to bind one of them, so that the 973222623Srmacklem * cycle of socket creation/binding can start anew. 974222623Srmacklem */ 97537663Scharnierstatic void 976222623Srmacklemclearout_service(void) 977222623Srmacklem{ 978222623Srmacklem int i; 979222623Srmacklem 980222623Srmacklem for (i = 0; i < sock_fdcnt; i++) { 981222623Srmacklem if (sock_fd[i] >= 0) { 982222623Srmacklem shutdown(sock_fd[i], SHUT_RDWR); 983222623Srmacklem close(sock_fd[i]); 984222623Srmacklem } 985222623Srmacklem } 986222623Srmacklem} 987222623Srmacklem 988222623Srmacklemstatic void 989216587Scharnierusage(void) 99037663Scharnier{ 99137663Scharnier fprintf(stderr, 992192993Srmacklem "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] " 993241568Srmacklem "[-S] [-h <bindip>] [export_file ...]\n"); 99437663Scharnier exit(1); 99537663Scharnier} 99637663Scharnier 9971558Srgrimes/* 9981558Srgrimes * The mount rpc service 9991558Srgrimes */ 10001558Srgrimesvoid 1001216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp) 10021558Srgrimes{ 10031558Srgrimes struct exportlist *ep; 10041558Srgrimes struct dirlist *dp; 10059336Sdfr struct fhreturn fhr; 10061558Srgrimes struct stat stb; 10071558Srgrimes struct statfs fsb; 100874462Salfred char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 100974462Salfred int lookup_failed = 1; 101074462Salfred struct sockaddr *saddr; 10119336Sdfr u_short sport; 1012194880Sdfr char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; 101328911Sguido int bad = 0, defset, hostset; 10149336Sdfr sigset_t sighup_mask; 1015240902Srmacklem int numsecflavors, *secflavorsp; 10161558Srgrimes 10179336Sdfr sigemptyset(&sighup_mask); 10189336Sdfr sigaddset(&sighup_mask, SIGHUP); 101974462Salfred saddr = svc_getrpccaller(transp)->buf; 102074462Salfred switch (saddr->sa_family) { 102174462Salfred case AF_INET6: 102275635Siedowse sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 102374462Salfred break; 102474462Salfred case AF_INET: 102575635Siedowse sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 102674462Salfred break; 102774462Salfred default: 102874462Salfred syslog(LOG_ERR, "request from unknown address family"); 102974462Salfred return; 103074462Salfred } 1031346769Smav switch (rqstp->rq_proc) { 1032346769Smav case MOUNTPROC_MNT: 1033346769Smav case MOUNTPROC_UMNT: 1034346769Smav case MOUNTPROC_UMNTALL: 1035346769Smav lookup_failed = getnameinfo(saddr, saddr->sa_len, host, 1036346769Smav sizeof host, NULL, 0, 0); 1037346769Smav } 103874462Salfred getnameinfo(saddr, saddr->sa_len, numerichost, 103974462Salfred sizeof numerichost, NULL, 0, NI_NUMERICHOST); 10401558Srgrimes switch (rqstp->rq_proc) { 10411558Srgrimes case NULLPROC: 1042121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) 104337663Scharnier syslog(LOG_ERR, "can't send reply"); 10441558Srgrimes return; 1045194880Sdfr case MOUNTPROC_MNT: 10469336Sdfr if (sport >= IPPORT_RESERVED && resvport_only) { 104731656Sguido syslog(LOG_NOTICE, 104831656Sguido "mount request from %s from unprivileged port", 104974462Salfred numerichost); 10501558Srgrimes svcerr_weakauth(transp); 10511558Srgrimes return; 10521558Srgrimes } 1053121556Speter if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 105431656Sguido syslog(LOG_NOTICE, "undecodable mount request from %s", 105574462Salfred numerichost); 10561558Srgrimes svcerr_decode(transp); 10571558Srgrimes return; 10581558Srgrimes } 10591558Srgrimes 10601558Srgrimes /* 10611558Srgrimes * Get the real pathname and make sure it is a directory 10629336Sdfr * or a regular file if the -r option was specified 10639336Sdfr * and it exists. 10641558Srgrimes */ 106551968Salfred if (realpath(rpcpath, dirpath) == NULL || 10661558Srgrimes stat(dirpath, &stb) < 0 || 10671558Srgrimes statfs(dirpath, &fsb) < 0) { 10681558Srgrimes chdir("/"); /* Just in case realpath doesn't */ 106931656Sguido syslog(LOG_NOTICE, 107037663Scharnier "mount request from %s for non existent path %s", 107174462Salfred numerichost, dirpath); 10721558Srgrimes if (debug) 107337663Scharnier warnx("stat failed on %s", dirpath); 107428911Sguido bad = ENOENT; /* We will send error reply later */ 10751558Srgrimes } 1076330092Srpokala if (!bad && 1077330092Srpokala !S_ISDIR(stb.st_mode) && 1078330092Srpokala (dir_only || !S_ISREG(stb.st_mode))) { 1079330092Srpokala syslog(LOG_NOTICE, 1080330092Srpokala "mount request from %s for non-directory path %s", 1081330092Srpokala numerichost, dirpath); 1082330092Srpokala if (debug) 1083330092Srpokala warnx("mounting non-directory %s", dirpath); 1084330092Srpokala bad = ENOTDIR; /* We will send error reply later */ 1085330092Srpokala } 10861558Srgrimes 10871558Srgrimes /* Check in the exports list */ 10889336Sdfr sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1089330092Srpokala if (bad) 1090330092Srpokala ep = NULL; 1091330092Srpokala else 1092349124Srmacklem ep = ex_search(&fsb.f_fsid, &exphead); 10939336Sdfr hostset = defset = 0; 1094240902Srmacklem if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, 1095240902Srmacklem &numsecflavors, &secflavorsp) || 10961558Srgrimes ((dp = dirp_search(ep->ex_dirl, dirpath)) && 1097240902Srmacklem chk_host(dp, saddr, &defset, &hostset, &numsecflavors, 1098240902Srmacklem &secflavorsp)) || 109974462Salfred (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 110074462Salfred scan_tree(ep->ex_dirl, saddr) == 0))) { 110128911Sguido if (bad) { 1102121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 110328911Sguido (caddr_t)&bad)) 110437663Scharnier syslog(LOG_ERR, "can't send reply"); 110528911Sguido sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 110628911Sguido return; 110728911Sguido } 1108240902Srmacklem if (hostset & DP_HOSTSET) { 11099336Sdfr fhr.fhr_flag = hostset; 1110240902Srmacklem fhr.fhr_numsecflavors = numsecflavors; 1111240902Srmacklem fhr.fhr_secflavors = secflavorsp; 1112240902Srmacklem } else { 11139336Sdfr fhr.fhr_flag = defset; 1114240902Srmacklem fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; 1115240902Srmacklem fhr.fhr_secflavors = ep->ex_defsecflavors; 1116240902Srmacklem } 11179336Sdfr fhr.fhr_vers = rqstp->rq_vers; 11181558Srgrimes /* Get the file handle */ 111923681Speter memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 11209336Sdfr if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 11211558Srgrimes bad = errno; 112237663Scharnier syslog(LOG_ERR, "can't get fh for %s", dirpath); 1123121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 11241558Srgrimes (caddr_t)&bad)) 112537663Scharnier syslog(LOG_ERR, "can't send reply"); 11269336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 11271558Srgrimes return; 11281558Srgrimes } 1129121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, 1130121556Speter (caddr_t)&fhr)) 113137663Scharnier syslog(LOG_ERR, "can't send reply"); 113274462Salfred if (!lookup_failed) 113374462Salfred add_mlist(host, dirpath); 11341558Srgrimes else 113574462Salfred add_mlist(numerichost, dirpath); 11361558Srgrimes if (debug) 113737663Scharnier warnx("mount successful"); 1138121767Speter if (dolog) 113931656Sguido syslog(LOG_NOTICE, 114031656Sguido "mount request succeeded from %s for %s", 114174462Salfred numerichost, dirpath); 114231656Sguido } else { 1143330092Srpokala if (!bad) 1144330092Srpokala bad = EACCES; 114531656Sguido syslog(LOG_NOTICE, 114631656Sguido "mount request denied from %s for %s", 114774462Salfred numerichost, dirpath); 114831656Sguido } 114928911Sguido 1150121556Speter if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, 1151121556Speter (caddr_t)&bad)) 115237663Scharnier syslog(LOG_ERR, "can't send reply"); 11539336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 11541558Srgrimes return; 1155194880Sdfr case MOUNTPROC_DUMP: 1156121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) 115737663Scharnier syslog(LOG_ERR, "can't send reply"); 1158121767Speter else if (dolog) 115931656Sguido syslog(LOG_NOTICE, 116031656Sguido "dump request succeeded from %s", 116174462Salfred numerichost); 11621558Srgrimes return; 1163194880Sdfr case MOUNTPROC_UMNT: 11649336Sdfr if (sport >= IPPORT_RESERVED && resvport_only) { 116531656Sguido syslog(LOG_NOTICE, 116631656Sguido "umount request from %s from unprivileged port", 116774462Salfred numerichost); 11681558Srgrimes svcerr_weakauth(transp); 11691558Srgrimes return; 11701558Srgrimes } 1171121556Speter if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 117231656Sguido syslog(LOG_NOTICE, "undecodable umount request from %s", 117374462Salfred numerichost); 11741558Srgrimes svcerr_decode(transp); 11751558Srgrimes return; 11761558Srgrimes } 117751968Salfred if (realpath(rpcpath, dirpath) == NULL) { 117851968Salfred syslog(LOG_NOTICE, "umount request from %s " 117951968Salfred "for non existent path %s", 118074462Salfred numerichost, dirpath); 118151968Salfred } 1182121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 118337663Scharnier syslog(LOG_ERR, "can't send reply"); 118474462Salfred if (!lookup_failed) 118575635Siedowse del_mlist(host, dirpath); 118675635Siedowse del_mlist(numerichost, dirpath); 1187121767Speter if (dolog) 118831656Sguido syslog(LOG_NOTICE, 118931656Sguido "umount request succeeded from %s for %s", 119074462Salfred numerichost, dirpath); 11911558Srgrimes return; 1192194880Sdfr case MOUNTPROC_UMNTALL: 11939336Sdfr if (sport >= IPPORT_RESERVED && resvport_only) { 119431656Sguido syslog(LOG_NOTICE, 119531656Sguido "umountall request from %s from unprivileged port", 119674462Salfred numerichost); 11971558Srgrimes svcerr_weakauth(transp); 11981558Srgrimes return; 11991558Srgrimes } 1200121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 120137663Scharnier syslog(LOG_ERR, "can't send reply"); 120274462Salfred if (!lookup_failed) 120375635Siedowse del_mlist(host, NULL); 120475635Siedowse del_mlist(numerichost, NULL); 1205121767Speter if (dolog) 120631656Sguido syslog(LOG_NOTICE, 120731656Sguido "umountall request succeeded from %s", 120874462Salfred numerichost); 12091558Srgrimes return; 1210194880Sdfr case MOUNTPROC_EXPORT: 1211121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) 1212121556Speter if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, 1213121556Speter (caddr_t)NULL)) 1214100117Salfred syslog(LOG_ERR, "can't send reply"); 1215121767Speter if (dolog) 121631656Sguido syslog(LOG_NOTICE, 121731656Sguido "export request succeeded from %s", 121874462Salfred numerichost); 12191558Srgrimes return; 12201558Srgrimes default: 12211558Srgrimes svcerr_noproc(transp); 12221558Srgrimes return; 12231558Srgrimes } 12241558Srgrimes} 12251558Srgrimes 12261558Srgrimes/* 12271558Srgrimes * Xdr conversion for a dirpath string 12281558Srgrimes */ 1229285128Straszstatic int 1230216587Scharnierxdr_dir(XDR *xdrsp, char *dirp) 12311558Srgrimes{ 1232194880Sdfr return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 12331558Srgrimes} 12341558Srgrimes 12351558Srgrimes/* 12369336Sdfr * Xdr routine to generate file handle reply 12371558Srgrimes */ 1238285128Straszstatic int 1239216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp) 12401558Srgrimes{ 124192806Sobrien struct fhreturn *fhrp = (struct fhreturn *)cp; 12429336Sdfr u_long ok = 0, len, auth; 1243184588Sdfr int i; 12441558Srgrimes 12451558Srgrimes if (!xdr_long(xdrsp, &ok)) 12461558Srgrimes return (0); 12479336Sdfr switch (fhrp->fhr_vers) { 12489336Sdfr case 1: 12499336Sdfr return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 12509336Sdfr case 3: 12519336Sdfr len = NFSX_V3FH; 12529336Sdfr if (!xdr_long(xdrsp, &len)) 12539336Sdfr return (0); 12549336Sdfr if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 12559336Sdfr return (0); 1256184588Sdfr if (fhrp->fhr_numsecflavors) { 1257184588Sdfr if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors)) 1258184588Sdfr return (0); 1259184588Sdfr for (i = 0; i < fhrp->fhr_numsecflavors; i++) 1260184588Sdfr if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i])) 1261184588Sdfr return (0); 1262184588Sdfr return (1); 1263184588Sdfr } else { 1264184588Sdfr auth = AUTH_SYS; 1265184588Sdfr len = 1; 1266184588Sdfr if (!xdr_long(xdrsp, &len)) 1267184588Sdfr return (0); 1268184588Sdfr return (xdr_long(xdrsp, &auth)); 1269184588Sdfr } 1270298089Spfg } 12719336Sdfr return (0); 12721558Srgrimes} 12731558Srgrimes 1274285128Straszstatic int 1275216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused) 12761558Srgrimes{ 12771558Srgrimes struct mountlist *mlp; 12781558Srgrimes int true = 1; 12791558Srgrimes int false = 0; 12801558Srgrimes char *strp; 12811558Srgrimes 1282324955Smanu SLIST_FOREACH(mlp, &mlhead, next) { 12831558Srgrimes if (!xdr_bool(xdrsp, &true)) 12841558Srgrimes return (0); 12851558Srgrimes strp = &mlp->ml_host[0]; 1286194880Sdfr if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) 12871558Srgrimes return (0); 12881558Srgrimes strp = &mlp->ml_dirp[0]; 1289194880Sdfr if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 12901558Srgrimes return (0); 12911558Srgrimes } 12921558Srgrimes if (!xdr_bool(xdrsp, &false)) 12931558Srgrimes return (0); 12941558Srgrimes return (1); 12951558Srgrimes} 12961558Srgrimes 12971558Srgrimes/* 12981558Srgrimes * Xdr conversion for export list 12991558Srgrimes */ 1300285128Straszstatic int 1301216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief) 13021558Srgrimes{ 13031558Srgrimes struct exportlist *ep; 13041558Srgrimes int false = 0; 13059336Sdfr int putdef; 13069336Sdfr sigset_t sighup_mask; 13071558Srgrimes 13089336Sdfr sigemptyset(&sighup_mask); 13099336Sdfr sigaddset(&sighup_mask, SIGHUP); 13109336Sdfr sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1311324955Smanu 1312324955Smanu SLIST_FOREACH(ep, &exphead, entries) { 13131558Srgrimes putdef = 0; 1314100117Salfred if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, 1315100117Salfred &putdef, brief)) 13161558Srgrimes goto errout; 13171558Srgrimes if (ep->ex_defdir && putdef == 0 && 13181558Srgrimes put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, 1319100117Salfred &putdef, brief)) 13201558Srgrimes goto errout; 13211558Srgrimes } 13229336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 13231558Srgrimes if (!xdr_bool(xdrsp, &false)) 13241558Srgrimes return (0); 13251558Srgrimes return (1); 13261558Srgrimeserrout: 13279336Sdfr sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 13281558Srgrimes return (0); 13291558Srgrimes} 13301558Srgrimes 13311558Srgrimes/* 13321558Srgrimes * Called from xdr_explist() to traverse the tree and export the 13331558Srgrimes * directory paths. 13341558Srgrimes */ 1335285128Straszstatic int 1336216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp, 1337216587Scharnier int brief) 13381558Srgrimes{ 13391558Srgrimes struct grouplist *grp; 13401558Srgrimes struct hostlist *hp; 13411558Srgrimes int true = 1; 13421558Srgrimes int false = 0; 13431558Srgrimes int gotalldir = 0; 13441558Srgrimes char *strp; 13451558Srgrimes 13461558Srgrimes if (dp) { 1347100117Salfred if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) 13481558Srgrimes return (1); 13491558Srgrimes if (!xdr_bool(xdrsp, &true)) 13501558Srgrimes return (1); 13511558Srgrimes strp = dp->dp_dirp; 1352194880Sdfr if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 13531558Srgrimes return (1); 13541558Srgrimes if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 13551558Srgrimes gotalldir = 1; 13561558Srgrimes *putdefp = 1; 13571558Srgrimes } 1358100117Salfred if (brief) { 1359100117Salfred if (!xdr_bool(xdrsp, &true)) 1360100117Salfred return (1); 1361100117Salfred strp = "(...)"; 1362194880Sdfr if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1363100117Salfred return (1); 1364100117Salfred } else if ((dp->dp_flag & DP_DEFSET) == 0 && 13651558Srgrimes (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 13661558Srgrimes hp = dp->dp_hosts; 13671558Srgrimes while (hp) { 13681558Srgrimes grp = hp->ht_grp; 13691558Srgrimes if (grp->gr_type == GT_HOST) { 13701558Srgrimes if (!xdr_bool(xdrsp, &true)) 13711558Srgrimes return (1); 137274462Salfred strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 13738871Srgrimes if (!xdr_string(xdrsp, &strp, 1374194880Sdfr MNTNAMLEN)) 13751558Srgrimes return (1); 13761558Srgrimes } else if (grp->gr_type == GT_NET) { 13771558Srgrimes if (!xdr_bool(xdrsp, &true)) 13781558Srgrimes return (1); 13791558Srgrimes strp = grp->gr_ptr.gt_net.nt_name; 13808871Srgrimes if (!xdr_string(xdrsp, &strp, 1381194880Sdfr MNTNAMLEN)) 13821558Srgrimes return (1); 13831558Srgrimes } 13841558Srgrimes hp = hp->ht_next; 13851558Srgrimes if (gotalldir && hp == (struct hostlist *)NULL) { 13861558Srgrimes hp = adp->dp_hosts; 13871558Srgrimes gotalldir = 0; 13881558Srgrimes } 13891558Srgrimes } 13901558Srgrimes } 13911558Srgrimes if (!xdr_bool(xdrsp, &false)) 13921558Srgrimes return (1); 1393100117Salfred if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) 13941558Srgrimes return (1); 13951558Srgrimes } 13961558Srgrimes return (0); 13971558Srgrimes} 13981558Srgrimes 1399285128Straszstatic int 1400216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp) 1401100117Salfred{ 1402100117Salfred 1403100117Salfred return xdr_explist_common(xdrsp, cp, 0); 1404100117Salfred} 1405100117Salfred 1406285128Straszstatic int 1407216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp) 1408100117Salfred{ 1409100117Salfred 1410100117Salfred return xdr_explist_common(xdrsp, cp, 1); 1411100117Salfred} 1412100117Salfred 1413285128Straszstatic char *line; 1414285128Straszstatic size_t linesize; 1415285128Straszstatic FILE *exp_file; 14161558Srgrimes 14171558Srgrimes/* 1418166440Spjd * Get the export list from one, currently open file 14191558Srgrimes */ 1420166440Spjdstatic void 1421216587Scharnierget_exportlist_one(void) 14221558Srgrimes{ 1423324955Smanu struct exportlist *ep; 14241558Srgrimes struct grouplist *grp, *tgrp; 14251558Srgrimes struct dirlist *dirhead; 1426166440Spjd struct statfs fsb; 142772650Sgreen struct xucred anon; 14281558Srgrimes char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 1429166440Spjd int len, has_host, exflags, got_nondir, dirplen, netgrp; 14301558Srgrimes 1431192934Srmacklem v4root_phase = 0; 14321558Srgrimes dirhead = (struct dirlist *)NULL; 14331558Srgrimes while (get_line()) { 14341558Srgrimes if (debug) 143537663Scharnier warnx("got line %s", line); 14361558Srgrimes cp = line; 14371558Srgrimes nextfield(&cp, &endcp); 14381558Srgrimes if (*cp == '#') 14391558Srgrimes goto nextline; 14401558Srgrimes 14411558Srgrimes /* 14421558Srgrimes * Set defaults. 14431558Srgrimes */ 14441558Srgrimes has_host = FALSE; 14451558Srgrimes anon = def_anon; 14461558Srgrimes exflags = MNT_EXPORTED; 14471558Srgrimes got_nondir = 0; 14481558Srgrimes opt_flags = 0; 14491558Srgrimes ep = (struct exportlist *)NULL; 1450192934Srmacklem dirp = NULL; 14511558Srgrimes 14521558Srgrimes /* 1453192934Srmacklem * Handle the V4 root dir. 1454192934Srmacklem */ 1455192934Srmacklem if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') { 1456192934Srmacklem /* 1457192934Srmacklem * V4: just indicates that it is the v4 root point, 1458192934Srmacklem * so skip over that and set v4root_phase. 1459192934Srmacklem */ 1460192934Srmacklem if (v4root_phase > 0) { 1461192934Srmacklem syslog(LOG_ERR, "V4:duplicate line, ignored"); 1462192934Srmacklem goto nextline; 1463192934Srmacklem } 1464192934Srmacklem v4root_phase = 1; 1465192934Srmacklem cp += 3; 1466192934Srmacklem nextfield(&cp, &endcp); 1467192934Srmacklem } 1468192934Srmacklem 1469192934Srmacklem /* 14701558Srgrimes * Create new exports list entry 14711558Srgrimes */ 14721558Srgrimes len = endcp-cp; 14731558Srgrimes tgrp = grp = get_grp(); 14741558Srgrimes while (len > 0) { 1475194880Sdfr if (len > MNTNAMLEN) { 1476329392Sbrd getexp_err(ep, tgrp, "mountpoint too long"); 14771558Srgrimes goto nextline; 14781558Srgrimes } 14791558Srgrimes if (*cp == '-') { 14801558Srgrimes if (ep == (struct exportlist *)NULL) { 1481329392Sbrd getexp_err(ep, tgrp, 1482329392Sbrd "flag before export path definition"); 14831558Srgrimes goto nextline; 14841558Srgrimes } 14851558Srgrimes if (debug) 148637663Scharnier warnx("doing opt %s", cp); 14871558Srgrimes got_nondir = 1; 14881558Srgrimes if (do_opt(&cp, &endcp, ep, grp, &has_host, 14891558Srgrimes &exflags, &anon)) { 1490329392Sbrd getexp_err(ep, tgrp, NULL); 14911558Srgrimes goto nextline; 14921558Srgrimes } 14931558Srgrimes } else if (*cp == '/') { 14941558Srgrimes savedc = *endcp; 14951558Srgrimes *endcp = '\0'; 1496192934Srmacklem if (v4root_phase > 1) { 1497192934Srmacklem if (dirp != NULL) { 1498329392Sbrd getexp_err(ep, tgrp, "Multiple V4 dirs"); 1499192934Srmacklem goto nextline; 1500192934Srmacklem } 1501192934Srmacklem } 15021558Srgrimes if (check_dirpath(cp) && 15031558Srgrimes statfs(cp, &fsb) >= 0) { 1504283008Srmacklem if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0) 1505283008Srmacklem syslog(LOG_ERR, "Warning: exporting of " 1506283008Srmacklem "automounted fs %s not supported", cp); 15071558Srgrimes if (got_nondir) { 1508329392Sbrd getexp_err(ep, tgrp, "dirs must be first"); 15091558Srgrimes goto nextline; 15101558Srgrimes } 1511192934Srmacklem if (v4root_phase == 1) { 1512192934Srmacklem if (dirp != NULL) { 1513329392Sbrd getexp_err(ep, tgrp, "Multiple V4 dirs"); 15141558Srgrimes goto nextline; 15151558Srgrimes } 1516192934Srmacklem if (strlen(v4root_dirpath) == 0) { 1517192934Srmacklem strlcpy(v4root_dirpath, cp, 1518192934Srmacklem sizeof (v4root_dirpath)); 1519192934Srmacklem } else if (strcmp(v4root_dirpath, cp) 1520192934Srmacklem != 0) { 1521192934Srmacklem syslog(LOG_ERR, 1522192934Srmacklem "different V4 dirpath %s", cp); 1523329392Sbrd getexp_err(ep, tgrp, NULL); 1524192934Srmacklem goto nextline; 1525192934Srmacklem } 1526192934Srmacklem dirp = cp; 1527192934Srmacklem v4root_phase = 2; 1528192934Srmacklem got_nondir = 1; 1529192934Srmacklem ep = get_exp(); 15301558Srgrimes } else { 1531192934Srmacklem if (ep) { 1532192934Srmacklem if (ep->ex_fs.val[0] != 1533192934Srmacklem fsb.f_fsid.val[0] || 1534192934Srmacklem ep->ex_fs.val[1] != 1535192934Srmacklem fsb.f_fsid.val[1]) { 1536329392Sbrd getexp_err(ep, tgrp, 1537329392Sbrd "fsid mismatch"); 1538192934Srmacklem goto nextline; 1539192934Srmacklem } 1540192934Srmacklem } else { 1541192934Srmacklem /* 1542192934Srmacklem * See if this directory is already 1543192934Srmacklem * in the list. 1544192934Srmacklem */ 1545349124Srmacklem ep = ex_search(&fsb.f_fsid, &exphead); 1546192934Srmacklem if (ep == (struct exportlist *)NULL) { 1547192934Srmacklem ep = get_exp(); 1548192934Srmacklem ep->ex_fs = fsb.f_fsid; 1549324234Smanu ep->ex_fsdir = strdup(fsb.f_mntonname); 1550324234Smanu if (ep->ex_fsdir == NULL) 1551192934Srmacklem out_of_mem(); 1552192934Srmacklem if (debug) 1553192934Srmacklem warnx( 1554192934Srmacklem "making new ep fs=0x%x,0x%x", 1555192934Srmacklem fsb.f_fsid.val[0], 1556192934Srmacklem fsb.f_fsid.val[1]); 1557192934Srmacklem } else if (debug) 1558192934Srmacklem warnx("found ep fs=0x%x,0x%x", 1559192934Srmacklem fsb.f_fsid.val[0], 1560192934Srmacklem fsb.f_fsid.val[1]); 1561192934Srmacklem } 1562192934Srmacklem 15631558Srgrimes /* 1564192934Srmacklem * Add dirpath to export mount point. 15651558Srgrimes */ 1566192934Srmacklem dirp = add_expdir(&dirhead, cp, len); 1567192934Srmacklem dirplen = len; 15681558Srgrimes } 15691558Srgrimes } else { 1570329392Sbrd getexp_err(ep, tgrp, 1571329392Sbrd "symbolic link in export path or statfs failed"); 15721558Srgrimes goto nextline; 15731558Srgrimes } 15741558Srgrimes *endcp = savedc; 15751558Srgrimes } else { 15761558Srgrimes savedc = *endcp; 15771558Srgrimes *endcp = '\0'; 15781558Srgrimes got_nondir = 1; 15791558Srgrimes if (ep == (struct exportlist *)NULL) { 1580329392Sbrd getexp_err(ep, tgrp, 1581329392Sbrd "host(s) before export path definition"); 15821558Srgrimes goto nextline; 15831558Srgrimes } 15841558Srgrimes 15851558Srgrimes /* 15861558Srgrimes * Get the host or netgroup. 15871558Srgrimes */ 15881558Srgrimes setnetgrent(cp); 15891558Srgrimes netgrp = getnetgrent(&hst, &usr, &dom); 15901558Srgrimes do { 15911558Srgrimes if (has_host) { 15921558Srgrimes grp->gr_next = get_grp(); 15931558Srgrimes grp = grp->gr_next; 15941558Srgrimes } 15951558Srgrimes if (netgrp) { 159637003Sjoerg if (hst == 0) { 159737663Scharnier syslog(LOG_ERR, 159837663Scharnier "null hostname in netgroup %s, skipping", cp); 159937004Sjoerg grp->gr_type = GT_IGNORE; 160037003Sjoerg } else if (get_host(hst, grp, tgrp)) { 160137663Scharnier syslog(LOG_ERR, 160237663Scharnier "bad host %s in netgroup %s, skipping", hst, cp); 160329317Sjlemon grp->gr_type = GT_IGNORE; 16041558Srgrimes } 16057401Swpaul } else if (get_host(cp, grp, tgrp)) { 160637663Scharnier syslog(LOG_ERR, "bad host %s, skipping", cp); 160729317Sjlemon grp->gr_type = GT_IGNORE; 16081558Srgrimes } 16091558Srgrimes has_host = TRUE; 16101558Srgrimes } while (netgrp && getnetgrent(&hst, &usr, &dom)); 16111558Srgrimes endnetgrent(); 16121558Srgrimes *endcp = savedc; 16131558Srgrimes } 16141558Srgrimes cp = endcp; 16151558Srgrimes nextfield(&cp, &endcp); 16161558Srgrimes len = endcp - cp; 16171558Srgrimes } 16181558Srgrimes if (check_options(dirhead)) { 1619329392Sbrd getexp_err(ep, tgrp, NULL); 16201558Srgrimes goto nextline; 16211558Srgrimes } 16221558Srgrimes if (!has_host) { 162375641Siedowse grp->gr_type = GT_DEFAULT; 16241558Srgrimes if (debug) 162537663Scharnier warnx("adding a default entry"); 16261558Srgrimes 16271558Srgrimes /* 16281558Srgrimes * Don't allow a network export coincide with a list of 16291558Srgrimes * host(s) on the same line. 16301558Srgrimes */ 16311558Srgrimes } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1632329392Sbrd getexp_err(ep, tgrp, "network/host conflict"); 16331558Srgrimes goto nextline; 163429317Sjlemon 163574462Salfred /* 163674462Salfred * If an export list was specified on this line, make sure 163729317Sjlemon * that we have at least one valid entry, otherwise skip it. 163829317Sjlemon */ 163929317Sjlemon } else { 164029317Sjlemon grp = tgrp; 164174462Salfred while (grp && grp->gr_type == GT_IGNORE) 164229317Sjlemon grp = grp->gr_next; 164329317Sjlemon if (! grp) { 1644329392Sbrd getexp_err(ep, tgrp, "no valid entries"); 164529317Sjlemon goto nextline; 164629317Sjlemon } 16471558Srgrimes } 16481558Srgrimes 1649192934Srmacklem if (v4root_phase == 1) { 1650329392Sbrd getexp_err(ep, tgrp, "V4:root, no dirp, ignored"); 1651192934Srmacklem goto nextline; 1652192934Srmacklem } 1653192934Srmacklem 16541558Srgrimes /* 16551558Srgrimes * Loop through hosts, pushing the exports into the kernel. 16561558Srgrimes * After loop, tgrp points to the start of the list and 16571558Srgrimes * grp points to the last entry in the list. 16581558Srgrimes */ 16591558Srgrimes grp = tgrp; 16601558Srgrimes do { 166175635Siedowse if (do_mount(ep, grp, exflags, &anon, dirp, dirplen, 166275635Siedowse &fsb)) { 1663329392Sbrd getexp_err(ep, tgrp, NULL); 166475635Siedowse goto nextline; 166575635Siedowse } 16661558Srgrimes } while (grp->gr_next && (grp = grp->gr_next)); 16671558Srgrimes 16681558Srgrimes /* 1669192934Srmacklem * For V4: don't enter in mount lists. 1670192934Srmacklem */ 1671194773Srmacklem if (v4root_phase > 0 && v4root_phase <= 2) { 1672194773Srmacklem /* 1673194773Srmacklem * Since these structures aren't used by mountd, 1674194773Srmacklem * free them up now. 1675194773Srmacklem */ 1676194773Srmacklem if (ep != NULL) 1677194773Srmacklem free_exp(ep); 1678194773Srmacklem while (tgrp != NULL) { 1679194773Srmacklem grp = tgrp; 1680194773Srmacklem tgrp = tgrp->gr_next; 1681194773Srmacklem free_grp(grp); 1682194773Srmacklem } 1683192934Srmacklem goto nextline; 1684194773Srmacklem } 1685192934Srmacklem 1686192934Srmacklem /* 16871558Srgrimes * Success. Update the data structures. 16881558Srgrimes */ 16891558Srgrimes if (has_host) { 16909336Sdfr hang_dirp(dirhead, tgrp, ep, opt_flags); 16911558Srgrimes grp->gr_next = grphead; 16921558Srgrimes grphead = tgrp; 16931558Srgrimes } else { 16941558Srgrimes hang_dirp(dirhead, (struct grouplist *)NULL, ep, 16959336Sdfr opt_flags); 16961558Srgrimes free_grp(grp); 16971558Srgrimes } 16981558Srgrimes dirhead = (struct dirlist *)NULL; 16991558Srgrimes if ((ep->ex_flag & EX_LINKED) == 0) { 1700349124Srmacklem insert_exports(ep, &exphead); 17011558Srgrimes 17021558Srgrimes ep->ex_flag |= EX_LINKED; 17031558Srgrimes } 17041558Srgrimesnextline: 1705192934Srmacklem v4root_phase = 0; 17061558Srgrimes if (dirhead) { 17071558Srgrimes free_dir(dirhead); 17081558Srgrimes dirhead = (struct dirlist *)NULL; 17091558Srgrimes } 17101558Srgrimes } 17111558Srgrimes} 17121558Srgrimes 17131558Srgrimes/* 1714166440Spjd * Get the export list from all specified files 1715166440Spjd */ 1716285128Straszstatic void 1717216587Scharnierget_exportlist(void) 1718166440Spjd{ 1719166440Spjd struct grouplist *grp, *tgrp; 1720166440Spjd struct export_args export; 1721166440Spjd struct iovec *iov; 1722166440Spjd struct statfs *fsp, *mntbufp; 1723166440Spjd struct xvfsconf vfc; 1724166440Spjd char errmsg[255]; 1725230352Seadler int num, i; 1726166440Spjd int iovlen; 1727168684Spjd int done; 1728192934Srmacklem struct nfsex_args eargs; 1729166440Spjd 1730241568Srmacklem if (suspend_nfsd != 0) 1731241568Srmacklem (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); 1732192934Srmacklem v4root_dirpath[0] = '\0'; 1733166440Spjd bzero(&export, sizeof(export)); 1734166440Spjd export.ex_flags = MNT_DELEXPORT; 1735166440Spjd iov = NULL; 1736166440Spjd iovlen = 0; 1737166440Spjd bzero(errmsg, sizeof(errmsg)); 1738166440Spjd 1739166440Spjd /* 1740166440Spjd * First, get rid of the old list 1741166440Spjd */ 1742349124Srmacklem free_exports(&exphead); 1743166440Spjd 1744166440Spjd grp = grphead; 1745166440Spjd while (grp) { 1746166440Spjd tgrp = grp; 1747166440Spjd grp = grp->gr_next; 1748166440Spjd free_grp(tgrp); 1749166440Spjd } 1750166440Spjd grphead = (struct grouplist *)NULL; 1751166440Spjd 1752166440Spjd /* 1753192934Srmacklem * and the old V4 root dir. 1754192934Srmacklem */ 1755192934Srmacklem bzero(&eargs, sizeof (eargs)); 1756192934Srmacklem eargs.export.ex_flags = MNT_DELEXPORT; 1757282214Strasz if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 && 1758192934Srmacklem errno != ENOENT) 1759192934Srmacklem syslog(LOG_ERR, "Can't delete exports for V4:"); 1760192934Srmacklem 1761192934Srmacklem /* 1762192934Srmacklem * and clear flag that notes if a public fh has been exported. 1763192934Srmacklem */ 1764192934Srmacklem has_publicfh = 0; 1765192934Srmacklem 1766192934Srmacklem /* 1767166440Spjd * And delete exports that are in the kernel for all local 1768166440Spjd * filesystems. 1769166440Spjd * XXX: Should know how to handle all local exportable filesystems. 1770166440Spjd */ 1771166440Spjd num = getmntinfo(&mntbufp, MNT_NOWAIT); 1772166440Spjd 1773166440Spjd if (num > 0) { 1774166440Spjd build_iovec(&iov, &iovlen, "fstype", NULL, 0); 1775166440Spjd build_iovec(&iov, &iovlen, "fspath", NULL, 0); 1776166440Spjd build_iovec(&iov, &iovlen, "from", NULL, 0); 1777166440Spjd build_iovec(&iov, &iovlen, "update", NULL, 0); 1778166440Spjd build_iovec(&iov, &iovlen, "export", &export, sizeof(export)); 1779166440Spjd build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 1780166440Spjd } 1781166440Spjd 1782166440Spjd for (i = 0; i < num; i++) { 1783166440Spjd fsp = &mntbufp[i]; 1784166440Spjd if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 1785166440Spjd syslog(LOG_ERR, "getvfsbyname() failed for %s", 1786166440Spjd fsp->f_fstypename); 1787166440Spjd continue; 1788166440Spjd } 1789166440Spjd 1790166440Spjd /* 1791281699Ssjg * We do not need to delete "export" flag from 1792281699Ssjg * filesystems that do not have it set. 1793281699Ssjg */ 1794281699Ssjg if (!(fsp->f_flags & MNT_EXPORTED)) 1795281699Ssjg continue; 1796281699Ssjg /* 1797166440Spjd * Do not delete export for network filesystem by 1798166440Spjd * passing "export" arg to nmount(). 1799166440Spjd * It only makes sense to do this for local filesystems. 1800166440Spjd */ 1801166440Spjd if (vfc.vfc_flags & VFCF_NETWORK) 1802166440Spjd continue; 1803166440Spjd 1804166440Spjd iov[1].iov_base = fsp->f_fstypename; 1805166440Spjd iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 1806166440Spjd iov[3].iov_base = fsp->f_mntonname; 1807166440Spjd iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 1808166440Spjd iov[5].iov_base = fsp->f_mntfromname; 1809166440Spjd iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 1810270183Sbdrewery errmsg[0] = '\0'; 1811166440Spjd 1812278523Skib /* 1813278523Skib * EXDEV is returned when path exists but is not a 1814278523Skib * mount point. May happens if raced with unmount. 1815278523Skib */ 1816166440Spjd if (nmount(iov, iovlen, fsp->f_flags) < 0 && 1817278523Skib errno != ENOENT && errno != ENOTSUP && errno != EXDEV) { 1818166440Spjd syslog(LOG_ERR, 1819166440Spjd "can't delete exports for %s: %m %s", 1820166440Spjd fsp->f_mntonname, errmsg); 1821166440Spjd } 1822166440Spjd } 1823166440Spjd 1824166440Spjd if (iov != NULL) { 1825166440Spjd /* Free strings allocated by strdup() in getmntopts.c */ 1826166440Spjd free(iov[0].iov_base); /* fstype */ 1827166440Spjd free(iov[2].iov_base); /* fspath */ 1828166440Spjd free(iov[4].iov_base); /* from */ 1829166440Spjd free(iov[6].iov_base); /* update */ 1830166440Spjd free(iov[8].iov_base); /* export */ 1831166440Spjd free(iov[10].iov_base); /* errmsg */ 1832166440Spjd 1833166440Spjd /* free iov, allocated by realloc() */ 1834166440Spjd free(iov); 1835166440Spjd iovlen = 0; 1836166440Spjd } 1837166440Spjd 1838166440Spjd /* 1839166440Spjd * Read in the exports file and build the list, calling 1840166440Spjd * nmount() as we go along to push the export rules into the kernel. 1841166440Spjd */ 1842168684Spjd done = 0; 1843166440Spjd for (i = 0; exnames[i] != NULL; i++) { 1844166440Spjd if (debug) 1845166440Spjd warnx("reading exports from %s", exnames[i]); 1846166440Spjd if ((exp_file = fopen(exnames[i], "r")) == NULL) { 1847168684Spjd syslog(LOG_WARNING, "can't open %s", exnames[i]); 1848168684Spjd continue; 1849166440Spjd } 1850166440Spjd get_exportlist_one(); 1851166440Spjd fclose(exp_file); 1852168684Spjd done++; 1853166440Spjd } 1854168684Spjd if (done == 0) { 1855168684Spjd syslog(LOG_ERR, "can't open any exports file"); 1856168684Spjd exit(2); 1857168684Spjd } 1858192934Srmacklem 1859192934Srmacklem /* 1860192934Srmacklem * If there was no public fh, clear any previous one set. 1861192934Srmacklem */ 1862282214Strasz if (has_publicfh == 0) 1863192934Srmacklem (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); 1864241568Srmacklem 1865241568Srmacklem /* Resume the nfsd. If they weren't suspended, this is harmless. */ 1866241568Srmacklem (void)nfssvc(NFSSVC_RESUMENFSD, NULL); 1867166440Spjd} 1868166440Spjd 1869166440Spjd/* 1870349124Srmacklem * Insert an export entry in the appropriate list. 1871349124Srmacklem */ 1872349124Srmacklemstatic void 1873349124Srmackleminsert_exports(struct exportlist *ep, struct exportlisthead *exhp) 1874349124Srmacklem{ 1875349124Srmacklem 1876349124Srmacklem SLIST_INSERT_HEAD(exhp, ep, entries); 1877349124Srmacklem} 1878349124Srmacklem 1879349124Srmacklem/* 1880349124Srmacklem * Free up the exports lists passed in as arguments. 1881349124Srmacklem */ 1882349124Srmacklemstatic void 1883349124Srmacklemfree_exports(struct exportlisthead *exhp) 1884349124Srmacklem{ 1885349124Srmacklem struct exportlist *ep, *ep2; 1886349124Srmacklem 1887349124Srmacklem SLIST_FOREACH_SAFE(ep, exhp, entries, ep2) { 1888349124Srmacklem SLIST_REMOVE(exhp, ep, exportlist, entries); 1889349124Srmacklem free_exp(ep); 1890349124Srmacklem } 1891349124Srmacklem SLIST_INIT(exhp); 1892349124Srmacklem} 1893349124Srmacklem 1894349124Srmacklem/* 18951558Srgrimes * Allocate an export list element 18961558Srgrimes */ 1897285128Straszstatic struct exportlist * 1898216587Scharnierget_exp(void) 18991558Srgrimes{ 19001558Srgrimes struct exportlist *ep; 19011558Srgrimes 1902224003Sdelphij ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); 19031558Srgrimes if (ep == (struct exportlist *)NULL) 19041558Srgrimes out_of_mem(); 19051558Srgrimes return (ep); 19061558Srgrimes} 19071558Srgrimes 19081558Srgrimes/* 19091558Srgrimes * Allocate a group list element 19101558Srgrimes */ 1911285128Straszstatic struct grouplist * 1912216587Scharnierget_grp(void) 19131558Srgrimes{ 19141558Srgrimes struct grouplist *gp; 19151558Srgrimes 1916224003Sdelphij gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); 19171558Srgrimes if (gp == (struct grouplist *)NULL) 19181558Srgrimes out_of_mem(); 19191558Srgrimes return (gp); 19201558Srgrimes} 19211558Srgrimes 19221558Srgrimes/* 19231558Srgrimes * Clean up upon an error in get_exportlist(). 19241558Srgrimes */ 1925285128Straszstatic void 1926329392Sbrdgetexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) 19271558Srgrimes{ 19281558Srgrimes struct grouplist *tgrp; 19291558Srgrimes 1930329392Sbrd if (!(opt_flags & OP_QUIET)) { 1931329392Sbrd if (reason != NULL) 1932329392Sbrd syslog(LOG_ERR, "bad exports list line '%s': %s", line, 1933329392Sbrd reason); 1934329392Sbrd else 1935329392Sbrd syslog(LOG_ERR, "bad exports list line '%s'", line); 1936329392Sbrd } 19371558Srgrimes if (ep && (ep->ex_flag & EX_LINKED) == 0) 19381558Srgrimes free_exp(ep); 19391558Srgrimes while (grp) { 19401558Srgrimes tgrp = grp; 19411558Srgrimes grp = grp->gr_next; 19421558Srgrimes free_grp(tgrp); 19431558Srgrimes } 19441558Srgrimes} 19451558Srgrimes 19461558Srgrimes/* 19471558Srgrimes * Search the export list for a matching fs. 19481558Srgrimes */ 1949285128Straszstatic struct exportlist * 1950349124Srmacklemex_search(fsid_t *fsid, struct exportlisthead *exhp) 19511558Srgrimes{ 19521558Srgrimes struct exportlist *ep; 19531558Srgrimes 1954349124Srmacklem SLIST_FOREACH(ep, exhp, entries) { 19551558Srgrimes if (ep->ex_fs.val[0] == fsid->val[0] && 19561558Srgrimes ep->ex_fs.val[1] == fsid->val[1]) 19571558Srgrimes return (ep); 19581558Srgrimes } 1959324955Smanu 19601558Srgrimes return (ep); 19611558Srgrimes} 19621558Srgrimes 19631558Srgrimes/* 19641558Srgrimes * Add a directory path to the list. 19651558Srgrimes */ 1966285128Straszstatic char * 1967216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len) 19681558Srgrimes{ 19691558Srgrimes struct dirlist *dp; 19701558Srgrimes 1971324234Smanu dp = malloc(sizeof (struct dirlist)); 197237663Scharnier if (dp == (struct dirlist *)NULL) 197337663Scharnier out_of_mem(); 19741558Srgrimes dp->dp_left = *dpp; 19751558Srgrimes dp->dp_right = (struct dirlist *)NULL; 19761558Srgrimes dp->dp_flag = 0; 19771558Srgrimes dp->dp_hosts = (struct hostlist *)NULL; 1978324234Smanu dp->dp_dirp = strndup(cp, len); 1979324234Smanu if (dp->dp_dirp == NULL) 1980324234Smanu out_of_mem(); 19811558Srgrimes *dpp = dp; 19821558Srgrimes return (dp->dp_dirp); 19831558Srgrimes} 19841558Srgrimes 19851558Srgrimes/* 19861558Srgrimes * Hang the dir list element off the dirpath binary tree as required 19871558Srgrimes * and update the entry for host. 19881558Srgrimes */ 1989285128Straszstatic void 1990216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 1991216587Scharnier int flags) 19921558Srgrimes{ 19931558Srgrimes struct hostlist *hp; 19941558Srgrimes struct dirlist *dp2; 19951558Srgrimes 19969336Sdfr if (flags & OP_ALLDIRS) { 19971558Srgrimes if (ep->ex_defdir) 19981558Srgrimes free((caddr_t)dp); 19991558Srgrimes else 20001558Srgrimes ep->ex_defdir = dp; 20019336Sdfr if (grp == (struct grouplist *)NULL) { 20021558Srgrimes ep->ex_defdir->dp_flag |= DP_DEFSET; 2003240902Srmacklem /* Save the default security flavors list. */ 2004240902Srmacklem ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2005240902Srmacklem if (ep->ex_numsecflavors > 0) 2006240902Srmacklem memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2007240902Srmacklem sizeof(ep->ex_secflavors)); 20089336Sdfr } else while (grp) { 20091558Srgrimes hp = get_ht(); 20101558Srgrimes hp->ht_grp = grp; 20111558Srgrimes hp->ht_next = ep->ex_defdir->dp_hosts; 20121558Srgrimes ep->ex_defdir->dp_hosts = hp; 2013240902Srmacklem /* Save the security flavors list for this host set. */ 2014240902Srmacklem grp->gr_numsecflavors = ep->ex_numsecflavors; 2015240902Srmacklem if (ep->ex_numsecflavors > 0) 2016240902Srmacklem memcpy(grp->gr_secflavors, ep->ex_secflavors, 2017240902Srmacklem sizeof(ep->ex_secflavors)); 20181558Srgrimes grp = grp->gr_next; 20191558Srgrimes } 20201558Srgrimes } else { 20211558Srgrimes 20221558Srgrimes /* 202337663Scharnier * Loop through the directories adding them to the tree. 20241558Srgrimes */ 20251558Srgrimes while (dp) { 20261558Srgrimes dp2 = dp->dp_left; 2027240902Srmacklem add_dlist(&ep->ex_dirl, dp, grp, flags, ep); 20281558Srgrimes dp = dp2; 20291558Srgrimes } 20301558Srgrimes } 20311558Srgrimes} 20321558Srgrimes 20331558Srgrimes/* 20341558Srgrimes * Traverse the binary tree either updating a node that is already there 20351558Srgrimes * for the new directory or adding the new node. 20361558Srgrimes */ 2037285128Straszstatic void 2038216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 2039240902Srmacklem int flags, struct exportlist *ep) 20401558Srgrimes{ 20411558Srgrimes struct dirlist *dp; 20421558Srgrimes struct hostlist *hp; 20431558Srgrimes int cmp; 20441558Srgrimes 20451558Srgrimes dp = *dpp; 20461558Srgrimes if (dp) { 20471558Srgrimes cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 20481558Srgrimes if (cmp > 0) { 2049240902Srmacklem add_dlist(&dp->dp_left, newdp, grp, flags, ep); 20501558Srgrimes return; 20511558Srgrimes } else if (cmp < 0) { 2052240902Srmacklem add_dlist(&dp->dp_right, newdp, grp, flags, ep); 20531558Srgrimes return; 20541558Srgrimes } else 20551558Srgrimes free((caddr_t)newdp); 20561558Srgrimes } else { 20571558Srgrimes dp = newdp; 20581558Srgrimes dp->dp_left = (struct dirlist *)NULL; 20591558Srgrimes *dpp = dp; 20601558Srgrimes } 20611558Srgrimes if (grp) { 20621558Srgrimes 20631558Srgrimes /* 20641558Srgrimes * Hang all of the host(s) off of the directory point. 20651558Srgrimes */ 20661558Srgrimes do { 20671558Srgrimes hp = get_ht(); 20681558Srgrimes hp->ht_grp = grp; 20691558Srgrimes hp->ht_next = dp->dp_hosts; 20701558Srgrimes dp->dp_hosts = hp; 2071240902Srmacklem /* Save the security flavors list for this host set. */ 2072240902Srmacklem grp->gr_numsecflavors = ep->ex_numsecflavors; 2073240902Srmacklem if (ep->ex_numsecflavors > 0) 2074240902Srmacklem memcpy(grp->gr_secflavors, ep->ex_secflavors, 2075240902Srmacklem sizeof(ep->ex_secflavors)); 20761558Srgrimes grp = grp->gr_next; 20771558Srgrimes } while (grp); 20789336Sdfr } else { 20791558Srgrimes dp->dp_flag |= DP_DEFSET; 2080240902Srmacklem /* Save the default security flavors list. */ 2081240902Srmacklem ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2082240902Srmacklem if (ep->ex_numsecflavors > 0) 2083240902Srmacklem memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2084240902Srmacklem sizeof(ep->ex_secflavors)); 20859336Sdfr } 20861558Srgrimes} 20871558Srgrimes 20881558Srgrimes/* 20891558Srgrimes * Search for a dirpath on the export point. 20901558Srgrimes */ 2091285128Straszstatic struct dirlist * 2092216587Scharnierdirp_search(struct dirlist *dp, char *dirp) 20931558Srgrimes{ 20941558Srgrimes int cmp; 20951558Srgrimes 20961558Srgrimes if (dp) { 209774462Salfred cmp = strcmp(dp->dp_dirp, dirp); 20981558Srgrimes if (cmp > 0) 209974462Salfred return (dirp_search(dp->dp_left, dirp)); 21001558Srgrimes else if (cmp < 0) 210174462Salfred return (dirp_search(dp->dp_right, dirp)); 21021558Srgrimes else 21031558Srgrimes return (dp); 21041558Srgrimes } 21051558Srgrimes return (dp); 21061558Srgrimes} 21071558Srgrimes 21081558Srgrimes/* 21091558Srgrimes * Scan for a host match in a directory tree. 21101558Srgrimes */ 2111285128Straszstatic int 2112216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 2113240902Srmacklem int *hostsetp, int *numsecflavors, int **secflavorsp) 21141558Srgrimes{ 21151558Srgrimes struct hostlist *hp; 21161558Srgrimes struct grouplist *grp; 211774462Salfred struct addrinfo *ai; 21181558Srgrimes 21191558Srgrimes if (dp) { 21201558Srgrimes if (dp->dp_flag & DP_DEFSET) 21219336Sdfr *defsetp = dp->dp_flag; 21221558Srgrimes hp = dp->dp_hosts; 21231558Srgrimes while (hp) { 21241558Srgrimes grp = hp->ht_grp; 21251558Srgrimes switch (grp->gr_type) { 21261558Srgrimes case GT_HOST: 212774462Salfred ai = grp->gr_ptr.gt_addrinfo; 212874462Salfred for (; ai; ai = ai->ai_next) { 212975801Siedowse if (!sacmp(ai->ai_addr, saddr, NULL)) { 213074462Salfred *hostsetp = 213174462Salfred (hp->ht_flag | DP_HOSTSET); 2132240902Srmacklem if (numsecflavors != NULL) { 2133240902Srmacklem *numsecflavors = 2134240902Srmacklem grp->gr_numsecflavors; 2135240902Srmacklem *secflavorsp = 2136240902Srmacklem grp->gr_secflavors; 2137240902Srmacklem } 213874462Salfred return (1); 213974462Salfred } 21409336Sdfr } 214175801Siedowse break; 21421558Srgrimes case GT_NET: 214375801Siedowse if (!sacmp(saddr, (struct sockaddr *) 214475801Siedowse &grp->gr_ptr.gt_net.nt_net, 214575801Siedowse (struct sockaddr *) 214675801Siedowse &grp->gr_ptr.gt_net.nt_mask)) { 214774462Salfred *hostsetp = (hp->ht_flag | DP_HOSTSET); 2148240902Srmacklem if (numsecflavors != NULL) { 2149240902Srmacklem *numsecflavors = 2150240902Srmacklem grp->gr_numsecflavors; 2151240902Srmacklem *secflavorsp = 2152240902Srmacklem grp->gr_secflavors; 2153240902Srmacklem } 215474462Salfred return (1); 215574462Salfred } 215675801Siedowse break; 215775801Siedowse } 21581558Srgrimes hp = hp->ht_next; 21591558Srgrimes } 21601558Srgrimes } 21611558Srgrimes return (0); 21621558Srgrimes} 21631558Srgrimes 21641558Srgrimes/* 21651558Srgrimes * Scan tree for a host that matches the address. 21661558Srgrimes */ 2167285128Straszstatic int 2168216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr) 21691558Srgrimes{ 21709336Sdfr int defset, hostset; 21711558Srgrimes 21721558Srgrimes if (dp) { 21731558Srgrimes if (scan_tree(dp->dp_left, saddr)) 21741558Srgrimes return (1); 2175240902Srmacklem if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) 21761558Srgrimes return (1); 21771558Srgrimes if (scan_tree(dp->dp_right, saddr)) 21781558Srgrimes return (1); 21791558Srgrimes } 21801558Srgrimes return (0); 21811558Srgrimes} 21821558Srgrimes 21831558Srgrimes/* 21841558Srgrimes * Traverse the dirlist tree and free it up. 21851558Srgrimes */ 2186285128Straszstatic void 2187216587Scharnierfree_dir(struct dirlist *dp) 21881558Srgrimes{ 21891558Srgrimes 21901558Srgrimes if (dp) { 21911558Srgrimes free_dir(dp->dp_left); 21921558Srgrimes free_dir(dp->dp_right); 21931558Srgrimes free_host(dp->dp_hosts); 2194324234Smanu free(dp->dp_dirp); 2195324234Smanu free(dp); 21961558Srgrimes } 21971558Srgrimes} 21981558Srgrimes 21991558Srgrimes/* 2200184588Sdfr * Parse a colon separated list of security flavors 2201184588Sdfr */ 2202285128Straszstatic int 2203216587Scharnierparsesec(char *seclist, struct exportlist *ep) 2204184588Sdfr{ 2205184588Sdfr char *cp, savedc; 2206184588Sdfr int flavor; 2207184588Sdfr 2208184588Sdfr ep->ex_numsecflavors = 0; 2209184588Sdfr for (;;) { 2210184588Sdfr cp = strchr(seclist, ':'); 2211184588Sdfr if (cp) { 2212184588Sdfr savedc = *cp; 2213184588Sdfr *cp = '\0'; 2214184588Sdfr } 2215184588Sdfr 2216184588Sdfr if (!strcmp(seclist, "sys")) 2217184588Sdfr flavor = AUTH_SYS; 2218184588Sdfr else if (!strcmp(seclist, "krb5")) 2219184588Sdfr flavor = RPCSEC_GSS_KRB5; 2220184588Sdfr else if (!strcmp(seclist, "krb5i")) 2221184588Sdfr flavor = RPCSEC_GSS_KRB5I; 2222184588Sdfr else if (!strcmp(seclist, "krb5p")) 2223184588Sdfr flavor = RPCSEC_GSS_KRB5P; 2224184588Sdfr else { 2225184588Sdfr if (cp) 2226184588Sdfr *cp = savedc; 2227184588Sdfr syslog(LOG_ERR, "bad sec flavor: %s", seclist); 2228184588Sdfr return (1); 2229184588Sdfr } 2230184588Sdfr if (ep->ex_numsecflavors == MAXSECFLAVORS) { 2231184588Sdfr if (cp) 2232184588Sdfr *cp = savedc; 2233184588Sdfr syslog(LOG_ERR, "too many sec flavors: %s", seclist); 2234184588Sdfr return (1); 2235184588Sdfr } 2236184588Sdfr ep->ex_secflavors[ep->ex_numsecflavors] = flavor; 2237184588Sdfr ep->ex_numsecflavors++; 2238184588Sdfr if (cp) { 2239184588Sdfr *cp = savedc; 2240184588Sdfr seclist = cp + 1; 2241184588Sdfr } else { 2242184588Sdfr break; 2243184588Sdfr } 2244184588Sdfr } 2245184588Sdfr return (0); 2246184588Sdfr} 2247184588Sdfr 2248184588Sdfr/* 22491558Srgrimes * Parse the option string and update fields. 22501558Srgrimes * Option arguments may either be -<option>=<value> or 22511558Srgrimes * -<option> <value> 22521558Srgrimes */ 2253285128Straszstatic int 2254216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 2255216587Scharnier int *has_hostp, int *exflagsp, struct xucred *cr) 22561558Srgrimes{ 22571558Srgrimes char *cpoptarg, *cpoptend; 22581558Srgrimes char *cp, *endcp, *cpopt, savedc, savedc2; 22591558Srgrimes int allflag, usedarg; 22601558Srgrimes 226151968Salfred savedc2 = '\0'; 22621558Srgrimes cpopt = *cpp; 22631558Srgrimes cpopt++; 22641558Srgrimes cp = *endcpp; 22651558Srgrimes savedc = *cp; 22661558Srgrimes *cp = '\0'; 22671558Srgrimes while (cpopt && *cpopt) { 22681558Srgrimes allflag = 1; 22691558Srgrimes usedarg = -2; 227037663Scharnier if ((cpoptend = strchr(cpopt, ','))) { 22711558Srgrimes *cpoptend++ = '\0'; 227237663Scharnier if ((cpoptarg = strchr(cpopt, '='))) 22731558Srgrimes *cpoptarg++ = '\0'; 22741558Srgrimes } else { 227537663Scharnier if ((cpoptarg = strchr(cpopt, '='))) 22761558Srgrimes *cpoptarg++ = '\0'; 22771558Srgrimes else { 22781558Srgrimes *cp = savedc; 22791558Srgrimes nextfield(&cp, &endcp); 22801558Srgrimes **endcpp = '\0'; 22811558Srgrimes if (endcp > cp && *cp != '-') { 22821558Srgrimes cpoptarg = cp; 22831558Srgrimes savedc2 = *endcp; 22841558Srgrimes *endcp = '\0'; 22851558Srgrimes usedarg = 0; 22861558Srgrimes } 22871558Srgrimes } 22881558Srgrimes } 22891558Srgrimes if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 22901558Srgrimes *exflagsp |= MNT_EXRDONLY; 22911558Srgrimes } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 22921558Srgrimes !(allflag = strcmp(cpopt, "mapall")) || 22931558Srgrimes !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 22941558Srgrimes usedarg++; 22951558Srgrimes parsecred(cpoptarg, cr); 22961558Srgrimes if (allflag == 0) { 22971558Srgrimes *exflagsp |= MNT_EXPORTANON; 22981558Srgrimes opt_flags |= OP_MAPALL; 22991558Srgrimes } else 23001558Srgrimes opt_flags |= OP_MAPROOT; 23011558Srgrimes } else if (cpoptarg && (!strcmp(cpopt, "mask") || 230275801Siedowse !strcmp(cpopt, "m"))) { 23031558Srgrimes if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 230437663Scharnier syslog(LOG_ERR, "bad mask: %s", cpoptarg); 23051558Srgrimes return (1); 23061558Srgrimes } 23071558Srgrimes usedarg++; 23081558Srgrimes opt_flags |= OP_MASK; 23091558Srgrimes } else if (cpoptarg && (!strcmp(cpopt, "network") || 23101558Srgrimes !strcmp(cpopt, "n"))) { 231174462Salfred if (strchr(cpoptarg, '/') != NULL) { 231274462Salfred if (debug) 231374462Salfred fprintf(stderr, "setting OP_MASKLEN\n"); 231474462Salfred opt_flags |= OP_MASKLEN; 231574462Salfred } 23161558Srgrimes if (grp->gr_type != GT_NULL) { 231737663Scharnier syslog(LOG_ERR, "network/host conflict"); 23181558Srgrimes return (1); 23191558Srgrimes } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 232037663Scharnier syslog(LOG_ERR, "bad net: %s", cpoptarg); 23211558Srgrimes return (1); 23221558Srgrimes } 23231558Srgrimes grp->gr_type = GT_NET; 23241558Srgrimes *has_hostp = 1; 23251558Srgrimes usedarg++; 23261558Srgrimes opt_flags |= OP_NET; 23271558Srgrimes } else if (!strcmp(cpopt, "alldirs")) { 23281558Srgrimes opt_flags |= OP_ALLDIRS; 232927447Sdfr } else if (!strcmp(cpopt, "public")) { 233027447Sdfr *exflagsp |= MNT_EXPUBLIC; 233127447Sdfr } else if (!strcmp(cpopt, "webnfs")) { 233227447Sdfr *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 233327447Sdfr opt_flags |= OP_MAPALL; 233427447Sdfr } else if (cpoptarg && !strcmp(cpopt, "index")) { 233527447Sdfr ep->ex_indexfile = strdup(cpoptarg); 2336100336Sjoerg } else if (!strcmp(cpopt, "quiet")) { 2337100336Sjoerg opt_flags |= OP_QUIET; 2338247034Spluknet } else if (cpoptarg && !strcmp(cpopt, "sec")) { 2339184588Sdfr if (parsesec(cpoptarg, ep)) 2340184588Sdfr return (1); 2341184588Sdfr opt_flags |= OP_SEC; 2342184588Sdfr usedarg++; 23431558Srgrimes } else { 234437663Scharnier syslog(LOG_ERR, "bad opt %s", cpopt); 23451558Srgrimes return (1); 23461558Srgrimes } 23471558Srgrimes if (usedarg >= 0) { 23481558Srgrimes *endcp = savedc2; 23491558Srgrimes **endcpp = savedc; 23501558Srgrimes if (usedarg > 0) { 23511558Srgrimes *cpp = cp; 23521558Srgrimes *endcpp = endcp; 23531558Srgrimes } 23541558Srgrimes return (0); 23551558Srgrimes } 23561558Srgrimes cpopt = cpoptend; 23571558Srgrimes } 23581558Srgrimes **endcpp = savedc; 23591558Srgrimes return (0); 23601558Srgrimes} 23611558Srgrimes 23621558Srgrimes/* 23631558Srgrimes * Translate a character string to the corresponding list of network 23641558Srgrimes * addresses for a hostname. 23651558Srgrimes */ 2366285128Straszstatic int 2367216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 23681558Srgrimes{ 23697401Swpaul struct grouplist *checkgrp; 237075635Siedowse struct addrinfo *ai, *tai, hints; 237174462Salfred int ecode; 237274462Salfred char host[NI_MAXHOST]; 23731558Srgrimes 237474462Salfred if (grp->gr_type != GT_NULL) { 237574462Salfred syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 23761558Srgrimes return (1); 23771558Srgrimes } 237874462Salfred memset(&hints, 0, sizeof hints); 237974462Salfred hints.ai_flags = AI_CANONNAME; 238074462Salfred hints.ai_protocol = IPPROTO_UDP; 238174462Salfred ecode = getaddrinfo(cp, NULL, &hints, &ai); 238274462Salfred if (ecode != 0) { 238375635Siedowse syslog(LOG_ERR,"can't get address info for host %s", cp); 238474462Salfred return 1; 238574462Salfred } 238674462Salfred grp->gr_ptr.gt_addrinfo = ai; 238774462Salfred while (ai != NULL) { 238874462Salfred if (ai->ai_canonname == NULL) { 238974462Salfred if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 2390146187Sume sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 239174462Salfred strlcpy(host, "?", sizeof(host)); 239274462Salfred ai->ai_canonname = strdup(host); 239374462Salfred ai->ai_flags |= AI_CANONNAME; 239475641Siedowse } 239574462Salfred if (debug) 239675635Siedowse fprintf(stderr, "got host %s\n", ai->ai_canonname); 239775635Siedowse /* 239875635Siedowse * Sanity check: make sure we don't already have an entry 239975635Siedowse * for this host in the grouplist. 240075635Siedowse */ 240175635Siedowse for (checkgrp = tgrp; checkgrp != NULL; 240275635Siedowse checkgrp = checkgrp->gr_next) { 240375635Siedowse if (checkgrp->gr_type != GT_HOST) 240475635Siedowse continue; 240575635Siedowse for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 240675635Siedowse tai = tai->ai_next) { 240775801Siedowse if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 240875635Siedowse continue; 240975635Siedowse if (debug) 241075635Siedowse fprintf(stderr, 241175635Siedowse "ignoring duplicate host %s\n", 241275635Siedowse ai->ai_canonname); 241375635Siedowse grp->gr_type = GT_IGNORE; 241475635Siedowse return (0); 241575635Siedowse } 241675635Siedowse } 241774462Salfred ai = ai->ai_next; 24181558Srgrimes } 241975635Siedowse grp->gr_type = GT_HOST; 24201558Srgrimes return (0); 24211558Srgrimes} 24221558Srgrimes 24231558Srgrimes/* 24241558Srgrimes * Free up an exports list component 24251558Srgrimes */ 2426285128Straszstatic void 2427216587Scharnierfree_exp(struct exportlist *ep) 24281558Srgrimes{ 24291558Srgrimes 24301558Srgrimes if (ep->ex_defdir) { 24311558Srgrimes free_host(ep->ex_defdir->dp_hosts); 24321558Srgrimes free((caddr_t)ep->ex_defdir); 24331558Srgrimes } 24341558Srgrimes if (ep->ex_fsdir) 24351558Srgrimes free(ep->ex_fsdir); 243627447Sdfr if (ep->ex_indexfile) 243727447Sdfr free(ep->ex_indexfile); 24381558Srgrimes free_dir(ep->ex_dirl); 24391558Srgrimes free((caddr_t)ep); 24401558Srgrimes} 24411558Srgrimes 24421558Srgrimes/* 24431558Srgrimes * Free hosts. 24441558Srgrimes */ 2445285128Straszstatic void 2446216587Scharnierfree_host(struct hostlist *hp) 24471558Srgrimes{ 24481558Srgrimes struct hostlist *hp2; 24491558Srgrimes 24501558Srgrimes while (hp) { 24511558Srgrimes hp2 = hp; 24521558Srgrimes hp = hp->ht_next; 24531558Srgrimes free((caddr_t)hp2); 24541558Srgrimes } 24551558Srgrimes} 24561558Srgrimes 2457285128Straszstatic struct hostlist * 2458216587Scharnierget_ht(void) 24591558Srgrimes{ 24601558Srgrimes struct hostlist *hp; 24611558Srgrimes 24621558Srgrimes hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 24631558Srgrimes if (hp == (struct hostlist *)NULL) 24641558Srgrimes out_of_mem(); 24651558Srgrimes hp->ht_next = (struct hostlist *)NULL; 24669336Sdfr hp->ht_flag = 0; 24671558Srgrimes return (hp); 24681558Srgrimes} 24691558Srgrimes 24701558Srgrimes/* 24711558Srgrimes * Out of memory, fatal 24721558Srgrimes */ 2473285128Straszstatic void 2474216587Scharnierout_of_mem(void) 24751558Srgrimes{ 24761558Srgrimes 247737663Scharnier syslog(LOG_ERR, "out of memory"); 24781558Srgrimes exit(2); 24791558Srgrimes} 24801558Srgrimes 24811558Srgrimes/* 2482158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into 24831558Srgrimes * the kernel. 24841558Srgrimes */ 2485285128Straszstatic int 2486158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags, 2487158857Srodrigc struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb) 24881558Srgrimes{ 248975841Siedowse struct statfs fsb1; 249074462Salfred struct addrinfo *ai; 2491282214Strasz struct export_args *eap; 2492158857Srodrigc char errmsg[255]; 2493158857Srodrigc char *cp; 24941558Srgrimes int done; 2495158857Srodrigc char savedc; 2496158857Srodrigc struct iovec *iov; 2497184588Sdfr int i, iovlen; 2498158857Srodrigc int ret; 2499192934Srmacklem struct nfsex_args nfsea; 25001558Srgrimes 2501282214Strasz eap = &nfsea.export; 2502192934Srmacklem 2503158857Srodrigc cp = NULL; 2504158857Srodrigc savedc = '\0'; 2505158857Srodrigc iov = NULL; 2506158857Srodrigc iovlen = 0; 2507158857Srodrigc ret = 0; 250875801Siedowse 2509192934Srmacklem bzero(eap, sizeof (struct export_args)); 2510158857Srodrigc bzero(errmsg, sizeof(errmsg)); 2511192934Srmacklem eap->ex_flags = exflags; 2512192934Srmacklem eap->ex_anon = *anoncrp; 2513192934Srmacklem eap->ex_indexfile = ep->ex_indexfile; 251475641Siedowse if (grp->gr_type == GT_HOST) 251574462Salfred ai = grp->gr_ptr.gt_addrinfo; 251675641Siedowse else 251775641Siedowse ai = NULL; 2518192934Srmacklem eap->ex_numsecflavors = ep->ex_numsecflavors; 2519192934Srmacklem for (i = 0; i < eap->ex_numsecflavors; i++) 2520192934Srmacklem eap->ex_secflavors[i] = ep->ex_secflavors[i]; 2521192934Srmacklem if (eap->ex_numsecflavors == 0) { 2522192934Srmacklem eap->ex_numsecflavors = 1; 2523192934Srmacklem eap->ex_secflavors[0] = AUTH_SYS; 2524184588Sdfr } 25251558Srgrimes done = FALSE; 2526158857Srodrigc 2527192934Srmacklem if (v4root_phase == 0) { 2528192934Srmacklem build_iovec(&iov, &iovlen, "fstype", NULL, 0); 2529192934Srmacklem build_iovec(&iov, &iovlen, "fspath", NULL, 0); 2530192934Srmacklem build_iovec(&iov, &iovlen, "from", NULL, 0); 2531192934Srmacklem build_iovec(&iov, &iovlen, "update", NULL, 0); 2532192934Srmacklem build_iovec(&iov, &iovlen, "export", eap, 2533192934Srmacklem sizeof (struct export_args)); 2534192934Srmacklem build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 2535192934Srmacklem } 2536158857Srodrigc 25371558Srgrimes while (!done) { 25381558Srgrimes switch (grp->gr_type) { 25391558Srgrimes case GT_HOST: 254075641Siedowse if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 254174462Salfred goto skip; 2542192934Srmacklem eap->ex_addr = ai->ai_addr; 2543192934Srmacklem eap->ex_addrlen = ai->ai_addrlen; 2544192934Srmacklem eap->ex_masklen = 0; 25451558Srgrimes break; 25461558Srgrimes case GT_NET: 254775801Siedowse if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 254874462Salfred have_v6 == 0) 254974462Salfred goto skip; 2550192934Srmacklem eap->ex_addr = 255175801Siedowse (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 2552192934Srmacklem eap->ex_addrlen = 2553158857Srodrigc ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 2554192934Srmacklem eap->ex_mask = 255575801Siedowse (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 2556192934Srmacklem eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 25571558Srgrimes break; 255875641Siedowse case GT_DEFAULT: 2559192934Srmacklem eap->ex_addr = NULL; 2560192934Srmacklem eap->ex_addrlen = 0; 2561192934Srmacklem eap->ex_mask = NULL; 2562192934Srmacklem eap->ex_masklen = 0; 256375641Siedowse break; 25647401Swpaul case GT_IGNORE: 2565158857Srodrigc ret = 0; 2566158857Srodrigc goto error_exit; 25677401Swpaul break; 25681558Srgrimes default: 256937663Scharnier syslog(LOG_ERR, "bad grouptype"); 25701558Srgrimes if (cp) 25711558Srgrimes *cp = savedc; 2572158857Srodrigc ret = 1; 2573158857Srodrigc goto error_exit; 2574298089Spfg } 25751558Srgrimes 25761558Srgrimes /* 2577192934Srmacklem * For V4:, use the nfssvc() syscall, instead of mount(). 25781558Srgrimes */ 2579192934Srmacklem if (v4root_phase == 2) { 2580192934Srmacklem nfsea.fspec = v4root_dirpath; 2581282214Strasz if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) { 2582192934Srmacklem syslog(LOG_ERR, "Exporting V4: failed"); 2583192934Srmacklem return (2); 2584158857Srodrigc } 2585192934Srmacklem } else { 2586192934Srmacklem /* 2587192934Srmacklem * XXX: 2588192934Srmacklem * Maybe I should just use the fsb->f_mntonname path 2589192934Srmacklem * instead of looping back up the dirp to the mount 2590192934Srmacklem * point?? 2591192934Srmacklem * Also, needs to know how to export all types of local 2592192934Srmacklem * exportable filesystems and not just "ufs". 2593192934Srmacklem */ 2594192934Srmacklem iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 2595192934Srmacklem iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 2596192934Srmacklem iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 2597192934Srmacklem iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 2598192934Srmacklem iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 2599192934Srmacklem iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 2600270183Sbdrewery errmsg[0] = '\0'; 2601192934Srmacklem 2602192934Srmacklem while (nmount(iov, iovlen, fsb->f_flags) < 0) { 2603192934Srmacklem if (cp) 2604192934Srmacklem *cp-- = savedc; 2605192934Srmacklem else 2606192934Srmacklem cp = dirp + dirplen - 1; 2607192934Srmacklem if (opt_flags & OP_QUIET) { 2608192934Srmacklem ret = 1; 2609192934Srmacklem goto error_exit; 2610192934Srmacklem } 2611192934Srmacklem if (errno == EPERM) { 2612192934Srmacklem if (debug) 2613239744Sdelphij warnx("can't change attributes for %s: %s", 2614239744Sdelphij dirp, errmsg); 2615192934Srmacklem syslog(LOG_ERR, 2616239744Sdelphij "can't change attributes for %s: %s", 2617239744Sdelphij dirp, errmsg); 2618192934Srmacklem ret = 1; 2619192934Srmacklem goto error_exit; 2620192934Srmacklem } 2621192934Srmacklem if (opt_flags & OP_ALLDIRS) { 2622192934Srmacklem if (errno == EINVAL) 2623192934Srmacklem syslog(LOG_ERR, 2624100336Sjoerg "-alldirs requested but %s is not a filesystem mountpoint", 2625192934Srmacklem dirp); 2626192934Srmacklem else 2627192934Srmacklem syslog(LOG_ERR, 2628192934Srmacklem "could not remount %s: %m", 2629192934Srmacklem dirp); 2630192934Srmacklem ret = 1; 2631192934Srmacklem goto error_exit; 2632192934Srmacklem } 2633192934Srmacklem /* back up over the last component */ 2634192934Srmacklem while (*cp == '/' && cp > dirp) 2635192934Srmacklem cp--; 2636192934Srmacklem while (*(cp - 1) != '/' && cp > dirp) 2637192934Srmacklem cp--; 2638192934Srmacklem if (cp == dirp) { 2639192934Srmacklem if (debug) 2640192934Srmacklem warnx("mnt unsucc"); 2641192934Srmacklem syslog(LOG_ERR, "can't export %s %s", 2642192934Srmacklem dirp, errmsg); 2643192934Srmacklem ret = 1; 2644192934Srmacklem goto error_exit; 2645192934Srmacklem } 2646192934Srmacklem savedc = *cp; 2647192934Srmacklem *cp = '\0'; 2648192934Srmacklem /* 2649192934Srmacklem * Check that we're still on the same 2650192934Srmacklem * filesystem. 2651192934Srmacklem */ 2652192934Srmacklem if (statfs(dirp, &fsb1) != 0 || 2653192934Srmacklem bcmp(&fsb1.f_fsid, &fsb->f_fsid, 2654192934Srmacklem sizeof (fsb1.f_fsid)) != 0) { 2655192934Srmacklem *cp = savedc; 2656100336Sjoerg syslog(LOG_ERR, 2657192934Srmacklem "can't export %s %s", dirp, 2658192934Srmacklem errmsg); 2659192934Srmacklem ret = 1; 2660192934Srmacklem goto error_exit; 2661192934Srmacklem } 26621558Srgrimes } 26631558Srgrimes } 2664192934Srmacklem 2665192934Srmacklem /* 2666192934Srmacklem * For the experimental server: 2667192934Srmacklem * If this is the public directory, get the file handle 2668192934Srmacklem * and load it into the kernel via the nfssvc() syscall. 2669192934Srmacklem */ 2670282214Strasz if ((exflags & MNT_EXPUBLIC) != 0) { 2671192934Srmacklem fhandle_t fh; 2672192934Srmacklem char *public_name; 2673192934Srmacklem 2674192934Srmacklem if (eap->ex_indexfile != NULL) 2675192934Srmacklem public_name = eap->ex_indexfile; 2676192934Srmacklem else 2677192934Srmacklem public_name = dirp; 2678192934Srmacklem if (getfh(public_name, &fh) < 0) 2679192934Srmacklem syslog(LOG_ERR, 2680192934Srmacklem "Can't get public fh for %s", public_name); 2681192934Srmacklem else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) 2682192934Srmacklem syslog(LOG_ERR, 2683192934Srmacklem "Can't set public fh for %s", public_name); 2684192934Srmacklem else 2685192934Srmacklem has_publicfh = 1; 2686192934Srmacklem } 268774462Salfredskip: 268875641Siedowse if (ai != NULL) 268974462Salfred ai = ai->ai_next; 269075641Siedowse if (ai == NULL) 26911558Srgrimes done = TRUE; 26921558Srgrimes } 26931558Srgrimes if (cp) 26941558Srgrimes *cp = savedc; 2695158857Srodrigcerror_exit: 2696158857Srodrigc /* free strings allocated by strdup() in getmntopts.c */ 2697158857Srodrigc if (iov != NULL) { 2698158857Srodrigc free(iov[0].iov_base); /* fstype */ 2699158857Srodrigc free(iov[2].iov_base); /* fspath */ 2700158857Srodrigc free(iov[4].iov_base); /* from */ 2701158857Srodrigc free(iov[6].iov_base); /* update */ 2702158857Srodrigc free(iov[8].iov_base); /* export */ 2703158857Srodrigc free(iov[10].iov_base); /* errmsg */ 2704158857Srodrigc 2705158857Srodrigc /* free iov, allocated by realloc() */ 2706158857Srodrigc free(iov); 2707158857Srodrigc } 2708158857Srodrigc return (ret); 27091558Srgrimes} 27101558Srgrimes 27111558Srgrimes/* 27121558Srgrimes * Translate a net address. 271375801Siedowse * 271475801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 27151558Srgrimes */ 2716285128Straszstatic int 2717216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg) 27181558Srgrimes{ 271975861Siedowse struct netent *np = NULL; 272074462Salfred char *name, *p, *prefp; 272175801Siedowse struct sockaddr_in sin; 272275861Siedowse struct sockaddr *sa = NULL; 272374462Salfred struct addrinfo hints, *ai = NULL; 272474462Salfred char netname[NI_MAXHOST]; 272574462Salfred long preflen; 27261558Srgrimes 272775635Siedowse p = prefp = NULL; 272874462Salfred if ((opt_flags & OP_MASKLEN) && !maskflg) { 272974462Salfred p = strchr(cp, '/'); 273074462Salfred *p = '\0'; 273174462Salfred prefp = p + 1; 273274462Salfred } 273374462Salfred 273475861Siedowse /* 273575861Siedowse * Check for a numeric address first. We wish to avoid 273675861Siedowse * possible DNS lookups in getnetbyname(). 273775861Siedowse */ 273875861Siedowse if (isxdigit(*cp) || *cp == ':') { 273974462Salfred memset(&hints, 0, sizeof hints); 274075801Siedowse /* Ensure the mask and the network have the same family. */ 274175801Siedowse if (maskflg && (opt_flags & OP_NET)) 274275801Siedowse hints.ai_family = net->nt_net.ss_family; 274375801Siedowse else if (!maskflg && (opt_flags & OP_HAVEMASK)) 274475801Siedowse hints.ai_family = net->nt_mask.ss_family; 274575801Siedowse else 274675801Siedowse hints.ai_family = AF_UNSPEC; 274774462Salfred hints.ai_flags = AI_NUMERICHOST; 274875861Siedowse if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 274975861Siedowse sa = ai->ai_addr; 275075861Siedowse if (sa != NULL && ai->ai_family == AF_INET) { 275174462Salfred /* 275275801Siedowse * The address in `cp' is really a network address, so 275375801Siedowse * use inet_network() to re-interpret this correctly. 275475801Siedowse * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 275574462Salfred */ 275675801Siedowse bzero(&sin, sizeof sin); 275774462Salfred sin.sin_family = AF_INET; 275874462Salfred sin.sin_len = sizeof sin; 275975801Siedowse sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 276074462Salfred if (debug) 276175801Siedowse fprintf(stderr, "get_net: v4 addr %s\n", 276275801Siedowse inet_ntoa(sin.sin_addr)); 276374462Salfred sa = (struct sockaddr *)&sin; 276475861Siedowse } 276575861Siedowse } 276675861Siedowse if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 276775861Siedowse bzero(&sin, sizeof sin); 276875861Siedowse sin.sin_family = AF_INET; 276975861Siedowse sin.sin_len = sizeof sin; 277075861Siedowse sin.sin_addr = inet_makeaddr(np->n_net, 0); 277175861Siedowse sa = (struct sockaddr *)&sin; 277275861Siedowse } 277375861Siedowse if (sa == NULL) 277474462Salfred goto fail; 277525318Spst 277675801Siedowse if (maskflg) { 277775801Siedowse /* The specified sockaddr is a mask. */ 277875801Siedowse if (checkmask(sa) != 0) 277975801Siedowse goto fail; 278075801Siedowse bcopy(sa, &net->nt_mask, sa->sa_len); 278175801Siedowse opt_flags |= OP_HAVEMASK; 278275801Siedowse } else { 278375801Siedowse /* The specified sockaddr is a network address. */ 278475801Siedowse bcopy(sa, &net->nt_net, sa->sa_len); 278574462Salfred 278675801Siedowse /* Get a network name for the export list. */ 278775801Siedowse if (np) { 278875801Siedowse name = np->n_name; 278975801Siedowse } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 2790146187Sume NULL, 0, NI_NUMERICHOST) == 0) { 279175801Siedowse name = netname; 279275801Siedowse } else { 279375801Siedowse goto fail; 279475801Siedowse } 279575801Siedowse if ((net->nt_name = strdup(name)) == NULL) 279675801Siedowse out_of_mem(); 279775801Siedowse 279875801Siedowse /* 279975801Siedowse * Extract a mask from either a "/<masklen>" suffix, or 280075801Siedowse * from the class of an IPv4 address. 280175801Siedowse */ 280274462Salfred if (opt_flags & OP_MASKLEN) { 280374462Salfred preflen = strtol(prefp, NULL, 10); 280475801Siedowse if (preflen < 0L || preflen == LONG_MAX) 280574462Salfred goto fail; 280675801Siedowse bcopy(sa, &net->nt_mask, sa->sa_len); 280775801Siedowse if (makemask(&net->nt_mask, (int)preflen) != 0) 280875801Siedowse goto fail; 280975801Siedowse opt_flags |= OP_HAVEMASK; 281074462Salfred *p = '/'; 281175801Siedowse } else if (sa->sa_family == AF_INET && 281275801Siedowse (opt_flags & OP_MASK) == 0) { 281375801Siedowse in_addr_t addr; 281474462Salfred 281575801Siedowse addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 281675801Siedowse if (IN_CLASSA(addr)) 281775801Siedowse preflen = 8; 281875801Siedowse else if (IN_CLASSB(addr)) 281975801Siedowse preflen = 16; 282075801Siedowse else if (IN_CLASSC(addr)) 282175801Siedowse preflen = 24; 282275801Siedowse else if (IN_CLASSD(addr)) 282375801Siedowse preflen = 28; 282475801Siedowse else 282575801Siedowse preflen = 32; /* XXX */ 282675801Siedowse 282775801Siedowse bcopy(sa, &net->nt_mask, sa->sa_len); 282875801Siedowse makemask(&net->nt_mask, (int)preflen); 282975801Siedowse opt_flags |= OP_HAVEMASK; 283074462Salfred } 283174462Salfred } 283274462Salfred 283374462Salfred if (ai) 283474462Salfred freeaddrinfo(ai); 283574462Salfred return 0; 283674462Salfred 283774462Salfredfail: 283874462Salfred if (ai) 283974462Salfred freeaddrinfo(ai); 284074462Salfred return 1; 28411558Srgrimes} 28421558Srgrimes 28431558Srgrimes/* 28441558Srgrimes * Parse out the next white space separated field 28451558Srgrimes */ 2846285128Straszstatic void 2847216587Scharniernextfield(char **cp, char **endcp) 28481558Srgrimes{ 28491558Srgrimes char *p; 2850347341Smav char quot = 0; 28511558Srgrimes 28521558Srgrimes p = *cp; 28531558Srgrimes while (*p == ' ' || *p == '\t') 28541558Srgrimes p++; 2855347341Smav *cp = p; 2856347341Smav while (*p != '\0') { 2857347341Smav if (quot) { 2858347341Smav if (*p == quot) 2859347341Smav quot = 0; 2860347341Smav } else { 2861347341Smav if (*p == '\\' && *(p + 1) != '\0') 2862347341Smav p++; 2863347341Smav else if (*p == '\'' || *p == '"') 2864347341Smav quot = *p; 2865347341Smav else if (*p == ' ' || *p == '\t') 2866347341Smav break; 2867347341Smav } 2868347341Smav p++; 2869347341Smav }; 2870347341Smav *endcp = p; 28711558Srgrimes} 28721558Srgrimes 28731558Srgrimes/* 28741558Srgrimes * Get an exports file line. Skip over blank lines and handle line 28751558Srgrimes * continuations. 28761558Srgrimes */ 2877285128Straszstatic int 2878216587Scharnierget_line(void) 28791558Srgrimes{ 28801558Srgrimes char *p, *cp; 288196622Siedowse size_t len; 28821558Srgrimes int totlen, cont_line; 28831558Srgrimes 28841558Srgrimes /* 28851558Srgrimes * Loop around ignoring blank lines and getting all continuation lines. 28861558Srgrimes */ 28871558Srgrimes p = line; 28881558Srgrimes totlen = 0; 28891558Srgrimes do { 289096622Siedowse if ((p = fgetln(exp_file, &len)) == NULL) 28911558Srgrimes return (0); 28921558Srgrimes cp = p + len - 1; 28931558Srgrimes cont_line = 0; 28941558Srgrimes while (cp >= p && 28951558Srgrimes (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 28961558Srgrimes if (*cp == '\\') 28971558Srgrimes cont_line = 1; 28981558Srgrimes cp--; 28991558Srgrimes len--; 29001558Srgrimes } 290179117Sdd if (cont_line) { 290279117Sdd *++cp = ' '; 290379117Sdd len++; 290479117Sdd } 290596622Siedowse if (linesize < len + totlen + 1) { 290696622Siedowse linesize = len + totlen + 1; 290796622Siedowse line = realloc(line, linesize); 290896622Siedowse if (line == NULL) 290996622Siedowse out_of_mem(); 29101558Srgrimes } 291196622Siedowse memcpy(line + totlen, p, len); 291296622Siedowse totlen += len; 291396622Siedowse line[totlen] = '\0'; 29141558Srgrimes } while (totlen == 0 || cont_line); 29151558Srgrimes return (1); 29161558Srgrimes} 29171558Srgrimes 29181558Srgrimes/* 29191558Srgrimes * Parse a description of a credential. 29201558Srgrimes */ 2921285128Straszstatic void 2922216587Scharnierparsecred(char *namelist, struct xucred *cr) 29231558Srgrimes{ 29241558Srgrimes char *name; 29251558Srgrimes int cnt; 29261558Srgrimes char *names; 29271558Srgrimes struct passwd *pw; 29281558Srgrimes struct group *gr; 2929194498Sbrooks gid_t groups[XU_NGROUPS + 1]; 2930136051Sstefanf int ngroups; 29311558Srgrimes 293291354Sdd cr->cr_version = XUCRED_VERSION; 29331558Srgrimes /* 293437663Scharnier * Set up the unprivileged user. 29351558Srgrimes */ 29361558Srgrimes cr->cr_uid = -2; 29371558Srgrimes cr->cr_groups[0] = -2; 29381558Srgrimes cr->cr_ngroups = 1; 29391558Srgrimes /* 29401558Srgrimes * Get the user's password table entry. 29411558Srgrimes */ 2942347341Smav names = namelist; 2943347341Smav name = strsep_quote(&names, ":"); 2944293305Sjpaetzel /* Bug? name could be NULL here */ 29451558Srgrimes if (isdigit(*name) || *name == '-') 29461558Srgrimes pw = getpwuid(atoi(name)); 29471558Srgrimes else 29481558Srgrimes pw = getpwnam(name); 29491558Srgrimes /* 29501558Srgrimes * Credentials specified as those of a user. 29511558Srgrimes */ 29521558Srgrimes if (names == NULL) { 29531558Srgrimes if (pw == NULL) { 295437663Scharnier syslog(LOG_ERR, "unknown user: %s", name); 29551558Srgrimes return; 29561558Srgrimes } 29571558Srgrimes cr->cr_uid = pw->pw_uid; 2958194498Sbrooks ngroups = XU_NGROUPS + 1; 2959333197Savg if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) { 296037663Scharnier syslog(LOG_ERR, "too many groups"); 2961333197Savg ngroups = XU_NGROUPS + 1; 2962333197Savg } 2963333197Savg 29641558Srgrimes /* 2965136051Sstefanf * Compress out duplicate. 29661558Srgrimes */ 29671558Srgrimes cr->cr_ngroups = ngroups - 1; 29681558Srgrimes cr->cr_groups[0] = groups[0]; 29691558Srgrimes for (cnt = 2; cnt < ngroups; cnt++) 29701558Srgrimes cr->cr_groups[cnt - 1] = groups[cnt]; 29711558Srgrimes return; 29721558Srgrimes } 29731558Srgrimes /* 29741558Srgrimes * Explicit credential specified as a colon separated list: 29751558Srgrimes * uid:gid:gid:... 29761558Srgrimes */ 29771558Srgrimes if (pw != NULL) 29781558Srgrimes cr->cr_uid = pw->pw_uid; 29791558Srgrimes else if (isdigit(*name) || *name == '-') 29801558Srgrimes cr->cr_uid = atoi(name); 29811558Srgrimes else { 298237663Scharnier syslog(LOG_ERR, "unknown user: %s", name); 29831558Srgrimes return; 29841558Srgrimes } 29851558Srgrimes cr->cr_ngroups = 0; 2986194498Sbrooks while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) { 2987347341Smav name = strsep_quote(&names, ":"); 29881558Srgrimes if (isdigit(*name) || *name == '-') { 29891558Srgrimes cr->cr_groups[cr->cr_ngroups++] = atoi(name); 29901558Srgrimes } else { 29911558Srgrimes if ((gr = getgrnam(name)) == NULL) { 299237663Scharnier syslog(LOG_ERR, "unknown group: %s", name); 29931558Srgrimes continue; 29941558Srgrimes } 29951558Srgrimes cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 29961558Srgrimes } 29971558Srgrimes } 2998194498Sbrooks if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS) 299937663Scharnier syslog(LOG_ERR, "too many groups"); 30001558Srgrimes} 30011558Srgrimes 3002194880Sdfr#define STRSIZ (MNTNAMLEN+MNTPATHLEN+50) 30031558Srgrimes/* 30041558Srgrimes * Routines that maintain the remote mounttab 30051558Srgrimes */ 3006285128Straszstatic void 3007216587Scharnierget_mountlist(void) 30081558Srgrimes{ 3009324955Smanu struct mountlist *mlp; 301023681Speter char *host, *dirp, *cp; 30111558Srgrimes char str[STRSIZ]; 30121558Srgrimes FILE *mlfile; 30131558Srgrimes 30141558Srgrimes if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 301553117Sbillf if (errno == ENOENT) 301653117Sbillf return; 301753117Sbillf else { 301853117Sbillf syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 301953117Sbillf return; 302053117Sbillf } 30211558Srgrimes } 30221558Srgrimes while (fgets(str, STRSIZ, mlfile) != NULL) { 302323681Speter cp = str; 302423681Speter host = strsep(&cp, " \t\n"); 302523681Speter dirp = strsep(&cp, " \t\n"); 302623681Speter if (host == NULL || dirp == NULL) 30271558Srgrimes continue; 30281558Srgrimes mlp = (struct mountlist *)malloc(sizeof (*mlp)); 302937663Scharnier if (mlp == (struct mountlist *)NULL) 303037663Scharnier out_of_mem(); 3031194880Sdfr strncpy(mlp->ml_host, host, MNTNAMLEN); 3032194880Sdfr mlp->ml_host[MNTNAMLEN] = '\0'; 3033194880Sdfr strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3034194880Sdfr mlp->ml_dirp[MNTPATHLEN] = '\0'; 3035324955Smanu 3036324955Smanu SLIST_INSERT_HEAD(&mlhead, mlp, next); 30371558Srgrimes } 30381558Srgrimes fclose(mlfile); 30391558Srgrimes} 30401558Srgrimes 3041285128Straszstatic void 304275635Siedowsedel_mlist(char *hostp, char *dirp) 30431558Srgrimes{ 3044324955Smanu struct mountlist *mlp, *mlp2; 30451558Srgrimes FILE *mlfile; 30461558Srgrimes int fnd = 0; 30471558Srgrimes 3048324955Smanu SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) { 30491558Srgrimes if (!strcmp(mlp->ml_host, hostp) && 30501558Srgrimes (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 30511558Srgrimes fnd = 1; 3052324955Smanu SLIST_REMOVE(&mlhead, mlp, mountlist, next); 3053324955Smanu free((caddr_t)mlp); 30541558Srgrimes } 30551558Srgrimes } 30561558Srgrimes if (fnd) { 30571558Srgrimes if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 305837663Scharnier syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 30591558Srgrimes return; 30601558Srgrimes } 3061324955Smanu SLIST_FOREACH(mlp, &mlhead, next) { 30621558Srgrimes fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 30631558Srgrimes } 30641558Srgrimes fclose(mlfile); 30651558Srgrimes } 30661558Srgrimes} 30671558Srgrimes 3068285128Straszstatic void 3069216587Scharnieradd_mlist(char *hostp, char *dirp) 30701558Srgrimes{ 3071324955Smanu struct mountlist *mlp; 30721558Srgrimes FILE *mlfile; 30731558Srgrimes 3074324955Smanu SLIST_FOREACH(mlp, &mlhead, next) { 30751558Srgrimes if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 30761558Srgrimes return; 30771558Srgrimes } 3078324955Smanu 30791558Srgrimes mlp = (struct mountlist *)malloc(sizeof (*mlp)); 308037663Scharnier if (mlp == (struct mountlist *)NULL) 308137663Scharnier out_of_mem(); 3082194880Sdfr strncpy(mlp->ml_host, hostp, MNTNAMLEN); 3083194880Sdfr mlp->ml_host[MNTNAMLEN] = '\0'; 3084194880Sdfr strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3085194880Sdfr mlp->ml_dirp[MNTPATHLEN] = '\0'; 3086324955Smanu SLIST_INSERT_HEAD(&mlhead, mlp, next); 30871558Srgrimes if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 308837663Scharnier syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 30891558Srgrimes return; 30901558Srgrimes } 30911558Srgrimes fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 30921558Srgrimes fclose(mlfile); 30931558Srgrimes} 30941558Srgrimes 30951558Srgrimes/* 30961558Srgrimes * Free up a group list. 30971558Srgrimes */ 3098285128Straszstatic void 3099216587Scharnierfree_grp(struct grouplist *grp) 31001558Srgrimes{ 31011558Srgrimes if (grp->gr_type == GT_HOST) { 310274462Salfred if (grp->gr_ptr.gt_addrinfo != NULL) 310374462Salfred freeaddrinfo(grp->gr_ptr.gt_addrinfo); 31041558Srgrimes } else if (grp->gr_type == GT_NET) { 31051558Srgrimes if (grp->gr_ptr.gt_net.nt_name) 31061558Srgrimes free(grp->gr_ptr.gt_net.nt_name); 31071558Srgrimes } 31081558Srgrimes free((caddr_t)grp); 31091558Srgrimes} 31101558Srgrimes 31111558Srgrimes#ifdef DEBUG 3112285128Straszstatic void 31131558SrgrimesSYSLOG(int pri, const char *fmt, ...) 31141558Srgrimes{ 31151558Srgrimes va_list ap; 31161558Srgrimes 31171558Srgrimes va_start(ap, fmt); 31181558Srgrimes vfprintf(stderr, fmt, ap); 31191558Srgrimes va_end(ap); 31201558Srgrimes} 31211558Srgrimes#endif /* DEBUG */ 31221558Srgrimes 31231558Srgrimes/* 31241558Srgrimes * Check options for consistency. 31251558Srgrimes */ 3126285128Straszstatic int 3127216587Scharniercheck_options(struct dirlist *dp) 31281558Srgrimes{ 31291558Srgrimes 3130192934Srmacklem if (v4root_phase == 0 && dp == NULL) 31311558Srgrimes return (1); 313283653Speter if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 313383653Speter syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 31341558Srgrimes return (1); 31351558Srgrimes } 31361558Srgrimes if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 313775801Siedowse syslog(LOG_ERR, "-mask requires -network"); 313875801Siedowse return (1); 31391558Srgrimes } 314075801Siedowse if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 314175801Siedowse syslog(LOG_ERR, "-network requires mask specification"); 314275801Siedowse return (1); 314375801Siedowse } 314475801Siedowse if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 314575801Siedowse syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 314675801Siedowse return (1); 314775801Siedowse } 3148192934Srmacklem if (v4root_phase > 0 && 3149192934Srmacklem (opt_flags & 3150192934Srmacklem ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) { 3151192934Srmacklem syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:"); 3152192934Srmacklem return (1); 3153192934Srmacklem } 3154207689Srmacklem if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 3155207689Srmacklem syslog(LOG_ERR, "-alldirs has multiple directories"); 3156207689Srmacklem return (1); 3157207689Srmacklem } 31581558Srgrimes return (0); 31591558Srgrimes} 31601558Srgrimes 31611558Srgrimes/* 31621558Srgrimes * Check an absolute directory path for any symbolic links. Return true 31631558Srgrimes */ 3164285128Straszstatic int 3165216587Scharniercheck_dirpath(char *dirp) 31661558Srgrimes{ 31671558Srgrimes char *cp; 31681558Srgrimes int ret = 1; 31691558Srgrimes struct stat sb; 31701558Srgrimes 31711558Srgrimes cp = dirp + 1; 31721558Srgrimes while (*cp && ret) { 31731558Srgrimes if (*cp == '/') { 31741558Srgrimes *cp = '\0'; 31759336Sdfr if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 31761558Srgrimes ret = 0; 31771558Srgrimes *cp = '/'; 31781558Srgrimes } 31791558Srgrimes cp++; 31801558Srgrimes } 31819336Sdfr if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 31821558Srgrimes ret = 0; 31831558Srgrimes return (ret); 31841558Srgrimes} 31859336Sdfr 318675801Siedowse/* 318775801Siedowse * Make a netmask according to the specified prefix length. The ss_family 318875801Siedowse * and other non-address fields must be initialised before calling this. 318975801Siedowse */ 3190285128Straszstatic int 319175801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen) 319274462Salfred{ 319375801Siedowse u_char *p; 319475801Siedowse int bits, i, len; 319574462Salfred 319675801Siedowse if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 319775801Siedowse return (-1); 3198103949Smike if (bitlen > len * CHAR_BIT) 319975801Siedowse return (-1); 320074462Salfred 320175801Siedowse for (i = 0; i < len; i++) { 3202298912Saraujo bits = MIN(CHAR_BIT, bitlen); 3203219125Sru *p++ = (u_char)~0 << (CHAR_BIT - bits); 320475801Siedowse bitlen -= bits; 320574462Salfred } 320675801Siedowse return 0; 320774462Salfred} 320874462Salfred 320975801Siedowse/* 321075801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask 321175801Siedowse * is acceptable (i.e. of the form 1...10....0). 321275801Siedowse */ 3213285128Straszstatic int 321475801Siedowsecheckmask(struct sockaddr *sa) 321574462Salfred{ 321675801Siedowse u_char *mask; 321775801Siedowse int i, len; 321874462Salfred 321975801Siedowse if ((mask = sa_rawaddr(sa, &len)) == NULL) 322075801Siedowse return (-1); 322175801Siedowse 322275801Siedowse for (i = 0; i < len; i++) 322375801Siedowse if (mask[i] != 0xff) 322475801Siedowse break; 322575801Siedowse if (i < len) { 322675801Siedowse if (~mask[i] & (u_char)(~mask[i] + 1)) 322775801Siedowse return (-1); 322875801Siedowse i++; 322974462Salfred } 323075801Siedowse for (; i < len; i++) 323175801Siedowse if (mask[i] != 0) 323275801Siedowse return (-1); 323375801Siedowse return (0); 323474462Salfred} 323574462Salfred 323675801Siedowse/* 323775801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if 323875801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'. 3239228990Suqs * If samask is NULL, perform a full comparison. 324075801Siedowse */ 3241285128Straszstatic int 324275801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 324374462Salfred{ 324475801Siedowse unsigned char *p1, *p2, *mask; 324575801Siedowse int len, i; 324674462Salfred 324775801Siedowse if (sa1->sa_family != sa2->sa_family || 324875801Siedowse (p1 = sa_rawaddr(sa1, &len)) == NULL || 324975801Siedowse (p2 = sa_rawaddr(sa2, NULL)) == NULL) 325075801Siedowse return (1); 325175801Siedowse 325275801Siedowse switch (sa1->sa_family) { 325374462Salfred case AF_INET6: 325475801Siedowse if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 325575801Siedowse ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 325675801Siedowse return (1); 325774462Salfred break; 325874462Salfred } 325974462Salfred 326075801Siedowse /* Simple binary comparison if no mask specified. */ 326175801Siedowse if (samask == NULL) 326275801Siedowse return (memcmp(p1, p2, len)); 326374462Salfred 326475801Siedowse /* Set up the mask, and do a mask-based comparison. */ 326575801Siedowse if (sa1->sa_family != samask->sa_family || 326675801Siedowse (mask = sa_rawaddr(samask, NULL)) == NULL) 326775801Siedowse return (1); 326874462Salfred 326975801Siedowse for (i = 0; i < len; i++) 327075801Siedowse if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 327175801Siedowse return (1); 327275801Siedowse return (0); 327374462Salfred} 327474462Salfred 327575801Siedowse/* 327675801Siedowse * Return a pointer to the part of the sockaddr that contains the 327775801Siedowse * raw address, and set *nbytes to its length in bytes. Returns 327875801Siedowse * NULL if the address family is unknown. 327975801Siedowse */ 3280285128Straszstatic void * 328175801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) { 328275801Siedowse void *p; 328374462Salfred int len; 328474462Salfred 328575801Siedowse switch (sa->sa_family) { 328674462Salfred case AF_INET: 328775801Siedowse len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 328875801Siedowse p = &((struct sockaddr_in *)sa)->sin_addr; 328974462Salfred break; 329074462Salfred case AF_INET6: 329175801Siedowse len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 329275801Siedowse p = &((struct sockaddr_in6 *)sa)->sin6_addr; 329374462Salfred break; 329474462Salfred default: 329575801Siedowse p = NULL; 329675801Siedowse len = 0; 329774462Salfred } 329874462Salfred 329975801Siedowse if (nbytes != NULL) 330075801Siedowse *nbytes = len; 330175801Siedowse return (p); 330274462Salfred} 330374462Salfred 3304285128Straszstatic void 3305216587Scharnierhuphandler(int sig __unused) 330675754Siedowse{ 3307285128Strasz 330875754Siedowse got_sighup = 1; 330975754Siedowse} 331075754Siedowse 3311285128Straszstatic void 3312285128Straszterminate(int sig __unused) 331374462Salfred{ 3314149433Spjd pidfile_remove(pfh); 3315194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 3316194880Sdfr rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 331774462Salfred exit (0); 331874462Salfred} 3319