mountd.c revision 83653
1153323Srodrigc/* 2159451Srodrigc * Copyright (c) 1989, 1993 3159451Srodrigc * The Regents of the University of California. All rights reserved. 4153323Srodrigc * 5159451Srodrigc * This code is derived from software contributed to Berkeley by 6159451Srodrigc * Herb Hasler and Rick Macklem at The University of Guelph. 7153323Srodrigc * 8153323Srodrigc * Redistribution and use in source and binary forms, with or without 9159451Srodrigc * modification, are permitted provided that the following conditions 10159451Srodrigc * are met: 11159451Srodrigc * 1. Redistributions of source code must retain the above copyright 12159451Srodrigc * notice, this list of conditions and the following disclaimer. 13153323Srodrigc * 2. Redistributions in binary form must reproduce the above copyright 14159451Srodrigc * notice, this list of conditions and the following disclaimer in the 15159451Srodrigc * documentation and/or other materials provided with the distribution. 16159451Srodrigc * 3. All advertising materials mentioning features or use of this software 17153323Srodrigc * must display the following acknowledgement: 18153323Srodrigc * This product includes software developed by the University of 19159451Srodrigc * California, Berkeley and its contributors. 20153323Srodrigc * 4. Neither the name of the University nor the names of its contributors 21159451Srodrigc * may be used to endorse or promote products derived from this software 22153323Srodrigc * without specific prior written permission. 23159451Srodrigc * 24153323Srodrigc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25153323Srodrigc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26153323Srodrigc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27153323Srodrigc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28153323Srodrigc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29153323Srodrigc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30153323Srodrigc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31153323Srodrigc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32159451Srodrigc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33153323Srodrigc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34153323Srodrigc * SUCH DAMAGE. 35153323Srodrigc */ 36153323Srodrigc 37153323Srodrigc#ifndef lint 38153323Srodrigcstatic const char copyright[] = 39153323Srodrigc"@(#) Copyright (c) 1989, 1993\n\ 40153323Srodrigc The Regents of the University of California. All rights reserved.\n"; 41153323Srodrigc#endif /*not lint*/ 42153323Srodrigc 43153323Srodrigc#ifndef lint 44153323Srodrigc#if 0 45153323Srodrigcstatic char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; 46153323Srodrigc#endif 47153323Srodrigcstatic const char rcsid[] = 48153323Srodrigc "$FreeBSD: head/usr.sbin/mountd/mountd.c 83653 2001-09-18 23:34:44Z peter $"; 49153323Srodrigc#endif /*not lint*/ 50153323Srodrigc 51153323Srodrigc#include <sys/param.h> 52153323Srodrigc#include <sys/mount.h> 53153323Srodrigc#include <sys/fcntl.h> 54153323Srodrigc#include <sys/stat.h> 55153323Srodrigc#include <sys/syslog.h> 56153323Srodrigc#include <sys/sysctl.h> 57153323Srodrigc 58153323Srodrigc#include <rpc/rpc.h> 59153323Srodrigc#include <rpc/pmap_clnt.h> 60153323Srodrigc#include <rpc/pmap_prot.h> 61153323Srodrigc#include <rpcsvc/mount.h> 62153323Srodrigc#include <nfs/rpcv2.h> 63153323Srodrigc#include <nfs/nfsproto.h> 64153323Srodrigc#include <nfsserver/nfs.h> 65153323Srodrigc#include <ufs/ufs/ufsmount.h> 66153323Srodrigc#include <fs/msdosfs/msdosfsmount.h> 67153323Srodrigc#include <fs/ntfs/ntfsmount.h> 68153323Srodrigc#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */ 69153323Srodrigc 70153323Srodrigc#include <arpa/inet.h> 71153323Srodrigc 72153323Srodrigc#include <ctype.h> 73153323Srodrigc#include <err.h> 74153323Srodrigc#include <errno.h> 75153323Srodrigc#include <grp.h> 76153323Srodrigc#include <netdb.h> 77153323Srodrigc#include <pwd.h> 78153323Srodrigc#include <signal.h> 79153323Srodrigc#include <stdio.h> 80153323Srodrigc#include <stdlib.h> 81153323Srodrigc#include <string.h> 82153323Srodrigc#include <unistd.h> 83153323Srodrigc#include "pathnames.h" 84153323Srodrigc 85153323Srodrigc#ifdef DEBUG 86153323Srodrigc#include <stdarg.h> 87153323Srodrigc#endif 88153323Srodrigc 89153323Srodrigc#ifndef MOUNTDLOCK 90153323Srodrigc#define MOUNTDLOCK "/var/run/mountd.lock" 91153323Srodrigc#endif 92153323Srodrigc 93153323Srodrigc/* 94153323Srodrigc * Structures for keeping the mount list and export list 95153323Srodrigc */ 96153323Srodrigcstruct mountlist { 97153323Srodrigc struct mountlist *ml_next; 98153323Srodrigc char ml_host[RPCMNT_NAMELEN+1]; 99153323Srodrigc char ml_dirp[RPCMNT_PATHLEN+1]; 100153323Srodrigc}; 101153323Srodrigc 102153323Srodrigcstruct dirlist { 103153323Srodrigc struct dirlist *dp_left; 104153323Srodrigc struct dirlist *dp_right; 105153323Srodrigc int dp_flag; 106153323Srodrigc struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 107153323Srodrigc char dp_dirp[1]; /* Actually malloc'd to size of dir */ 108153323Srodrigc}; 109153323Srodrigc/* dp_flag bits */ 110153323Srodrigc#define DP_DEFSET 0x1 111153323Srodrigc#define DP_HOSTSET 0x2 112153323Srodrigc 113153323Srodrigcstruct exportlist { 114153323Srodrigc struct exportlist *ex_next; 115153323Srodrigc struct dirlist *ex_dirl; 116153323Srodrigc struct dirlist *ex_defdir; 117153323Srodrigc int ex_flag; 118153323Srodrigc fsid_t ex_fs; 119153323Srodrigc char *ex_fsdir; 120153323Srodrigc char *ex_indexfile; 121153323Srodrigc}; 122153323Srodrigc/* ex_flag bits */ 123153323Srodrigc#define EX_LINKED 0x1 124153323Srodrigc 125153323Srodrigcstruct netmsk { 126153323Srodrigc struct sockaddr_storage nt_net; 127153323Srodrigc struct sockaddr_storage nt_mask; 128153323Srodrigc char *nt_name; 129153323Srodrigc}; 130153323Srodrigc 131153323Srodrigcunion grouptypes { 132153323Srodrigc struct addrinfo *gt_addrinfo; 133153323Srodrigc struct netmsk gt_net; 134153323Srodrigc}; 135153323Srodrigc 136153323Srodrigcstruct grouplist { 137153323Srodrigc int gr_type; 138153323Srodrigc union grouptypes gr_ptr; 139153323Srodrigc struct grouplist *gr_next; 140153323Srodrigc}; 141153323Srodrigc/* Group types */ 142153323Srodrigc#define GT_NULL 0x0 143153323Srodrigc#define GT_HOST 0x1 144153323Srodrigc#define GT_NET 0x2 145153323Srodrigc#define GT_DEFAULT 0x3 146153323Srodrigc#define GT_IGNORE 0x5 147153323Srodrigc 148153323Srodrigcstruct hostlist { 149153323Srodrigc int ht_flag; /* Uses DP_xx bits */ 150153323Srodrigc struct grouplist *ht_grp; 151153323Srodrigc struct hostlist *ht_next; 152153323Srodrigc}; 153153323Srodrigc 154153323Srodrigcstruct fhreturn { 155153323Srodrigc int fhr_flag; 156153323Srodrigc int fhr_vers; 157153323Srodrigc nfsfh_t fhr_fh; 158153323Srodrigc}; 159153323Srodrigc 160153323Srodrigc/* Global defs */ 161153323Srodrigcchar *add_expdir __P((struct dirlist **, char *, int)); 162153323Srodrigcvoid add_dlist __P((struct dirlist **, struct dirlist *, 163153323Srodrigc struct grouplist *, int)); 164153323Srodrigcvoid add_mlist __P((char *, char *)); 165153323Srodrigcint check_dirpath __P((char *)); 166153323Srodrigcint check_options __P((struct dirlist *)); 167153323Srodrigcint checkmask(struct sockaddr *sa); 168153323Srodrigcint chk_host __P((struct dirlist *, struct sockaddr *, int *, int *)); 169153323Srodrigcvoid del_mlist(char *hostp, char *dirp); 170153323Srodrigcstruct dirlist *dirp_search __P((struct dirlist *, char *)); 171153323Srodrigcint do_mount __P((struct exportlist *, struct grouplist *, int, 172153323Srodrigc struct xucred *, char *, int, struct statfs *)); 173153323Srodrigcint do_opt __P((char **, char **, struct exportlist *, struct grouplist *, 174153323Srodrigc int *, int *, struct xucred *)); 175153323Srodrigcstruct exportlist *ex_search __P((fsid_t *)); 176153323Srodrigcstruct exportlist *get_exp __P((void)); 177153323Srodrigcvoid free_dir __P((struct dirlist *)); 178153323Srodrigcvoid free_exp __P((struct exportlist *)); 179153323Srodrigcvoid free_grp __P((struct grouplist *)); 180153323Srodrigcvoid free_host __P((struct hostlist *)); 181153323Srodrigcvoid get_exportlist __P((void)); 182153323Srodrigcint get_host __P((char *, struct grouplist *, struct grouplist *)); 183153323Srodrigcstruct hostlist *get_ht __P((void)); 184153323Srodrigcint get_line __P((void)); 185153323Srodrigcvoid get_mountlist __P((void)); 186153323Srodrigcint get_net __P((char *, struct netmsk *, int)); 187153323Srodrigcvoid getexp_err __P((struct exportlist *, struct grouplist *)); 188153323Srodrigcstruct grouplist *get_grp __P((void)); 189153323Srodrigcvoid hang_dirp __P((struct dirlist *, struct grouplist *, 190153323Srodrigc struct exportlist *, int)); 191153323Srodrigcvoid huphandler(int sig); 192153323Srodrigcint makemask(struct sockaddr_storage *ssp, int bitlen); 193153323Srodrigcvoid mntsrv __P((struct svc_req *, SVCXPRT *)); 194153323Srodrigcvoid nextfield __P((char **, char **)); 195153323Srodrigcvoid out_of_mem __P((void)); 196153323Srodrigcvoid parsecred __P((char *, struct xucred *)); 197153323Srodrigcint put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); 198153323Srodrigcvoid *sa_rawaddr(struct sockaddr *sa, int *nbytes); 199153323Srodrigcint sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 200153323Srodrigc struct sockaddr *samask); 201153323Srodrigcint scan_tree __P((struct dirlist *, struct sockaddr *)); 202153323Srodrigcstatic void usage __P((void)); 203153323Srodrigcint xdr_dir __P((XDR *, char *)); 204153323Srodrigcint xdr_explist __P((XDR *, caddr_t)); 205153323Srodrigcint xdr_fhs __P((XDR *, caddr_t)); 206153323Srodrigcint xdr_mlist __P((XDR *, caddr_t)); 207153323Srodrigcvoid terminate __P((int)); 208153323Srodrigc 209153323Srodrigcstruct exportlist *exphead; 210153323Srodrigcstruct mountlist *mlhead; 211153323Srodrigcstruct grouplist *grphead; 212153323Srodrigcchar exname[MAXPATHLEN]; 213153323Srodrigcstruct xucred def_anon = { 214153323Srodrigc 0, 215153323Srodrigc (uid_t)-2, 216153323Srodrigc 1, 217153323Srodrigc { (gid_t)-2 }, 218153323Srodrigc NULL 219153323Srodrigc}; 220153323Srodrigcint force_v2 = 0; 221153323Srodrigcint resvport_only = 1; 222153323Srodrigcint dir_only = 1; 223153323Srodrigcint log = 0; 224153323Srodrigcint got_sighup = 0; 225153323Srodrigc 226153323Srodrigcint opt_flags; 227153323Srodrigcstatic int have_v6 = 1; 228153323Srodrigc#ifdef NI_WITHSCOPEID 229153323Srodrigcstatic const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID; 230153323Srodrigc#else 231153323Srodrigcstatic const int ninumeric = NI_NUMERICHOST; 232153323Srodrigc#endif 233153323Srodrigc 234153323Srodrigcint mountdlockfd; 235153323Srodrigc/* Bits for opt_flags above */ 236153323Srodrigc#define OP_MAPROOT 0x01 237153323Srodrigc#define OP_MAPALL 0x02 238153323Srodrigc/* 0x4 free */ 239153323Srodrigc#define OP_MASK 0x08 240153323Srodrigc#define OP_NET 0x10 241153323Srodrigc#define OP_ALLDIRS 0x40 242153323Srodrigc#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 243153323Srodrigc#define OP_MASKLEN 0x200 244153323Srodrigc 245153323Srodrigc#ifdef DEBUG 246153323Srodrigcint debug = 1; 247153323Srodrigcvoid SYSLOG __P((int, const char *, ...)) __printflike(2, 3); 248153323Srodrigc#define syslog SYSLOG 249153323Srodrigc#else 250153323Srodrigcint debug = 0; 251153323Srodrigc#endif 252153323Srodrigc 253153323Srodrigc/* 254153323Srodrigc * Mountd server for NFS mount protocol as described in: 255153323Srodrigc * NFS: Network File System Protocol Specification, RFC1094, Appendix A 256153323Srodrigc * The optional arguments are the exports file name 257153323Srodrigc * default: _PATH_EXPORTS 258153323Srodrigc * and "-n" to allow nonroot mount. 259153323Srodrigc */ 260153323Srodrigcint 261153323Srodrigcmain(argc, argv) 262153323Srodrigc int argc; 263153323Srodrigc char **argv; 264153323Srodrigc{ 265153323Srodrigc fd_set readfds; 266159451Srodrigc SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp; 267153323Srodrigc struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf; 268153323Srodrigc int udpsock, tcpsock, udp6sock, tcp6sock; 269153323Srodrigc int xcreated = 0, s; 270153323Srodrigc int one = 1; 271153323Srodrigc int c, error, mib[3]; 272153323Srodrigc struct vfsconf vfc; 273153323Srodrigc 274153323Srodrigc udp6conf = tcp6conf = NULL; 275153323Srodrigc udp6sock = tcp6sock = NULL; 276153323Srodrigc 277153323Srodrigc /* Check that another mountd isn't already running. */ 278153323Srodrigc if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) 279153323Srodrigc err(1, "%s", MOUNTDLOCK); 280153323Srodrigc 281153323Srodrigc if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) 282153323Srodrigc errx(1, "another rpc.mountd is already running. Aborting"); 283153323Srodrigc s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 284153323Srodrigc if (s < 0) 285153323Srodrigc have_v6 = 0; 286153323Srodrigc else 287153323Srodrigc close(s); 288153323Srodrigc error = getvfsbyname("nfs", &vfc); 289153323Srodrigc if (error && vfsisloadable("nfs")) { 290153323Srodrigc if(vfsload("nfs")) 291153323Srodrigc err(1, "vfsload(nfs)"); 292153323Srodrigc endvfsent(); /* flush cache */ 293153323Srodrigc error = getvfsbyname("nfs", &vfc); 294153323Srodrigc } 295153323Srodrigc if (error) 296153323Srodrigc errx(1, "NFS support is not available in the running kernel"); 297153323Srodrigc 298153323Srodrigc while ((c = getopt(argc, argv, "2dlnr")) != -1) 299153323Srodrigc switch (c) { 300153323Srodrigc case '2': 301153323Srodrigc force_v2 = 1; 302153323Srodrigc break; 303153323Srodrigc case 'n': 304153323Srodrigc resvport_only = 0; 305153323Srodrigc break; 306153323Srodrigc case 'r': 307153323Srodrigc dir_only = 0; 308153323Srodrigc break; 309153323Srodrigc case 'd': 310153323Srodrigc debug = debug ? 0 : 1; 311153323Srodrigc break; 312 case 'l': 313 log = 1; 314 break; 315 default: 316 usage(); 317 }; 318 argc -= optind; 319 argv += optind; 320 grphead = (struct grouplist *)NULL; 321 exphead = (struct exportlist *)NULL; 322 mlhead = (struct mountlist *)NULL; 323 if (argc == 1) { 324 strncpy(exname, *argv, MAXPATHLEN-1); 325 exname[MAXPATHLEN-1] = '\0'; 326 } else 327 strcpy(exname, _PATH_EXPORTS); 328 openlog("mountd", LOG_PID, LOG_DAEMON); 329 if (debug) 330 warnx("getting export list"); 331 get_exportlist(); 332 if (debug) 333 warnx("getting mount list"); 334 get_mountlist(); 335 if (debug) 336 warnx("here we go"); 337 if (debug == 0) { 338 daemon(0, 0); 339 signal(SIGINT, SIG_IGN); 340 signal(SIGQUIT, SIG_IGN); 341 } 342 signal(SIGHUP, huphandler); 343 signal(SIGTERM, terminate); 344 { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); 345 if (pidfile != NULL) { 346 fprintf(pidfile, "%d\n", getpid()); 347 fclose(pidfile); 348 } 349 } 350 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); 351 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); 352 udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 353 tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 354 udpconf = getnetconfigent("udp"); 355 tcpconf = getnetconfigent("tcp"); 356 if (!have_v6) 357 goto skip_v6; 358 udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 359 tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 360 /* 361 * We're doing host-based access checks here, so don't allow 362 * v4-in-v6 to confuse things. The kernel will disable it 363 * by default on NFS sockets too. 364 */ 365 if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6, 366 IPV6_BINDV6ONLY, &one, sizeof one) < 0){ 367 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); 368 exit(1); 369 } 370 if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6, 371 IPV6_BINDV6ONLY, &one, sizeof one) < 0){ 372 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); 373 exit(1); 374 } 375 udp6conf = getnetconfigent("udp6"); 376 tcp6conf = getnetconfigent("tcp6"); 377 378skip_v6: 379 if (!resvport_only) { 380 mib[0] = CTL_VFS; 381 mib[1] = vfc.vfc_typenum; 382 mib[2] = NFS_NFSPRIVPORT; 383 if (sysctl(mib, 3, NULL, NULL, &resvport_only, 384 sizeof(resvport_only)) != 0 && errno != ENOENT) { 385 syslog(LOG_ERR, "sysctl: %m"); 386 exit(1); 387 } 388 } 389 if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL || 390 (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) { 391 syslog(LOG_ERR, "can't create socket"); 392 exit(1); 393 } 394 if (udpsock != -1 && udpconf != NULL) { 395 bindresvport(udpsock, NULL); 396 udptransp = svc_dg_create(udpsock, 0, 0); 397 if (udptransp != NULL) { 398 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1, 399 mntsrv, udpconf)) 400 syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service"); 401 else 402 xcreated++; 403 if (!force_v2) { 404 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3, 405 mntsrv, udpconf)) 406 syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service"); 407 else 408 xcreated++; 409 } 410 } else 411 syslog(LOG_WARNING, "can't create UDP services"); 412 413 } 414 if (tcpsock != -1 && tcpconf != NULL) { 415 bindresvport(tcpsock, NULL); 416 listen(tcpsock, SOMAXCONN); 417 tcptransp = svc_vc_create(tcpsock, 0, 0); 418 if (tcptransp != NULL) { 419 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1, 420 mntsrv, tcpconf)) 421 syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service"); 422 else 423 xcreated++; 424 if (!force_v2) { 425 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3, 426 mntsrv, tcpconf)) 427 syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service"); 428 else 429 xcreated++; 430 } 431 } else 432 syslog(LOG_WARNING, "can't create TCP service"); 433 434 } 435 if (have_v6 && udp6sock != -1 && udp6conf != NULL) { 436 bindresvport(udp6sock, NULL); 437 udp6transp = svc_dg_create(udp6sock, 0, 0); 438 if (udp6transp != NULL) { 439 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1, 440 mntsrv, udp6conf)) 441 syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service"); 442 else 443 xcreated++; 444 if (!force_v2) { 445 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3, 446 mntsrv, udp6conf)) 447 syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service"); 448 else 449 xcreated++; 450 } 451 } else 452 syslog(LOG_WARNING, "can't create UDP6 service"); 453 454 } 455 if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) { 456 bindresvport(tcp6sock, NULL); 457 listen(tcp6sock, SOMAXCONN); 458 tcp6transp = svc_vc_create(tcp6sock, 0, 0); 459 if (tcp6transp != NULL) { 460 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1, 461 mntsrv, tcp6conf)) 462 syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service"); 463 else 464 xcreated++; 465 if (!force_v2) { 466 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3, 467 mntsrv, tcp6conf)) 468 syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service"); 469 else 470 xcreated++; 471 } 472 } else 473 syslog(LOG_WARNING, "can't create TCP6 service"); 474 475 } 476 if (xcreated == 0) { 477 syslog(LOG_ERR, "could not create any services"); 478 exit(1); 479 } 480 481 /* Expand svc_run() here so that we can call get_exportlist(). */ 482 for (;;) { 483 if (got_sighup) { 484 get_exportlist(); 485 got_sighup = 0; 486 } 487 readfds = svc_fdset; 488 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) { 489 case -1: 490 if (errno == EINTR) 491 continue; 492 syslog(LOG_ERR, "mountd died: select: %m"); 493 exit(1); 494 case 0: 495 continue; 496 default: 497 svc_getreqset(&readfds); 498 } 499 } 500} 501 502static void 503usage() 504{ 505 fprintf(stderr, 506 "usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n"); 507 exit(1); 508} 509 510/* 511 * The mount rpc service 512 */ 513void 514mntsrv(rqstp, transp) 515 struct svc_req *rqstp; 516 SVCXPRT *transp; 517{ 518 struct exportlist *ep; 519 struct dirlist *dp; 520 struct fhreturn fhr; 521 struct stat stb; 522 struct statfs fsb; 523 struct addrinfo *ai; 524 char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 525 int lookup_failed = 1; 526 struct sockaddr *saddr; 527 u_short sport; 528 char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; 529 int bad = 0, defset, hostset; 530 sigset_t sighup_mask; 531 532 sigemptyset(&sighup_mask); 533 sigaddset(&sighup_mask, SIGHUP); 534 saddr = svc_getrpccaller(transp)->buf; 535 switch (saddr->sa_family) { 536 case AF_INET6: 537 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 538 break; 539 case AF_INET: 540 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 541 break; 542 default: 543 syslog(LOG_ERR, "request from unknown address family"); 544 return; 545 } 546 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 547 NULL, 0, 0); 548 getnameinfo(saddr, saddr->sa_len, numerichost, 549 sizeof numerichost, NULL, 0, NI_NUMERICHOST); 550 ai = NULL; 551 switch (rqstp->rq_proc) { 552 case NULLPROC: 553 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 554 syslog(LOG_ERR, "can't send reply"); 555 return; 556 case RPCMNT_MOUNT: 557 if (sport >= IPPORT_RESERVED && resvport_only) { 558 syslog(LOG_NOTICE, 559 "mount request from %s from unprivileged port", 560 numerichost); 561 svcerr_weakauth(transp); 562 return; 563 } 564 if (!svc_getargs(transp, xdr_dir, rpcpath)) { 565 syslog(LOG_NOTICE, "undecodable mount request from %s", 566 numerichost); 567 svcerr_decode(transp); 568 return; 569 } 570 571 /* 572 * Get the real pathname and make sure it is a directory 573 * or a regular file if the -r option was specified 574 * and it exists. 575 */ 576 if (realpath(rpcpath, dirpath) == NULL || 577 stat(dirpath, &stb) < 0 || 578 (!S_ISDIR(stb.st_mode) && 579 (dir_only || !S_ISREG(stb.st_mode))) || 580 statfs(dirpath, &fsb) < 0) { 581 chdir("/"); /* Just in case realpath doesn't */ 582 syslog(LOG_NOTICE, 583 "mount request from %s for non existent path %s", 584 numerichost, dirpath); 585 if (debug) 586 warnx("stat failed on %s", dirpath); 587 bad = ENOENT; /* We will send error reply later */ 588 } 589 590 /* Check in the exports list */ 591 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 592 ep = ex_search(&fsb.f_fsid); 593 hostset = defset = 0; 594 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || 595 ((dp = dirp_search(ep->ex_dirl, dirpath)) && 596 chk_host(dp, saddr, &defset, &hostset)) || 597 (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 598 scan_tree(ep->ex_dirl, saddr) == 0))) { 599 if (bad) { 600 if (!svc_sendreply(transp, xdr_long, 601 (caddr_t)&bad)) 602 syslog(LOG_ERR, "can't send reply"); 603 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 604 return; 605 } 606 if (hostset & DP_HOSTSET) 607 fhr.fhr_flag = hostset; 608 else 609 fhr.fhr_flag = defset; 610 fhr.fhr_vers = rqstp->rq_vers; 611 /* Get the file handle */ 612 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 613 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 614 bad = errno; 615 syslog(LOG_ERR, "can't get fh for %s", dirpath); 616 if (!svc_sendreply(transp, xdr_long, 617 (caddr_t)&bad)) 618 syslog(LOG_ERR, "can't send reply"); 619 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 620 return; 621 } 622 if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) 623 syslog(LOG_ERR, "can't send reply"); 624 if (!lookup_failed) 625 add_mlist(host, dirpath); 626 else 627 add_mlist(numerichost, dirpath); 628 if (debug) 629 warnx("mount successful"); 630 if (log) 631 syslog(LOG_NOTICE, 632 "mount request succeeded from %s for %s", 633 numerichost, dirpath); 634 } else { 635 bad = EACCES; 636 syslog(LOG_NOTICE, 637 "mount request denied from %s for %s", 638 numerichost, dirpath); 639 } 640 641 if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad)) 642 syslog(LOG_ERR, "can't send reply"); 643 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 644 return; 645 case RPCMNT_DUMP: 646 if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL)) 647 syslog(LOG_ERR, "can't send reply"); 648 else if (log) 649 syslog(LOG_NOTICE, 650 "dump request succeeded from %s", 651 numerichost); 652 return; 653 case RPCMNT_UMOUNT: 654 if (sport >= IPPORT_RESERVED && resvport_only) { 655 syslog(LOG_NOTICE, 656 "umount request from %s from unprivileged port", 657 numerichost); 658 svcerr_weakauth(transp); 659 return; 660 } 661 if (!svc_getargs(transp, xdr_dir, rpcpath)) { 662 syslog(LOG_NOTICE, "undecodable umount request from %s", 663 numerichost); 664 svcerr_decode(transp); 665 return; 666 } 667 if (realpath(rpcpath, dirpath) == NULL) { 668 syslog(LOG_NOTICE, "umount request from %s " 669 "for non existent path %s", 670 numerichost, dirpath); 671 } 672 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 673 syslog(LOG_ERR, "can't send reply"); 674 if (!lookup_failed) 675 del_mlist(host, dirpath); 676 del_mlist(numerichost, dirpath); 677 if (log) 678 syslog(LOG_NOTICE, 679 "umount request succeeded from %s for %s", 680 numerichost, dirpath); 681 return; 682 case RPCMNT_UMNTALL: 683 if (sport >= IPPORT_RESERVED && resvport_only) { 684 syslog(LOG_NOTICE, 685 "umountall request from %s from unprivileged port", 686 numerichost); 687 svcerr_weakauth(transp); 688 return; 689 } 690 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) 691 syslog(LOG_ERR, "can't send reply"); 692 if (!lookup_failed) 693 del_mlist(host, NULL); 694 del_mlist(numerichost, NULL); 695 if (log) 696 syslog(LOG_NOTICE, 697 "umountall request succeeded from %s", 698 numerichost); 699 return; 700 case RPCMNT_EXPORT: 701 if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) 702 syslog(LOG_ERR, "can't send reply"); 703 if (log) 704 syslog(LOG_NOTICE, 705 "export request succeeded from %s", 706 numerichost); 707 return; 708 default: 709 svcerr_noproc(transp); 710 return; 711 } 712} 713 714/* 715 * Xdr conversion for a dirpath string 716 */ 717int 718xdr_dir(xdrsp, dirp) 719 XDR *xdrsp; 720 char *dirp; 721{ 722 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 723} 724 725/* 726 * Xdr routine to generate file handle reply 727 */ 728int 729xdr_fhs(xdrsp, cp) 730 XDR *xdrsp; 731 caddr_t cp; 732{ 733 register struct fhreturn *fhrp = (struct fhreturn *)cp; 734 u_long ok = 0, len, auth; 735 736 if (!xdr_long(xdrsp, &ok)) 737 return (0); 738 switch (fhrp->fhr_vers) { 739 case 1: 740 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 741 case 3: 742 len = NFSX_V3FH; 743 if (!xdr_long(xdrsp, &len)) 744 return (0); 745 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 746 return (0); 747 auth = RPCAUTH_UNIX; 748 len = 1; 749 if (!xdr_long(xdrsp, &len)) 750 return (0); 751 return (xdr_long(xdrsp, &auth)); 752 }; 753 return (0); 754} 755 756int 757xdr_mlist(xdrsp, cp) 758 XDR *xdrsp; 759 caddr_t cp; 760{ 761 struct mountlist *mlp; 762 int true = 1; 763 int false = 0; 764 char *strp; 765 766 mlp = mlhead; 767 while (mlp) { 768 if (!xdr_bool(xdrsp, &true)) 769 return (0); 770 strp = &mlp->ml_host[0]; 771 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 772 return (0); 773 strp = &mlp->ml_dirp[0]; 774 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 775 return (0); 776 mlp = mlp->ml_next; 777 } 778 if (!xdr_bool(xdrsp, &false)) 779 return (0); 780 return (1); 781} 782 783/* 784 * Xdr conversion for export list 785 */ 786int 787xdr_explist(xdrsp, cp) 788 XDR *xdrsp; 789 caddr_t cp; 790{ 791 struct exportlist *ep; 792 int false = 0; 793 int putdef; 794 sigset_t sighup_mask; 795 796 sigemptyset(&sighup_mask); 797 sigaddset(&sighup_mask, SIGHUP); 798 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 799 ep = exphead; 800 while (ep) { 801 putdef = 0; 802 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef)) 803 goto errout; 804 if (ep->ex_defdir && putdef == 0 && 805 put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, 806 &putdef)) 807 goto errout; 808 ep = ep->ex_next; 809 } 810 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 811 if (!xdr_bool(xdrsp, &false)) 812 return (0); 813 return (1); 814errout: 815 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 816 return (0); 817} 818 819/* 820 * Called from xdr_explist() to traverse the tree and export the 821 * directory paths. 822 */ 823int 824put_exlist(dp, xdrsp, adp, putdefp) 825 struct dirlist *dp; 826 XDR *xdrsp; 827 struct dirlist *adp; 828 int *putdefp; 829{ 830 struct grouplist *grp; 831 struct hostlist *hp; 832 int true = 1; 833 int false = 0; 834 int gotalldir = 0; 835 char *strp; 836 837 if (dp) { 838 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp)) 839 return (1); 840 if (!xdr_bool(xdrsp, &true)) 841 return (1); 842 strp = dp->dp_dirp; 843 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 844 return (1); 845 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 846 gotalldir = 1; 847 *putdefp = 1; 848 } 849 if ((dp->dp_flag & DP_DEFSET) == 0 && 850 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 851 hp = dp->dp_hosts; 852 while (hp) { 853 grp = hp->ht_grp; 854 if (grp->gr_type == GT_HOST) { 855 if (!xdr_bool(xdrsp, &true)) 856 return (1); 857 strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 858 if (!xdr_string(xdrsp, &strp, 859 RPCMNT_NAMELEN)) 860 return (1); 861 } else if (grp->gr_type == GT_NET) { 862 if (!xdr_bool(xdrsp, &true)) 863 return (1); 864 strp = grp->gr_ptr.gt_net.nt_name; 865 if (!xdr_string(xdrsp, &strp, 866 RPCMNT_NAMELEN)) 867 return (1); 868 } 869 hp = hp->ht_next; 870 if (gotalldir && hp == (struct hostlist *)NULL) { 871 hp = adp->dp_hosts; 872 gotalldir = 0; 873 } 874 } 875 } 876 if (!xdr_bool(xdrsp, &false)) 877 return (1); 878 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp)) 879 return (1); 880 } 881 return (0); 882} 883 884#define LINESIZ 10240 885char line[LINESIZ]; 886FILE *exp_file; 887 888/* 889 * Get the export list 890 */ 891void 892get_exportlist() 893{ 894 struct exportlist *ep, *ep2; 895 struct grouplist *grp, *tgrp; 896 struct exportlist **epp; 897 struct dirlist *dirhead; 898 struct statfs fsb, *fsp; 899 struct xucred anon; 900 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 901 int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; 902 903 dirp = NULL; 904 dirplen = 0; 905 906 /* 907 * First, get rid of the old list 908 */ 909 ep = exphead; 910 while (ep) { 911 ep2 = ep; 912 ep = ep->ex_next; 913 free_exp(ep2); 914 } 915 exphead = (struct exportlist *)NULL; 916 917 grp = grphead; 918 while (grp) { 919 tgrp = grp; 920 grp = grp->gr_next; 921 free_grp(tgrp); 922 } 923 grphead = (struct grouplist *)NULL; 924 925 /* 926 * And delete exports that are in the kernel for all local 927 * file systems. 928 * XXX: Should know how to handle all local exportable file systems 929 * instead of just "ufs". 930 */ 931 num = getmntinfo(&fsp, MNT_NOWAIT); 932 for (i = 0; i < num; i++) { 933 union { 934 struct ufs_args ua; 935 struct iso_args ia; 936 struct msdosfs_args da; 937 struct ntfs_args na; 938 } targs; 939 940 if (!strcmp(fsp->f_fstypename, "ufs") || 941 !strcmp(fsp->f_fstypename, "msdosfs") || 942 !strcmp(fsp->f_fstypename, "ntfs") || 943 !strcmp(fsp->f_fstypename, "cd9660")) { 944 targs.ua.fspec = NULL; 945 targs.ua.export.ex_flags = MNT_DELEXPORT; 946 if (mount(fsp->f_fstypename, fsp->f_mntonname, 947 fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 && 948 errno != ENOENT) 949 syslog(LOG_ERR, 950 "can't delete exports for %s: %m", 951 fsp->f_mntonname); 952 } 953 fsp++; 954 } 955 956 /* 957 * Read in the exports file and build the list, calling 958 * mount() as we go along to push the export rules into the kernel. 959 */ 960 if ((exp_file = fopen(exname, "r")) == NULL) { 961 syslog(LOG_ERR, "can't open %s", exname); 962 exit(2); 963 } 964 dirhead = (struct dirlist *)NULL; 965 while (get_line()) { 966 if (debug) 967 warnx("got line %s", line); 968 cp = line; 969 nextfield(&cp, &endcp); 970 if (*cp == '#') 971 goto nextline; 972 973 /* 974 * Set defaults. 975 */ 976 has_host = FALSE; 977 anon = def_anon; 978 exflags = MNT_EXPORTED; 979 got_nondir = 0; 980 opt_flags = 0; 981 ep = (struct exportlist *)NULL; 982 983 /* 984 * Create new exports list entry 985 */ 986 len = endcp-cp; 987 tgrp = grp = get_grp(); 988 while (len > 0) { 989 if (len > RPCMNT_NAMELEN) { 990 getexp_err(ep, tgrp); 991 goto nextline; 992 } 993 if (*cp == '-') { 994 if (ep == (struct exportlist *)NULL) { 995 getexp_err(ep, tgrp); 996 goto nextline; 997 } 998 if (debug) 999 warnx("doing opt %s", cp); 1000 got_nondir = 1; 1001 if (do_opt(&cp, &endcp, ep, grp, &has_host, 1002 &exflags, &anon)) { 1003 getexp_err(ep, tgrp); 1004 goto nextline; 1005 } 1006 } else if (*cp == '/') { 1007 savedc = *endcp; 1008 *endcp = '\0'; 1009 if (check_dirpath(cp) && 1010 statfs(cp, &fsb) >= 0) { 1011 if (got_nondir) { 1012 syslog(LOG_ERR, "dirs must be first"); 1013 getexp_err(ep, tgrp); 1014 goto nextline; 1015 } 1016 if (ep) { 1017 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || 1018 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { 1019 getexp_err(ep, tgrp); 1020 goto nextline; 1021 } 1022 } else { 1023 /* 1024 * See if this directory is already 1025 * in the list. 1026 */ 1027 ep = ex_search(&fsb.f_fsid); 1028 if (ep == (struct exportlist *)NULL) { 1029 ep = get_exp(); 1030 ep->ex_fs = fsb.f_fsid; 1031 ep->ex_fsdir = (char *) 1032 malloc(strlen(fsb.f_mntonname) + 1); 1033 if (ep->ex_fsdir) 1034 strcpy(ep->ex_fsdir, 1035 fsb.f_mntonname); 1036 else 1037 out_of_mem(); 1038 if (debug) 1039 warnx("making new ep fs=0x%x,0x%x", 1040 fsb.f_fsid.val[0], 1041 fsb.f_fsid.val[1]); 1042 } else if (debug) 1043 warnx("found ep fs=0x%x,0x%x", 1044 fsb.f_fsid.val[0], 1045 fsb.f_fsid.val[1]); 1046 } 1047 1048 /* 1049 * Add dirpath to export mount point. 1050 */ 1051 dirp = add_expdir(&dirhead, cp, len); 1052 dirplen = len; 1053 } else { 1054 getexp_err(ep, tgrp); 1055 goto nextline; 1056 } 1057 *endcp = savedc; 1058 } else { 1059 savedc = *endcp; 1060 *endcp = '\0'; 1061 got_nondir = 1; 1062 if (ep == (struct exportlist *)NULL) { 1063 getexp_err(ep, tgrp); 1064 goto nextline; 1065 } 1066 1067 /* 1068 * Get the host or netgroup. 1069 */ 1070 setnetgrent(cp); 1071 netgrp = getnetgrent(&hst, &usr, &dom); 1072 do { 1073 if (has_host) { 1074 grp->gr_next = get_grp(); 1075 grp = grp->gr_next; 1076 } 1077 if (netgrp) { 1078 if (hst == 0) { 1079 syslog(LOG_ERR, 1080 "null hostname in netgroup %s, skipping", cp); 1081 grp->gr_type = GT_IGNORE; 1082 } else if (get_host(hst, grp, tgrp)) { 1083 syslog(LOG_ERR, 1084 "bad host %s in netgroup %s, skipping", hst, cp); 1085 grp->gr_type = GT_IGNORE; 1086 } 1087 } else if (get_host(cp, grp, tgrp)) { 1088 syslog(LOG_ERR, "bad host %s, skipping", cp); 1089 grp->gr_type = GT_IGNORE; 1090 } 1091 has_host = TRUE; 1092 } while (netgrp && getnetgrent(&hst, &usr, &dom)); 1093 endnetgrent(); 1094 *endcp = savedc; 1095 } 1096 cp = endcp; 1097 nextfield(&cp, &endcp); 1098 len = endcp - cp; 1099 } 1100 if (check_options(dirhead)) { 1101 getexp_err(ep, tgrp); 1102 goto nextline; 1103 } 1104 if (!has_host) { 1105 grp->gr_type = GT_DEFAULT; 1106 if (debug) 1107 warnx("adding a default entry"); 1108 1109 /* 1110 * Don't allow a network export coincide with a list of 1111 * host(s) on the same line. 1112 */ 1113 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1114 syslog(LOG_ERR, "network/host conflict"); 1115 getexp_err(ep, tgrp); 1116 goto nextline; 1117 1118 /* 1119 * If an export list was specified on this line, make sure 1120 * that we have at least one valid entry, otherwise skip it. 1121 */ 1122 } else { 1123 grp = tgrp; 1124 while (grp && grp->gr_type == GT_IGNORE) 1125 grp = grp->gr_next; 1126 if (! grp) { 1127 getexp_err(ep, tgrp); 1128 goto nextline; 1129 } 1130 } 1131 1132 /* 1133 * Loop through hosts, pushing the exports into the kernel. 1134 * After loop, tgrp points to the start of the list and 1135 * grp points to the last entry in the list. 1136 */ 1137 grp = tgrp; 1138 do { 1139 if (do_mount(ep, grp, exflags, &anon, dirp, dirplen, 1140 &fsb)) { 1141 getexp_err(ep, tgrp); 1142 goto nextline; 1143 } 1144 } while (grp->gr_next && (grp = grp->gr_next)); 1145 1146 /* 1147 * Success. Update the data structures. 1148 */ 1149 if (has_host) { 1150 hang_dirp(dirhead, tgrp, ep, opt_flags); 1151 grp->gr_next = grphead; 1152 grphead = tgrp; 1153 } else { 1154 hang_dirp(dirhead, (struct grouplist *)NULL, ep, 1155 opt_flags); 1156 free_grp(grp); 1157 } 1158 dirhead = (struct dirlist *)NULL; 1159 if ((ep->ex_flag & EX_LINKED) == 0) { 1160 ep2 = exphead; 1161 epp = &exphead; 1162 1163 /* 1164 * Insert in the list in alphabetical order. 1165 */ 1166 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { 1167 epp = &ep2->ex_next; 1168 ep2 = ep2->ex_next; 1169 } 1170 if (ep2) 1171 ep->ex_next = ep2; 1172 *epp = ep; 1173 ep->ex_flag |= EX_LINKED; 1174 } 1175nextline: 1176 if (dirhead) { 1177 free_dir(dirhead); 1178 dirhead = (struct dirlist *)NULL; 1179 } 1180 } 1181 fclose(exp_file); 1182} 1183 1184/* 1185 * Allocate an export list element 1186 */ 1187struct exportlist * 1188get_exp() 1189{ 1190 struct exportlist *ep; 1191 1192 ep = (struct exportlist *)malloc(sizeof (struct exportlist)); 1193 if (ep == (struct exportlist *)NULL) 1194 out_of_mem(); 1195 memset(ep, 0, sizeof(struct exportlist)); 1196 return (ep); 1197} 1198 1199/* 1200 * Allocate a group list element 1201 */ 1202struct grouplist * 1203get_grp() 1204{ 1205 struct grouplist *gp; 1206 1207 gp = (struct grouplist *)malloc(sizeof (struct grouplist)); 1208 if (gp == (struct grouplist *)NULL) 1209 out_of_mem(); 1210 memset(gp, 0, sizeof(struct grouplist)); 1211 return (gp); 1212} 1213 1214/* 1215 * Clean up upon an error in get_exportlist(). 1216 */ 1217void 1218getexp_err(ep, grp) 1219 struct exportlist *ep; 1220 struct grouplist *grp; 1221{ 1222 struct grouplist *tgrp; 1223 1224 syslog(LOG_ERR, "bad exports list line %s", line); 1225 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1226 free_exp(ep); 1227 while (grp) { 1228 tgrp = grp; 1229 grp = grp->gr_next; 1230 free_grp(tgrp); 1231 } 1232} 1233 1234/* 1235 * Search the export list for a matching fs. 1236 */ 1237struct exportlist * 1238ex_search(fsid) 1239 fsid_t *fsid; 1240{ 1241 struct exportlist *ep; 1242 1243 ep = exphead; 1244 while (ep) { 1245 if (ep->ex_fs.val[0] == fsid->val[0] && 1246 ep->ex_fs.val[1] == fsid->val[1]) 1247 return (ep); 1248 ep = ep->ex_next; 1249 } 1250 return (ep); 1251} 1252 1253/* 1254 * Add a directory path to the list. 1255 */ 1256char * 1257add_expdir(dpp, cp, len) 1258 struct dirlist **dpp; 1259 char *cp; 1260 int len; 1261{ 1262 struct dirlist *dp; 1263 1264 dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); 1265 if (dp == (struct dirlist *)NULL) 1266 out_of_mem(); 1267 dp->dp_left = *dpp; 1268 dp->dp_right = (struct dirlist *)NULL; 1269 dp->dp_flag = 0; 1270 dp->dp_hosts = (struct hostlist *)NULL; 1271 strcpy(dp->dp_dirp, cp); 1272 *dpp = dp; 1273 return (dp->dp_dirp); 1274} 1275 1276/* 1277 * Hang the dir list element off the dirpath binary tree as required 1278 * and update the entry for host. 1279 */ 1280void 1281hang_dirp(dp, grp, ep, flags) 1282 struct dirlist *dp; 1283 struct grouplist *grp; 1284 struct exportlist *ep; 1285 int flags; 1286{ 1287 struct hostlist *hp; 1288 struct dirlist *dp2; 1289 1290 if (flags & OP_ALLDIRS) { 1291 if (ep->ex_defdir) 1292 free((caddr_t)dp); 1293 else 1294 ep->ex_defdir = dp; 1295 if (grp == (struct grouplist *)NULL) { 1296 ep->ex_defdir->dp_flag |= DP_DEFSET; 1297 } else while (grp) { 1298 hp = get_ht(); 1299 hp->ht_grp = grp; 1300 hp->ht_next = ep->ex_defdir->dp_hosts; 1301 ep->ex_defdir->dp_hosts = hp; 1302 grp = grp->gr_next; 1303 } 1304 } else { 1305 1306 /* 1307 * Loop through the directories adding them to the tree. 1308 */ 1309 while (dp) { 1310 dp2 = dp->dp_left; 1311 add_dlist(&ep->ex_dirl, dp, grp, flags); 1312 dp = dp2; 1313 } 1314 } 1315} 1316 1317/* 1318 * Traverse the binary tree either updating a node that is already there 1319 * for the new directory or adding the new node. 1320 */ 1321void 1322add_dlist(dpp, newdp, grp, flags) 1323 struct dirlist **dpp; 1324 struct dirlist *newdp; 1325 struct grouplist *grp; 1326 int flags; 1327{ 1328 struct dirlist *dp; 1329 struct hostlist *hp; 1330 int cmp; 1331 1332 dp = *dpp; 1333 if (dp) { 1334 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 1335 if (cmp > 0) { 1336 add_dlist(&dp->dp_left, newdp, grp, flags); 1337 return; 1338 } else if (cmp < 0) { 1339 add_dlist(&dp->dp_right, newdp, grp, flags); 1340 return; 1341 } else 1342 free((caddr_t)newdp); 1343 } else { 1344 dp = newdp; 1345 dp->dp_left = (struct dirlist *)NULL; 1346 *dpp = dp; 1347 } 1348 if (grp) { 1349 1350 /* 1351 * Hang all of the host(s) off of the directory point. 1352 */ 1353 do { 1354 hp = get_ht(); 1355 hp->ht_grp = grp; 1356 hp->ht_next = dp->dp_hosts; 1357 dp->dp_hosts = hp; 1358 grp = grp->gr_next; 1359 } while (grp); 1360 } else { 1361 dp->dp_flag |= DP_DEFSET; 1362 } 1363} 1364 1365/* 1366 * Search for a dirpath on the export point. 1367 */ 1368struct dirlist * 1369dirp_search(dp, dirp) 1370 struct dirlist *dp; 1371 char *dirp; 1372{ 1373 int cmp; 1374 1375 if (dp) { 1376 cmp = strcmp(dp->dp_dirp, dirp); 1377 if (cmp > 0) 1378 return (dirp_search(dp->dp_left, dirp)); 1379 else if (cmp < 0) 1380 return (dirp_search(dp->dp_right, dirp)); 1381 else 1382 return (dp); 1383 } 1384 return (dp); 1385} 1386 1387/* 1388 * Scan for a host match in a directory tree. 1389 */ 1390int 1391chk_host(dp, saddr, defsetp, hostsetp) 1392 struct dirlist *dp; 1393 struct sockaddr *saddr; 1394 int *defsetp; 1395 int *hostsetp; 1396{ 1397 struct hostlist *hp; 1398 struct grouplist *grp; 1399 struct addrinfo *ai; 1400 1401 if (dp) { 1402 if (dp->dp_flag & DP_DEFSET) 1403 *defsetp = dp->dp_flag; 1404 hp = dp->dp_hosts; 1405 while (hp) { 1406 grp = hp->ht_grp; 1407 switch (grp->gr_type) { 1408 case GT_HOST: 1409 ai = grp->gr_ptr.gt_addrinfo; 1410 for (; ai; ai = ai->ai_next) { 1411 if (!sacmp(ai->ai_addr, saddr, NULL)) { 1412 *hostsetp = 1413 (hp->ht_flag | DP_HOSTSET); 1414 return (1); 1415 } 1416 } 1417 break; 1418 case GT_NET: 1419 if (!sacmp(saddr, (struct sockaddr *) 1420 &grp->gr_ptr.gt_net.nt_net, 1421 (struct sockaddr *) 1422 &grp->gr_ptr.gt_net.nt_mask)) { 1423 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1424 return (1); 1425 } 1426 break; 1427 } 1428 hp = hp->ht_next; 1429 } 1430 } 1431 return (0); 1432} 1433 1434/* 1435 * Scan tree for a host that matches the address. 1436 */ 1437int 1438scan_tree(dp, saddr) 1439 struct dirlist *dp; 1440 struct sockaddr *saddr; 1441{ 1442 int defset, hostset; 1443 1444 if (dp) { 1445 if (scan_tree(dp->dp_left, saddr)) 1446 return (1); 1447 if (chk_host(dp, saddr, &defset, &hostset)) 1448 return (1); 1449 if (scan_tree(dp->dp_right, saddr)) 1450 return (1); 1451 } 1452 return (0); 1453} 1454 1455/* 1456 * Traverse the dirlist tree and free it up. 1457 */ 1458void 1459free_dir(dp) 1460 struct dirlist *dp; 1461{ 1462 1463 if (dp) { 1464 free_dir(dp->dp_left); 1465 free_dir(dp->dp_right); 1466 free_host(dp->dp_hosts); 1467 free((caddr_t)dp); 1468 } 1469} 1470 1471/* 1472 * Parse the option string and update fields. 1473 * Option arguments may either be -<option>=<value> or 1474 * -<option> <value> 1475 */ 1476int 1477do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) 1478 char **cpp, **endcpp; 1479 struct exportlist *ep; 1480 struct grouplist *grp; 1481 int *has_hostp; 1482 int *exflagsp; 1483 struct xucred *cr; 1484{ 1485 char *cpoptarg, *cpoptend; 1486 char *cp, *endcp, *cpopt, savedc, savedc2; 1487 int allflag, usedarg; 1488 1489 savedc2 = '\0'; 1490 cpopt = *cpp; 1491 cpopt++; 1492 cp = *endcpp; 1493 savedc = *cp; 1494 *cp = '\0'; 1495 while (cpopt && *cpopt) { 1496 allflag = 1; 1497 usedarg = -2; 1498 if ((cpoptend = strchr(cpopt, ','))) { 1499 *cpoptend++ = '\0'; 1500 if ((cpoptarg = strchr(cpopt, '='))) 1501 *cpoptarg++ = '\0'; 1502 } else { 1503 if ((cpoptarg = strchr(cpopt, '='))) 1504 *cpoptarg++ = '\0'; 1505 else { 1506 *cp = savedc; 1507 nextfield(&cp, &endcp); 1508 **endcpp = '\0'; 1509 if (endcp > cp && *cp != '-') { 1510 cpoptarg = cp; 1511 savedc2 = *endcp; 1512 *endcp = '\0'; 1513 usedarg = 0; 1514 } 1515 } 1516 } 1517 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 1518 *exflagsp |= MNT_EXRDONLY; 1519 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 1520 !(allflag = strcmp(cpopt, "mapall")) || 1521 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 1522 usedarg++; 1523 parsecred(cpoptarg, cr); 1524 if (allflag == 0) { 1525 *exflagsp |= MNT_EXPORTANON; 1526 opt_flags |= OP_MAPALL; 1527 } else 1528 opt_flags |= OP_MAPROOT; 1529 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 1530 !strcmp(cpopt, "m"))) { 1531 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 1532 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 1533 return (1); 1534 } 1535 usedarg++; 1536 opt_flags |= OP_MASK; 1537 } else if (cpoptarg && (!strcmp(cpopt, "network") || 1538 !strcmp(cpopt, "n"))) { 1539 if (strchr(cpoptarg, '/') != NULL) { 1540 if (debug) 1541 fprintf(stderr, "setting OP_MASKLEN\n"); 1542 opt_flags |= OP_MASKLEN; 1543 } 1544 if (grp->gr_type != GT_NULL) { 1545 syslog(LOG_ERR, "network/host conflict"); 1546 return (1); 1547 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 1548 syslog(LOG_ERR, "bad net: %s", cpoptarg); 1549 return (1); 1550 } 1551 grp->gr_type = GT_NET; 1552 *has_hostp = 1; 1553 usedarg++; 1554 opt_flags |= OP_NET; 1555 } else if (!strcmp(cpopt, "alldirs")) { 1556 opt_flags |= OP_ALLDIRS; 1557 } else if (!strcmp(cpopt, "public")) { 1558 *exflagsp |= MNT_EXPUBLIC; 1559 } else if (!strcmp(cpopt, "webnfs")) { 1560 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 1561 opt_flags |= OP_MAPALL; 1562 } else if (cpoptarg && !strcmp(cpopt, "index")) { 1563 ep->ex_indexfile = strdup(cpoptarg); 1564 } else { 1565 syslog(LOG_ERR, "bad opt %s", cpopt); 1566 return (1); 1567 } 1568 if (usedarg >= 0) { 1569 *endcp = savedc2; 1570 **endcpp = savedc; 1571 if (usedarg > 0) { 1572 *cpp = cp; 1573 *endcpp = endcp; 1574 } 1575 return (0); 1576 } 1577 cpopt = cpoptend; 1578 } 1579 **endcpp = savedc; 1580 return (0); 1581} 1582 1583/* 1584 * Translate a character string to the corresponding list of network 1585 * addresses for a hostname. 1586 */ 1587int 1588get_host(cp, grp, tgrp) 1589 char *cp; 1590 struct grouplist *grp; 1591 struct grouplist *tgrp; 1592{ 1593 struct grouplist *checkgrp; 1594 struct addrinfo *ai, *tai, hints; 1595 int ecode; 1596 char host[NI_MAXHOST]; 1597 1598 if (grp->gr_type != GT_NULL) { 1599 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 1600 return (1); 1601 } 1602 memset(&hints, 0, sizeof hints); 1603 hints.ai_flags = AI_CANONNAME; 1604 hints.ai_protocol = IPPROTO_UDP; 1605 ecode = getaddrinfo(cp, NULL, &hints, &ai); 1606 if (ecode != 0) { 1607 syslog(LOG_ERR,"can't get address info for host %s", cp); 1608 return 1; 1609 } 1610 grp->gr_ptr.gt_addrinfo = ai; 1611 while (ai != NULL) { 1612 if (ai->ai_canonname == NULL) { 1613 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 1614 sizeof host, NULL, 0, ninumeric) != 0) 1615 strlcpy(host, "?", sizeof(host)); 1616 ai->ai_canonname = strdup(host); 1617 ai->ai_flags |= AI_CANONNAME; 1618 } 1619 if (debug) 1620 fprintf(stderr, "got host %s\n", ai->ai_canonname); 1621 /* 1622 * Sanity check: make sure we don't already have an entry 1623 * for this host in the grouplist. 1624 */ 1625 for (checkgrp = tgrp; checkgrp != NULL; 1626 checkgrp = checkgrp->gr_next) { 1627 if (checkgrp->gr_type != GT_HOST) 1628 continue; 1629 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 1630 tai = tai->ai_next) { 1631 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 1632 continue; 1633 if (debug) 1634 fprintf(stderr, 1635 "ignoring duplicate host %s\n", 1636 ai->ai_canonname); 1637 grp->gr_type = GT_IGNORE; 1638 return (0); 1639 } 1640 } 1641 ai = ai->ai_next; 1642 } 1643 grp->gr_type = GT_HOST; 1644 return (0); 1645} 1646 1647/* 1648 * Free up an exports list component 1649 */ 1650void 1651free_exp(ep) 1652 struct exportlist *ep; 1653{ 1654 1655 if (ep->ex_defdir) { 1656 free_host(ep->ex_defdir->dp_hosts); 1657 free((caddr_t)ep->ex_defdir); 1658 } 1659 if (ep->ex_fsdir) 1660 free(ep->ex_fsdir); 1661 if (ep->ex_indexfile) 1662 free(ep->ex_indexfile); 1663 free_dir(ep->ex_dirl); 1664 free((caddr_t)ep); 1665} 1666 1667/* 1668 * Free hosts. 1669 */ 1670void 1671free_host(hp) 1672 struct hostlist *hp; 1673{ 1674 struct hostlist *hp2; 1675 1676 while (hp) { 1677 hp2 = hp; 1678 hp = hp->ht_next; 1679 free((caddr_t)hp2); 1680 } 1681} 1682 1683struct hostlist * 1684get_ht() 1685{ 1686 struct hostlist *hp; 1687 1688 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 1689 if (hp == (struct hostlist *)NULL) 1690 out_of_mem(); 1691 hp->ht_next = (struct hostlist *)NULL; 1692 hp->ht_flag = 0; 1693 return (hp); 1694} 1695 1696/* 1697 * Out of memory, fatal 1698 */ 1699void 1700out_of_mem() 1701{ 1702 1703 syslog(LOG_ERR, "out of memory"); 1704 exit(2); 1705} 1706 1707/* 1708 * Do the mount syscall with the update flag to push the export info into 1709 * the kernel. 1710 */ 1711int 1712do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) 1713 struct exportlist *ep; 1714 struct grouplist *grp; 1715 int exflags; 1716 struct xucred *anoncrp; 1717 char *dirp; 1718 int dirplen; 1719 struct statfs *fsb; 1720{ 1721 struct statfs fsb1; 1722 struct addrinfo *ai; 1723 struct export_args *eap; 1724 char *cp = NULL; 1725 int done; 1726 char savedc = '\0'; 1727 union { 1728 struct ufs_args ua; 1729 struct iso_args ia; 1730 struct msdosfs_args da; 1731 struct ntfs_args na; 1732 } args; 1733 1734 bzero(&args, sizeof args); 1735 /* XXX, we assume that all xx_args look like ufs_args. */ 1736 args.ua.fspec = 0; 1737 eap = &args.ua.export; 1738 1739 eap->ex_flags = exflags; 1740 eap->ex_anon = *anoncrp; 1741 eap->ex_indexfile = ep->ex_indexfile; 1742 if (grp->gr_type == GT_HOST) 1743 ai = grp->gr_ptr.gt_addrinfo; 1744 else 1745 ai = NULL; 1746 done = FALSE; 1747 while (!done) { 1748 switch (grp->gr_type) { 1749 case GT_HOST: 1750 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 1751 goto skip; 1752 eap->ex_addr = ai->ai_addr; 1753 eap->ex_addrlen = ai->ai_addrlen; 1754 eap->ex_masklen = 0; 1755 break; 1756 case GT_NET: 1757 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 1758 have_v6 == 0) 1759 goto skip; 1760 eap->ex_addr = 1761 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 1762 eap->ex_addrlen = args.ua.export.ex_addr->sa_len; 1763 eap->ex_mask = 1764 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 1765 eap->ex_masklen = args.ua.export.ex_mask->sa_len; 1766 break; 1767 case GT_DEFAULT: 1768 eap->ex_addr = NULL; 1769 eap->ex_addrlen = 0; 1770 eap->ex_mask = NULL; 1771 eap->ex_masklen = 0; 1772 break; 1773 case GT_IGNORE: 1774 return(0); 1775 break; 1776 default: 1777 syslog(LOG_ERR, "bad grouptype"); 1778 if (cp) 1779 *cp = savedc; 1780 return (1); 1781 }; 1782 1783 /* 1784 * XXX: 1785 * Maybe I should just use the fsb->f_mntonname path instead 1786 * of looping back up the dirp to the mount point?? 1787 * Also, needs to know how to export all types of local 1788 * exportable file systems and not just "ufs". 1789 */ 1790 while (mount(fsb->f_fstypename, dirp, 1791 fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { 1792 if (cp) 1793 *cp-- = savedc; 1794 else 1795 cp = dirp + dirplen - 1; 1796 if (errno == EPERM) { 1797 if (debug) 1798 warnx("can't change attributes for %s", 1799 dirp); 1800 syslog(LOG_ERR, 1801 "can't change attributes for %s", dirp); 1802 return (1); 1803 } 1804 if (opt_flags & OP_ALLDIRS) { 1805 syslog(LOG_ERR, "could not remount %s: %m", 1806 dirp); 1807 return (1); 1808 } 1809 /* back up over the last component */ 1810 while (*cp == '/' && cp > dirp) 1811 cp--; 1812 while (*(cp - 1) != '/' && cp > dirp) 1813 cp--; 1814 if (cp == dirp) { 1815 if (debug) 1816 warnx("mnt unsucc"); 1817 syslog(LOG_ERR, "can't export %s", dirp); 1818 return (1); 1819 } 1820 savedc = *cp; 1821 *cp = '\0'; 1822 /* Check that we're still on the same filesystem. */ 1823 if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid, 1824 &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) { 1825 *cp = savedc; 1826 syslog(LOG_ERR, "can't export %s", dirp); 1827 return (1); 1828 } 1829 } 1830skip: 1831 if (ai != NULL) 1832 ai = ai->ai_next; 1833 if (ai == NULL) 1834 done = TRUE; 1835 } 1836 if (cp) 1837 *cp = savedc; 1838 return (0); 1839} 1840 1841/* 1842 * Translate a net address. 1843 * 1844 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 1845 */ 1846int 1847get_net(cp, net, maskflg) 1848 char *cp; 1849 struct netmsk *net; 1850 int maskflg; 1851{ 1852 struct netent *np = NULL; 1853 char *name, *p, *prefp; 1854 struct sockaddr_in sin; 1855 struct sockaddr *sa = NULL; 1856 struct addrinfo hints, *ai = NULL; 1857 char netname[NI_MAXHOST]; 1858 long preflen; 1859 1860 p = prefp = NULL; 1861 if ((opt_flags & OP_MASKLEN) && !maskflg) { 1862 p = strchr(cp, '/'); 1863 *p = '\0'; 1864 prefp = p + 1; 1865 } 1866 1867 /* 1868 * Check for a numeric address first. We wish to avoid 1869 * possible DNS lookups in getnetbyname(). 1870 */ 1871 if (isxdigit(*cp) || *cp == ':') { 1872 memset(&hints, 0, sizeof hints); 1873 /* Ensure the mask and the network have the same family. */ 1874 if (maskflg && (opt_flags & OP_NET)) 1875 hints.ai_family = net->nt_net.ss_family; 1876 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 1877 hints.ai_family = net->nt_mask.ss_family; 1878 else 1879 hints.ai_family = AF_UNSPEC; 1880 hints.ai_flags = AI_NUMERICHOST; 1881 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 1882 sa = ai->ai_addr; 1883 if (sa != NULL && ai->ai_family == AF_INET) { 1884 /* 1885 * The address in `cp' is really a network address, so 1886 * use inet_network() to re-interpret this correctly. 1887 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 1888 */ 1889 bzero(&sin, sizeof sin); 1890 sin.sin_family = AF_INET; 1891 sin.sin_len = sizeof sin; 1892 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 1893 if (debug) 1894 fprintf(stderr, "get_net: v4 addr %s\n", 1895 inet_ntoa(sin.sin_addr)); 1896 sa = (struct sockaddr *)&sin; 1897 } 1898 } 1899 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 1900 bzero(&sin, sizeof sin); 1901 sin.sin_family = AF_INET; 1902 sin.sin_len = sizeof sin; 1903 sin.sin_addr = inet_makeaddr(np->n_net, 0); 1904 sa = (struct sockaddr *)&sin; 1905 } 1906 if (sa == NULL) 1907 goto fail; 1908 1909 if (maskflg) { 1910 /* The specified sockaddr is a mask. */ 1911 if (checkmask(sa) != 0) 1912 goto fail; 1913 bcopy(sa, &net->nt_mask, sa->sa_len); 1914 opt_flags |= OP_HAVEMASK; 1915 } else { 1916 /* The specified sockaddr is a network address. */ 1917 bcopy(sa, &net->nt_net, sa->sa_len); 1918 1919 /* Get a network name for the export list. */ 1920 if (np) { 1921 name = np->n_name; 1922 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 1923 NULL, 0, ninumeric) == 0) { 1924 name = netname; 1925 } else { 1926 goto fail; 1927 } 1928 if ((net->nt_name = strdup(name)) == NULL) 1929 out_of_mem(); 1930 1931 /* 1932 * Extract a mask from either a "/<masklen>" suffix, or 1933 * from the class of an IPv4 address. 1934 */ 1935 if (opt_flags & OP_MASKLEN) { 1936 preflen = strtol(prefp, NULL, 10); 1937 if (preflen < 0L || preflen == LONG_MAX) 1938 goto fail; 1939 bcopy(sa, &net->nt_mask, sa->sa_len); 1940 if (makemask(&net->nt_mask, (int)preflen) != 0) 1941 goto fail; 1942 opt_flags |= OP_HAVEMASK; 1943 *p = '/'; 1944 } else if (sa->sa_family == AF_INET && 1945 (opt_flags & OP_MASK) == 0) { 1946 in_addr_t addr; 1947 1948 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 1949 if (IN_CLASSA(addr)) 1950 preflen = 8; 1951 else if (IN_CLASSB(addr)) 1952 preflen = 16; 1953 else if (IN_CLASSC(addr)) 1954 preflen = 24; 1955 else if (IN_CLASSD(addr)) 1956 preflen = 28; 1957 else 1958 preflen = 32; /* XXX */ 1959 1960 bcopy(sa, &net->nt_mask, sa->sa_len); 1961 makemask(&net->nt_mask, (int)preflen); 1962 opt_flags |= OP_HAVEMASK; 1963 } 1964 } 1965 1966 if (ai) 1967 freeaddrinfo(ai); 1968 return 0; 1969 1970fail: 1971 if (ai) 1972 freeaddrinfo(ai); 1973 return 1; 1974} 1975 1976/* 1977 * Parse out the next white space separated field 1978 */ 1979void 1980nextfield(cp, endcp) 1981 char **cp; 1982 char **endcp; 1983{ 1984 char *p; 1985 1986 p = *cp; 1987 while (*p == ' ' || *p == '\t') 1988 p++; 1989 if (*p == '\n' || *p == '\0') 1990 *cp = *endcp = p; 1991 else { 1992 *cp = p++; 1993 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 1994 p++; 1995 *endcp = p; 1996 } 1997} 1998 1999/* 2000 * Get an exports file line. Skip over blank lines and handle line 2001 * continuations. 2002 */ 2003int 2004get_line() 2005{ 2006 char *p, *cp; 2007 int len; 2008 int totlen, cont_line; 2009 2010 /* 2011 * Loop around ignoring blank lines and getting all continuation lines. 2012 */ 2013 p = line; 2014 totlen = 0; 2015 do { 2016 if (fgets(p, LINESIZ - totlen, exp_file) == NULL) 2017 return (0); 2018 len = strlen(p); 2019 cp = p + len - 1; 2020 cont_line = 0; 2021 while (cp >= p && 2022 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 2023 if (*cp == '\\') 2024 cont_line = 1; 2025 cp--; 2026 len--; 2027 } 2028 if (cont_line) { 2029 *++cp = ' '; 2030 len++; 2031 } 2032 *++cp = '\0'; 2033 if (len > 0) { 2034 totlen += len; 2035 if (totlen >= LINESIZ) { 2036 syslog(LOG_ERR, "exports line too long"); 2037 exit(2); 2038 } 2039 p = cp; 2040 } 2041 } while (totlen == 0 || cont_line); 2042 return (1); 2043} 2044 2045/* 2046 * Parse a description of a credential. 2047 */ 2048void 2049parsecred(namelist, cr) 2050 char *namelist; 2051 struct xucred *cr; 2052{ 2053 char *name; 2054 int cnt; 2055 char *names; 2056 struct passwd *pw; 2057 struct group *gr; 2058 int ngroups, groups[NGROUPS + 1]; 2059 2060 /* 2061 * Set up the unprivileged user. 2062 */ 2063 cr->cr_uid = -2; 2064 cr->cr_groups[0] = -2; 2065 cr->cr_ngroups = 1; 2066 /* 2067 * Get the user's password table entry. 2068 */ 2069 names = strsep(&namelist, " \t\n"); 2070 name = strsep(&names, ":"); 2071 if (isdigit(*name) || *name == '-') 2072 pw = getpwuid(atoi(name)); 2073 else 2074 pw = getpwnam(name); 2075 /* 2076 * Credentials specified as those of a user. 2077 */ 2078 if (names == NULL) { 2079 if (pw == NULL) { 2080 syslog(LOG_ERR, "unknown user: %s", name); 2081 return; 2082 } 2083 cr->cr_uid = pw->pw_uid; 2084 ngroups = NGROUPS + 1; 2085 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 2086 syslog(LOG_ERR, "too many groups"); 2087 /* 2088 * Convert from int's to gid_t's and compress out duplicate 2089 */ 2090 cr->cr_ngroups = ngroups - 1; 2091 cr->cr_groups[0] = groups[0]; 2092 for (cnt = 2; cnt < ngroups; cnt++) 2093 cr->cr_groups[cnt - 1] = groups[cnt]; 2094 return; 2095 } 2096 /* 2097 * Explicit credential specified as a colon separated list: 2098 * uid:gid:gid:... 2099 */ 2100 if (pw != NULL) 2101 cr->cr_uid = pw->pw_uid; 2102 else if (isdigit(*name) || *name == '-') 2103 cr->cr_uid = atoi(name); 2104 else { 2105 syslog(LOG_ERR, "unknown user: %s", name); 2106 return; 2107 } 2108 cr->cr_ngroups = 0; 2109 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 2110 name = strsep(&names, ":"); 2111 if (isdigit(*name) || *name == '-') { 2112 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 2113 } else { 2114 if ((gr = getgrnam(name)) == NULL) { 2115 syslog(LOG_ERR, "unknown group: %s", name); 2116 continue; 2117 } 2118 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 2119 } 2120 } 2121 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 2122 syslog(LOG_ERR, "too many groups"); 2123} 2124 2125#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 2126/* 2127 * Routines that maintain the remote mounttab 2128 */ 2129void 2130get_mountlist() 2131{ 2132 struct mountlist *mlp, **mlpp; 2133 char *host, *dirp, *cp; 2134 char str[STRSIZ]; 2135 FILE *mlfile; 2136 2137 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 2138 if (errno == ENOENT) 2139 return; 2140 else { 2141 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 2142 return; 2143 } 2144 } 2145 mlpp = &mlhead; 2146 while (fgets(str, STRSIZ, mlfile) != NULL) { 2147 cp = str; 2148 host = strsep(&cp, " \t\n"); 2149 dirp = strsep(&cp, " \t\n"); 2150 if (host == NULL || dirp == NULL) 2151 continue; 2152 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2153 if (mlp == (struct mountlist *)NULL) 2154 out_of_mem(); 2155 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN); 2156 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2157 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2158 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2159 mlp->ml_next = (struct mountlist *)NULL; 2160 *mlpp = mlp; 2161 mlpp = &mlp->ml_next; 2162 } 2163 fclose(mlfile); 2164} 2165 2166void 2167del_mlist(char *hostp, char *dirp) 2168{ 2169 struct mountlist *mlp, **mlpp; 2170 struct mountlist *mlp2; 2171 FILE *mlfile; 2172 int fnd = 0; 2173 2174 mlpp = &mlhead; 2175 mlp = mlhead; 2176 while (mlp) { 2177 if (!strcmp(mlp->ml_host, hostp) && 2178 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 2179 fnd = 1; 2180 mlp2 = mlp; 2181 *mlpp = mlp = mlp->ml_next; 2182 free((caddr_t)mlp2); 2183 } else { 2184 mlpp = &mlp->ml_next; 2185 mlp = mlp->ml_next; 2186 } 2187 } 2188 if (fnd) { 2189 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 2190 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 2191 return; 2192 } 2193 mlp = mlhead; 2194 while (mlp) { 2195 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2196 mlp = mlp->ml_next; 2197 } 2198 fclose(mlfile); 2199 } 2200} 2201 2202void 2203add_mlist(hostp, dirp) 2204 char *hostp, *dirp; 2205{ 2206 struct mountlist *mlp, **mlpp; 2207 FILE *mlfile; 2208 2209 mlpp = &mlhead; 2210 mlp = mlhead; 2211 while (mlp) { 2212 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 2213 return; 2214 mlpp = &mlp->ml_next; 2215 mlp = mlp->ml_next; 2216 } 2217 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2218 if (mlp == (struct mountlist *)NULL) 2219 out_of_mem(); 2220 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN); 2221 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2222 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2223 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2224 mlp->ml_next = (struct mountlist *)NULL; 2225 *mlpp = mlp; 2226 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 2227 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 2228 return; 2229 } 2230 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2231 fclose(mlfile); 2232} 2233 2234/* 2235 * Free up a group list. 2236 */ 2237void 2238free_grp(grp) 2239 struct grouplist *grp; 2240{ 2241 if (grp->gr_type == GT_HOST) { 2242 if (grp->gr_ptr.gt_addrinfo != NULL) 2243 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 2244 } else if (grp->gr_type == GT_NET) { 2245 if (grp->gr_ptr.gt_net.nt_name) 2246 free(grp->gr_ptr.gt_net.nt_name); 2247 } 2248 free((caddr_t)grp); 2249} 2250 2251#ifdef DEBUG 2252void 2253SYSLOG(int pri, const char *fmt, ...) 2254{ 2255 va_list ap; 2256 2257 va_start(ap, fmt); 2258 vfprintf(stderr, fmt, ap); 2259 va_end(ap); 2260} 2261#endif /* DEBUG */ 2262 2263/* 2264 * Check options for consistency. 2265 */ 2266int 2267check_options(dp) 2268 struct dirlist *dp; 2269{ 2270 2271 if (dp == (struct dirlist *)NULL) 2272 return (1); 2273 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 2274 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 2275 return (1); 2276 } 2277 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 2278 syslog(LOG_ERR, "-mask requires -network"); 2279 return (1); 2280 } 2281 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 2282 syslog(LOG_ERR, "-network requires mask specification"); 2283 return (1); 2284 } 2285 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 2286 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 2287 return (1); 2288 } 2289 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 2290 syslog(LOG_ERR, "-alldirs has multiple directories"); 2291 return (1); 2292 } 2293 return (0); 2294} 2295 2296/* 2297 * Check an absolute directory path for any symbolic links. Return true 2298 */ 2299int 2300check_dirpath(dirp) 2301 char *dirp; 2302{ 2303 char *cp; 2304 int ret = 1; 2305 struct stat sb; 2306 2307 cp = dirp + 1; 2308 while (*cp && ret) { 2309 if (*cp == '/') { 2310 *cp = '\0'; 2311 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2312 ret = 0; 2313 *cp = '/'; 2314 } 2315 cp++; 2316 } 2317 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2318 ret = 0; 2319 return (ret); 2320} 2321 2322/* 2323 * Make a netmask according to the specified prefix length. The ss_family 2324 * and other non-address fields must be initialised before calling this. 2325 */ 2326int 2327makemask(struct sockaddr_storage *ssp, int bitlen) 2328{ 2329 u_char *p; 2330 int bits, i, len; 2331 2332 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 2333 return (-1); 2334 if (bitlen > len * NBBY) 2335 return (-1); 2336 2337 for (i = 0; i < len; i++) { 2338 bits = (bitlen > NBBY) ? NBBY : bitlen; 2339 *p++ = (1 << bits) - 1; 2340 bitlen -= bits; 2341 } 2342 return 0; 2343} 2344 2345/* 2346 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 2347 * is acceptable (i.e. of the form 1...10....0). 2348 */ 2349int 2350checkmask(struct sockaddr *sa) 2351{ 2352 u_char *mask; 2353 int i, len; 2354 2355 if ((mask = sa_rawaddr(sa, &len)) == NULL) 2356 return (-1); 2357 2358 for (i = 0; i < len; i++) 2359 if (mask[i] != 0xff) 2360 break; 2361 if (i < len) { 2362 if (~mask[i] & (u_char)(~mask[i] + 1)) 2363 return (-1); 2364 i++; 2365 } 2366 for (; i < len; i++) 2367 if (mask[i] != 0) 2368 return (-1); 2369 return (0); 2370} 2371 2372/* 2373 * Compare two sockaddrs according to a specified mask. Return zero if 2374 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 2375 * If samask is NULL, perform a full comparision. 2376 */ 2377int 2378sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 2379{ 2380 unsigned char *p1, *p2, *mask; 2381 int len, i; 2382 2383 if (sa1->sa_family != sa2->sa_family || 2384 (p1 = sa_rawaddr(sa1, &len)) == NULL || 2385 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 2386 return (1); 2387 2388 switch (sa1->sa_family) { 2389 case AF_INET6: 2390 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 2391 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 2392 return (1); 2393 break; 2394 } 2395 2396 /* Simple binary comparison if no mask specified. */ 2397 if (samask == NULL) 2398 return (memcmp(p1, p2, len)); 2399 2400 /* Set up the mask, and do a mask-based comparison. */ 2401 if (sa1->sa_family != samask->sa_family || 2402 (mask = sa_rawaddr(samask, NULL)) == NULL) 2403 return (1); 2404 2405 for (i = 0; i < len; i++) 2406 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 2407 return (1); 2408 return (0); 2409} 2410 2411/* 2412 * Return a pointer to the part of the sockaddr that contains the 2413 * raw address, and set *nbytes to its length in bytes. Returns 2414 * NULL if the address family is unknown. 2415 */ 2416void * 2417sa_rawaddr(struct sockaddr *sa, int *nbytes) { 2418 void *p; 2419 int len; 2420 2421 switch (sa->sa_family) { 2422 case AF_INET: 2423 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 2424 p = &((struct sockaddr_in *)sa)->sin_addr; 2425 break; 2426 case AF_INET6: 2427 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 2428 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 2429 break; 2430 default: 2431 p = NULL; 2432 len = 0; 2433 } 2434 2435 if (nbytes != NULL) 2436 *nbytes = len; 2437 return (p); 2438} 2439 2440void 2441huphandler(int sig) 2442{ 2443 got_sighup = 1; 2444} 2445 2446void terminate(sig) 2447int sig; 2448{ 2449 close(mountdlockfd); 2450 unlink(MOUNTDLOCK); 2451 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); 2452 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); 2453 exit (0); 2454} 2455