mountd.c revision 331722
1/* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Herb Hasler and Rick Macklem at The University of Guelph. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#ifndef lint 34static const char copyright[] = 35"@(#) Copyright (c) 1989, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37#endif /*not lint*/ 38 39#if 0 40#ifndef lint 41static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; 42#endif /*not lint*/ 43#endif 44 45#include <sys/cdefs.h> 46__FBSDID("$FreeBSD: stable/11/usr.sbin/mountd/mountd.c 331722 2018-03-29 02:50:57Z eadler $"); 47 48#include <sys/param.h> 49#include <sys/fcntl.h> 50#include <sys/linker.h> 51#include <sys/module.h> 52#include <sys/mount.h> 53#include <sys/queue.h> 54#include <sys/stat.h> 55#include <sys/sysctl.h> 56#include <sys/syslog.h> 57 58#include <rpc/rpc.h> 59#include <rpc/rpc_com.h> 60#include <rpc/pmap_clnt.h> 61#include <rpc/pmap_prot.h> 62#include <rpcsvc/mount.h> 63#include <nfs/nfsproto.h> 64#include <nfs/nfssvc.h> 65#include <nfsserver/nfs.h> 66 67#include <fs/nfs/nfsport.h> 68 69#include <arpa/inet.h> 70 71#include <ctype.h> 72#include <err.h> 73#include <errno.h> 74#include <grp.h> 75#include <libutil.h> 76#include <limits.h> 77#include <netdb.h> 78#include <pwd.h> 79#include <signal.h> 80#include <stdio.h> 81#include <stdlib.h> 82#include <string.h> 83#include <unistd.h> 84#include "pathnames.h" 85#include "mntopts.h" 86 87#ifdef DEBUG 88#include <stdarg.h> 89#endif 90 91/* 92 * Structures for keeping the mount list and export list 93 */ 94struct mountlist { 95 char ml_host[MNTNAMLEN+1]; 96 char ml_dirp[MNTPATHLEN+1]; 97 98 SLIST_ENTRY(mountlist) next; 99}; 100 101struct dirlist { 102 struct dirlist *dp_left; 103 struct dirlist *dp_right; 104 int dp_flag; 105 struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 106 char *dp_dirp; 107}; 108/* dp_flag bits */ 109#define DP_DEFSET 0x1 110#define DP_HOSTSET 0x2 111 112struct exportlist { 113 struct dirlist *ex_dirl; 114 struct dirlist *ex_defdir; 115 int ex_flag; 116 fsid_t ex_fs; 117 char *ex_fsdir; 118 char *ex_indexfile; 119 int ex_numsecflavors; 120 int ex_secflavors[MAXSECFLAVORS]; 121 int ex_defnumsecflavors; 122 int ex_defsecflavors[MAXSECFLAVORS]; 123 124 SLIST_ENTRY(exportlist) entries; 125}; 126/* ex_flag bits */ 127#define EX_LINKED 0x1 128 129struct netmsk { 130 struct sockaddr_storage nt_net; 131 struct sockaddr_storage nt_mask; 132 char *nt_name; 133}; 134 135union grouptypes { 136 struct addrinfo *gt_addrinfo; 137 struct netmsk gt_net; 138}; 139 140struct grouplist { 141 int gr_type; 142 union grouptypes gr_ptr; 143 struct grouplist *gr_next; 144 int gr_numsecflavors; 145 int gr_secflavors[MAXSECFLAVORS]; 146}; 147/* Group types */ 148#define GT_NULL 0x0 149#define GT_HOST 0x1 150#define GT_NET 0x2 151#define GT_DEFAULT 0x3 152#define GT_IGNORE 0x5 153 154struct hostlist { 155 int ht_flag; /* Uses DP_xx bits */ 156 struct grouplist *ht_grp; 157 struct hostlist *ht_next; 158}; 159 160struct fhreturn { 161 int fhr_flag; 162 int fhr_vers; 163 nfsfh_t fhr_fh; 164 int fhr_numsecflavors; 165 int *fhr_secflavors; 166}; 167 168#define GETPORT_MAXTRY 20 /* Max tries to get a port # */ 169 170/* Global defs */ 171static char *add_expdir(struct dirlist **, char *, int); 172static void add_dlist(struct dirlist **, struct dirlist *, 173 struct grouplist *, int, struct exportlist *); 174static void add_mlist(char *, char *); 175static int check_dirpath(char *); 176static int check_options(struct dirlist *); 177static int checkmask(struct sockaddr *sa); 178static int chk_host(struct dirlist *, struct sockaddr *, int *, int *, 179 int *, int **); 180static char *strsep_quote(char **stringp, const char *delim); 181static int create_service(struct netconfig *nconf); 182static void complete_service(struct netconfig *nconf, char *port_str); 183static void clearout_service(void); 184static void del_mlist(char *hostp, char *dirp); 185static struct dirlist *dirp_search(struct dirlist *, char *); 186static int do_mount(struct exportlist *, struct grouplist *, int, 187 struct xucred *, char *, int, struct statfs *); 188static int do_opt(char **, char **, struct exportlist *, 189 struct grouplist *, int *, int *, struct xucred *); 190static struct exportlist *ex_search(fsid_t *); 191static struct exportlist *get_exp(void); 192static void free_dir(struct dirlist *); 193static void free_exp(struct exportlist *); 194static void free_grp(struct grouplist *); 195static void free_host(struct hostlist *); 196static void get_exportlist(void); 197static int get_host(char *, struct grouplist *, struct grouplist *); 198static struct hostlist *get_ht(void); 199static int get_line(void); 200static void get_mountlist(void); 201static int get_net(char *, struct netmsk *, int); 202static void getexp_err(struct exportlist *, struct grouplist *, const char *); 203static struct grouplist *get_grp(void); 204static void hang_dirp(struct dirlist *, struct grouplist *, 205 struct exportlist *, int); 206static void huphandler(int sig); 207static int makemask(struct sockaddr_storage *ssp, int bitlen); 208static void mntsrv(struct svc_req *, SVCXPRT *); 209static void nextfield(char **, char **); 210static void out_of_mem(void); 211static void parsecred(char *, struct xucred *); 212static int parsesec(char *, struct exportlist *); 213static int put_exlist(struct dirlist *, XDR *, struct dirlist *, 214 int *, int); 215static void *sa_rawaddr(struct sockaddr *sa, int *nbytes); 216static int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 217 struct sockaddr *samask); 218static int scan_tree(struct dirlist *, struct sockaddr *); 219static void usage(void); 220static int xdr_dir(XDR *, char *); 221static int xdr_explist(XDR *, caddr_t); 222static int xdr_explist_brief(XDR *, caddr_t); 223static int xdr_explist_common(XDR *, caddr_t, int); 224static int xdr_fhs(XDR *, caddr_t); 225static int xdr_mlist(XDR *, caddr_t); 226static void terminate(int); 227 228static SLIST_HEAD(, exportlist) exphead = SLIST_HEAD_INITIALIZER(exphead); 229static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(mlhead); 230static struct grouplist *grphead; 231static char *exnames_default[2] = { _PATH_EXPORTS, NULL }; 232static char **exnames; 233static char **hosts = NULL; 234static struct xucred def_anon = { 235 XUCRED_VERSION, 236 (uid_t)-2, 237 1, 238 { (gid_t)-2 }, 239 NULL 240}; 241static int force_v2 = 0; 242static int resvport_only = 1; 243static int nhosts = 0; 244static int dir_only = 1; 245static int dolog = 0; 246static int got_sighup = 0; 247static int xcreated = 0; 248 249static char *svcport_str = NULL; 250static int mallocd_svcport = 0; 251static int *sock_fd; 252static int sock_fdcnt; 253static int sock_fdpos; 254static int suspend_nfsd = 0; 255 256static int opt_flags; 257static int have_v6 = 1; 258 259static int v4root_phase = 0; 260static char v4root_dirpath[PATH_MAX + 1]; 261static int has_publicfh = 0; 262 263static struct pidfh *pfh = NULL; 264/* Bits for opt_flags above */ 265#define OP_MAPROOT 0x01 266#define OP_MAPALL 0x02 267/* 0x4 free */ 268#define OP_MASK 0x08 269#define OP_NET 0x10 270#define OP_ALLDIRS 0x40 271#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 272#define OP_QUIET 0x100 273#define OP_MASKLEN 0x200 274#define OP_SEC 0x400 275 276#ifdef DEBUG 277static int debug = 1; 278static void SYSLOG(int, const char *, ...) __printflike(2, 3); 279#define syslog SYSLOG 280#else 281static int debug = 0; 282#endif 283 284/* 285 * Similar to strsep(), but it allows for quoted strings 286 * and escaped characters. 287 * 288 * It returns the string (or NULL, if *stringp is NULL), 289 * which is a de-quoted version of the string if necessary. 290 * 291 * It modifies *stringp in place. 292 */ 293static char * 294strsep_quote(char **stringp, const char *delim) 295{ 296 char *srcptr, *dstptr, *retval; 297 char quot = 0; 298 299 if (stringp == NULL || *stringp == NULL) 300 return (NULL); 301 302 srcptr = dstptr = retval = *stringp; 303 304 while (*srcptr) { 305 /* 306 * We're looking for several edge cases here. 307 * First: if we're in quote state (quot != 0), 308 * then we ignore the delim characters, but otherwise 309 * process as normal, unless it is the quote character. 310 * Second: if the current character is a backslash, 311 * we take the next character as-is, without checking 312 * for delim, quote, or backslash. Exception: if the 313 * next character is a NUL, that's the end of the string. 314 * Third: if the character is a quote character, we toggle 315 * quote state. 316 * Otherwise: check the current character for NUL, or 317 * being in delim, and end the string if either is true. 318 */ 319 if (*srcptr == '\\') { 320 srcptr++; 321 /* 322 * The edge case here is if the next character 323 * is NUL, we want to stop processing. But if 324 * it's not NUL, then we simply want to copy it. 325 */ 326 if (*srcptr) { 327 *dstptr++ = *srcptr++; 328 } 329 continue; 330 } 331 if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) { 332 quot = *srcptr++; 333 continue; 334 } 335 if (quot && *srcptr == quot) { 336 /* End of the quoted part */ 337 quot = 0; 338 srcptr++; 339 continue; 340 } 341 if (!quot && strchr(delim, *srcptr)) 342 break; 343 *dstptr++ = *srcptr++; 344 } 345 346 *dstptr = 0; /* Terminate the string */ 347 *stringp = (*srcptr == '\0') ? NULL : srcptr + 1; 348 return (retval); 349} 350 351/* 352 * Mountd server for NFS mount protocol as described in: 353 * NFS: Network File System Protocol Specification, RFC1094, Appendix A 354 * The optional arguments are the exports file name 355 * default: _PATH_EXPORTS 356 * and "-n" to allow nonroot mount. 357 */ 358int 359main(int argc, char **argv) 360{ 361 fd_set readfds; 362 struct netconfig *nconf; 363 char *endptr, **hosts_bak; 364 void *nc_handle; 365 pid_t otherpid; 366 in_port_t svcport; 367 int c, k, s; 368 int maxrec = RPC_MAXDATASIZE; 369 int attempt_cnt, port_len, port_pos, ret; 370 char **port_list; 371 372 /* Check that another mountd isn't already running. */ 373 pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); 374 if (pfh == NULL) { 375 if (errno == EEXIST) 376 errx(1, "mountd already running, pid: %d.", otherpid); 377 warn("cannot open or create pidfile"); 378 } 379 380 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 381 if (s < 0) 382 have_v6 = 0; 383 else 384 close(s); 385 386 while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1) 387 switch (c) { 388 case '2': 389 force_v2 = 1; 390 break; 391 case 'e': 392 /* now a no-op, since this is the default */ 393 break; 394 case 'n': 395 resvport_only = 0; 396 break; 397 case 'r': 398 dir_only = 0; 399 break; 400 case 'd': 401 debug = debug ? 0 : 1; 402 break; 403 case 'l': 404 dolog = 1; 405 break; 406 case 'p': 407 endptr = NULL; 408 svcport = (in_port_t)strtoul(optarg, &endptr, 10); 409 if (endptr == NULL || *endptr != '\0' || 410 svcport == 0 || svcport >= IPPORT_MAX) 411 usage(); 412 svcport_str = strdup(optarg); 413 break; 414 case 'h': 415 ++nhosts; 416 hosts_bak = hosts; 417 hosts_bak = realloc(hosts, nhosts * sizeof(char *)); 418 if (hosts_bak == NULL) { 419 if (hosts != NULL) { 420 for (k = 0; k < nhosts; k++) 421 free(hosts[k]); 422 free(hosts); 423 out_of_mem(); 424 } 425 } 426 hosts = hosts_bak; 427 hosts[nhosts - 1] = strdup(optarg); 428 if (hosts[nhosts - 1] == NULL) { 429 for (k = 0; k < (nhosts - 1); k++) 430 free(hosts[k]); 431 free(hosts); 432 out_of_mem(); 433 } 434 break; 435 case 'S': 436 suspend_nfsd = 1; 437 break; 438 default: 439 usage(); 440 } 441 442 if (modfind("nfsd") < 0) { 443 /* Not present in kernel, try loading it */ 444 if (kldload("nfsd") < 0 || modfind("nfsd") < 0) 445 errx(1, "NFS server is not available"); 446 } 447 448 argc -= optind; 449 argv += optind; 450 grphead = (struct grouplist *)NULL; 451 if (argc > 0) 452 exnames = argv; 453 else 454 exnames = exnames_default; 455 openlog("mountd", LOG_PID, LOG_DAEMON); 456 if (debug) 457 warnx("getting export list"); 458 get_exportlist(); 459 if (debug) 460 warnx("getting mount list"); 461 get_mountlist(); 462 if (debug) 463 warnx("here we go"); 464 if (debug == 0) { 465 daemon(0, 0); 466 signal(SIGINT, SIG_IGN); 467 signal(SIGQUIT, SIG_IGN); 468 } 469 signal(SIGHUP, huphandler); 470 signal(SIGTERM, terminate); 471 signal(SIGPIPE, SIG_IGN); 472 473 pidfile_write(pfh); 474 475 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 476 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 477 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 478 479 if (!resvport_only) { 480 if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL, 481 &resvport_only, sizeof(resvport_only)) != 0 && 482 errno != ENOENT) { 483 syslog(LOG_ERR, "sysctl: %m"); 484 exit(1); 485 } 486 } 487 488 /* 489 * If no hosts were specified, add a wildcard entry to bind to 490 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the 491 * list. 492 */ 493 if (nhosts == 0) { 494 hosts = malloc(sizeof(char *)); 495 if (hosts == NULL) 496 out_of_mem(); 497 hosts[0] = "*"; 498 nhosts = 1; 499 } else { 500 hosts_bak = hosts; 501 if (have_v6) { 502 hosts_bak = realloc(hosts, (nhosts + 2) * 503 sizeof(char *)); 504 if (hosts_bak == NULL) { 505 for (k = 0; k < nhosts; k++) 506 free(hosts[k]); 507 free(hosts); 508 out_of_mem(); 509 } else 510 hosts = hosts_bak; 511 nhosts += 2; 512 hosts[nhosts - 2] = "::1"; 513 } else { 514 hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); 515 if (hosts_bak == NULL) { 516 for (k = 0; k < nhosts; k++) 517 free(hosts[k]); 518 free(hosts); 519 out_of_mem(); 520 } else { 521 nhosts += 1; 522 hosts = hosts_bak; 523 } 524 } 525 526 hosts[nhosts - 1] = "127.0.0.1"; 527 } 528 529 attempt_cnt = 1; 530 sock_fdcnt = 0; 531 sock_fd = NULL; 532 port_list = NULL; 533 port_len = 0; 534 nc_handle = setnetconfig(); 535 while ((nconf = getnetconfig(nc_handle))) { 536 if (nconf->nc_flag & NC_VISIBLE) { 537 if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 538 "inet6") == 0) { 539 /* DO NOTHING */ 540 } else { 541 ret = create_service(nconf); 542 if (ret == 1) 543 /* Ignore this call */ 544 continue; 545 if (ret < 0) { 546 /* 547 * Failed to bind port, so close off 548 * all sockets created and try again 549 * if the port# was dynamically 550 * assigned via bind(2). 551 */ 552 clearout_service(); 553 if (mallocd_svcport != 0 && 554 attempt_cnt < GETPORT_MAXTRY) { 555 free(svcport_str); 556 svcport_str = NULL; 557 mallocd_svcport = 0; 558 } else { 559 errno = EADDRINUSE; 560 syslog(LOG_ERR, 561 "bindresvport_sa: %m"); 562 exit(1); 563 } 564 565 /* Start over at the first service. */ 566 free(sock_fd); 567 sock_fdcnt = 0; 568 sock_fd = NULL; 569 nc_handle = setnetconfig(); 570 attempt_cnt++; 571 } else if (mallocd_svcport != 0 && 572 attempt_cnt == GETPORT_MAXTRY) { 573 /* 574 * For the last attempt, allow 575 * different port #s for each nconf 576 * by saving the svcport_str and 577 * setting it back to NULL. 578 */ 579 port_list = realloc(port_list, 580 (port_len + 1) * sizeof(char *)); 581 if (port_list == NULL) 582 out_of_mem(); 583 port_list[port_len++] = svcport_str; 584 svcport_str = NULL; 585 mallocd_svcport = 0; 586 } 587 } 588 } 589 } 590 591 /* 592 * Successfully bound the ports, so call complete_service() to 593 * do the rest of the setup on the service(s). 594 */ 595 sock_fdpos = 0; 596 port_pos = 0; 597 nc_handle = setnetconfig(); 598 while ((nconf = getnetconfig(nc_handle))) { 599 if (nconf->nc_flag & NC_VISIBLE) { 600 if (have_v6 == 0 && strcmp(nconf->nc_protofmly, 601 "inet6") == 0) { 602 /* DO NOTHING */ 603 } else if (port_list != NULL) { 604 if (port_pos >= port_len) { 605 syslog(LOG_ERR, "too many port#s"); 606 exit(1); 607 } 608 complete_service(nconf, port_list[port_pos++]); 609 } else 610 complete_service(nconf, svcport_str); 611 } 612 } 613 endnetconfig(nc_handle); 614 free(sock_fd); 615 if (port_list != NULL) { 616 for (port_pos = 0; port_pos < port_len; port_pos++) 617 free(port_list[port_pos]); 618 free(port_list); 619 } 620 621 if (xcreated == 0) { 622 syslog(LOG_ERR, "could not create any services"); 623 exit(1); 624 } 625 626 /* Expand svc_run() here so that we can call get_exportlist(). */ 627 for (;;) { 628 if (got_sighup) { 629 get_exportlist(); 630 got_sighup = 0; 631 } 632 readfds = svc_fdset; 633 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) { 634 case -1: 635 if (errno == EINTR) 636 continue; 637 syslog(LOG_ERR, "mountd died: select: %m"); 638 exit(1); 639 case 0: 640 continue; 641 default: 642 svc_getreqset(&readfds); 643 } 644 } 645} 646 647/* 648 * This routine creates and binds sockets on the appropriate 649 * addresses. It gets called one time for each transport. 650 * It returns 0 upon success, 1 for ingore the call and -1 to indicate 651 * bind failed with EADDRINUSE. 652 * Any file descriptors that have been created are stored in sock_fd and 653 * the total count of them is maintained in sock_fdcnt. 654 */ 655static int 656create_service(struct netconfig *nconf) 657{ 658 struct addrinfo hints, *res = NULL; 659 struct sockaddr_in *sin; 660 struct sockaddr_in6 *sin6; 661 struct __rpc_sockinfo si; 662 int aicode; 663 int fd; 664 int nhostsbak; 665 int one = 1; 666 int r; 667 u_int32_t host_addr[4]; /* IPv4 or IPv6 */ 668 int mallocd_res; 669 670 if ((nconf->nc_semantics != NC_TPI_CLTS) && 671 (nconf->nc_semantics != NC_TPI_COTS) && 672 (nconf->nc_semantics != NC_TPI_COTS_ORD)) 673 return (1); /* not my type */ 674 675 /* 676 * XXX - using RPC library internal functions. 677 */ 678 if (!__rpc_nconf2sockinfo(nconf, &si)) { 679 syslog(LOG_ERR, "cannot get information for %s", 680 nconf->nc_netid); 681 return (1); 682 } 683 684 /* Get mountd's address on this transport */ 685 memset(&hints, 0, sizeof hints); 686 hints.ai_family = si.si_af; 687 hints.ai_socktype = si.si_socktype; 688 hints.ai_protocol = si.si_proto; 689 690 /* 691 * Bind to specific IPs if asked to 692 */ 693 nhostsbak = nhosts; 694 while (nhostsbak > 0) { 695 --nhostsbak; 696 sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int)); 697 if (sock_fd == NULL) 698 out_of_mem(); 699 sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */ 700 mallocd_res = 0; 701 702 hints.ai_flags = AI_PASSIVE; 703 704 /* 705 * XXX - using RPC library internal functions. 706 */ 707 if ((fd = __rpc_nconf2fd(nconf)) < 0) { 708 int non_fatal = 0; 709 if (errno == EAFNOSUPPORT && 710 nconf->nc_semantics != NC_TPI_CLTS) 711 non_fatal = 1; 712 713 syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 714 "cannot create socket for %s", nconf->nc_netid); 715 if (non_fatal != 0) 716 continue; 717 exit(1); 718 } 719 720 switch (hints.ai_family) { 721 case AF_INET: 722 if (inet_pton(AF_INET, hosts[nhostsbak], 723 host_addr) == 1) { 724 hints.ai_flags |= AI_NUMERICHOST; 725 } else { 726 /* 727 * Skip if we have an AF_INET6 address. 728 */ 729 if (inet_pton(AF_INET6, hosts[nhostsbak], 730 host_addr) == 1) { 731 close(fd); 732 continue; 733 } 734 } 735 break; 736 case AF_INET6: 737 if (inet_pton(AF_INET6, hosts[nhostsbak], 738 host_addr) == 1) { 739 hints.ai_flags |= AI_NUMERICHOST; 740 } else { 741 /* 742 * Skip if we have an AF_INET address. 743 */ 744 if (inet_pton(AF_INET, hosts[nhostsbak], 745 host_addr) == 1) { 746 close(fd); 747 continue; 748 } 749 } 750 751 /* 752 * We're doing host-based access checks here, so don't 753 * allow v4-in-v6 to confuse things. The kernel will 754 * disable it by default on NFS sockets too. 755 */ 756 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, 757 sizeof one) < 0) { 758 syslog(LOG_ERR, 759 "can't disable v4-in-v6 on IPv6 socket"); 760 exit(1); 761 } 762 break; 763 default: 764 break; 765 } 766 767 /* 768 * If no hosts were specified, just bind to INADDR_ANY 769 */ 770 if (strcmp("*", hosts[nhostsbak]) == 0) { 771 if (svcport_str == NULL) { 772 res = malloc(sizeof(struct addrinfo)); 773 if (res == NULL) 774 out_of_mem(); 775 mallocd_res = 1; 776 res->ai_flags = hints.ai_flags; 777 res->ai_family = hints.ai_family; 778 res->ai_protocol = hints.ai_protocol; 779 switch (res->ai_family) { 780 case AF_INET: 781 sin = malloc(sizeof(struct sockaddr_in)); 782 if (sin == NULL) 783 out_of_mem(); 784 sin->sin_family = AF_INET; 785 sin->sin_port = htons(0); 786 sin->sin_addr.s_addr = htonl(INADDR_ANY); 787 res->ai_addr = (struct sockaddr*) sin; 788 res->ai_addrlen = (socklen_t) 789 sizeof(struct sockaddr_in); 790 break; 791 case AF_INET6: 792 sin6 = malloc(sizeof(struct sockaddr_in6)); 793 if (sin6 == NULL) 794 out_of_mem(); 795 sin6->sin6_family = AF_INET6; 796 sin6->sin6_port = htons(0); 797 sin6->sin6_addr = in6addr_any; 798 res->ai_addr = (struct sockaddr*) sin6; 799 res->ai_addrlen = (socklen_t) 800 sizeof(struct sockaddr_in6); 801 break; 802 default: 803 syslog(LOG_ERR, "bad addr fam %d", 804 res->ai_family); 805 exit(1); 806 } 807 } else { 808 if ((aicode = getaddrinfo(NULL, svcport_str, 809 &hints, &res)) != 0) { 810 syslog(LOG_ERR, 811 "cannot get local address for %s: %s", 812 nconf->nc_netid, 813 gai_strerror(aicode)); 814 close(fd); 815 continue; 816 } 817 } 818 } else { 819 if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, 820 &hints, &res)) != 0) { 821 syslog(LOG_ERR, 822 "cannot get local address for %s: %s", 823 nconf->nc_netid, gai_strerror(aicode)); 824 close(fd); 825 continue; 826 } 827 } 828 829 /* Store the fd. */ 830 sock_fd[sock_fdcnt - 1] = fd; 831 832 /* Now, attempt the bind. */ 833 r = bindresvport_sa(fd, res->ai_addr); 834 if (r != 0) { 835 if (errno == EADDRINUSE && mallocd_svcport != 0) { 836 if (mallocd_res != 0) { 837 free(res->ai_addr); 838 free(res); 839 } else 840 freeaddrinfo(res); 841 return (-1); 842 } 843 syslog(LOG_ERR, "bindresvport_sa: %m"); 844 exit(1); 845 } 846 847 if (svcport_str == NULL) { 848 svcport_str = malloc(NI_MAXSERV * sizeof(char)); 849 if (svcport_str == NULL) 850 out_of_mem(); 851 mallocd_svcport = 1; 852 853 if (getnameinfo(res->ai_addr, 854 res->ai_addr->sa_len, NULL, NI_MAXHOST, 855 svcport_str, NI_MAXSERV * sizeof(char), 856 NI_NUMERICHOST | NI_NUMERICSERV)) 857 errx(1, "Cannot get port number"); 858 } 859 if (mallocd_res != 0) { 860 free(res->ai_addr); 861 free(res); 862 } else 863 freeaddrinfo(res); 864 res = NULL; 865 } 866 return (0); 867} 868 869/* 870 * Called after all the create_service() calls have succeeded, to complete 871 * the setup and registration. 872 */ 873static void 874complete_service(struct netconfig *nconf, char *port_str) 875{ 876 struct addrinfo hints, *res = NULL; 877 struct __rpc_sockinfo si; 878 struct netbuf servaddr; 879 SVCXPRT *transp = NULL; 880 int aicode, fd, nhostsbak; 881 int registered = 0; 882 883 if ((nconf->nc_semantics != NC_TPI_CLTS) && 884 (nconf->nc_semantics != NC_TPI_COTS) && 885 (nconf->nc_semantics != NC_TPI_COTS_ORD)) 886 return; /* not my type */ 887 888 /* 889 * XXX - using RPC library internal functions. 890 */ 891 if (!__rpc_nconf2sockinfo(nconf, &si)) { 892 syslog(LOG_ERR, "cannot get information for %s", 893 nconf->nc_netid); 894 return; 895 } 896 897 nhostsbak = nhosts; 898 while (nhostsbak > 0) { 899 --nhostsbak; 900 if (sock_fdpos >= sock_fdcnt) { 901 /* Should never happen. */ 902 syslog(LOG_ERR, "Ran out of socket fd's"); 903 return; 904 } 905 fd = sock_fd[sock_fdpos++]; 906 if (fd < 0) 907 continue; 908 909 if (nconf->nc_semantics != NC_TPI_CLTS) 910 listen(fd, SOMAXCONN); 911 912 if (nconf->nc_semantics == NC_TPI_CLTS ) 913 transp = svc_dg_create(fd, 0, 0); 914 else 915 transp = svc_vc_create(fd, RPC_MAXDATASIZE, 916 RPC_MAXDATASIZE); 917 918 if (transp != (SVCXPRT *) NULL) { 919 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv, 920 NULL)) 921 syslog(LOG_ERR, 922 "can't register %s MOUNTVERS service", 923 nconf->nc_netid); 924 if (!force_v2) { 925 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3, 926 mntsrv, NULL)) 927 syslog(LOG_ERR, 928 "can't register %s MOUNTVERS3 service", 929 nconf->nc_netid); 930 } 931 } else 932 syslog(LOG_WARNING, "can't create %s services", 933 nconf->nc_netid); 934 935 if (registered == 0) { 936 registered = 1; 937 memset(&hints, 0, sizeof hints); 938 hints.ai_flags = AI_PASSIVE; 939 hints.ai_family = si.si_af; 940 hints.ai_socktype = si.si_socktype; 941 hints.ai_protocol = si.si_proto; 942 943 if ((aicode = getaddrinfo(NULL, port_str, &hints, 944 &res)) != 0) { 945 syslog(LOG_ERR, "cannot get local address: %s", 946 gai_strerror(aicode)); 947 exit(1); 948 } 949 950 servaddr.buf = malloc(res->ai_addrlen); 951 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); 952 servaddr.len = res->ai_addrlen; 953 954 rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr); 955 rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr); 956 957 xcreated++; 958 freeaddrinfo(res); 959 } 960 } /* end while */ 961} 962 963/* 964 * Clear out sockets after a failure to bind one of them, so that the 965 * cycle of socket creation/binding can start anew. 966 */ 967static void 968clearout_service(void) 969{ 970 int i; 971 972 for (i = 0; i < sock_fdcnt; i++) { 973 if (sock_fd[i] >= 0) { 974 shutdown(sock_fd[i], SHUT_RDWR); 975 close(sock_fd[i]); 976 } 977 } 978} 979 980static void 981usage(void) 982{ 983 fprintf(stderr, 984 "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] " 985 "[-S] [-h <bindip>] [export_file ...]\n"); 986 exit(1); 987} 988 989/* 990 * The mount rpc service 991 */ 992void 993mntsrv(struct svc_req *rqstp, SVCXPRT *transp) 994{ 995 struct exportlist *ep; 996 struct dirlist *dp; 997 struct fhreturn fhr; 998 struct stat stb; 999 struct statfs fsb; 1000 char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 1001 int lookup_failed = 1; 1002 struct sockaddr *saddr; 1003 u_short sport; 1004 char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; 1005 int bad = 0, defset, hostset; 1006 sigset_t sighup_mask; 1007 int numsecflavors, *secflavorsp; 1008 1009 sigemptyset(&sighup_mask); 1010 sigaddset(&sighup_mask, SIGHUP); 1011 saddr = svc_getrpccaller(transp)->buf; 1012 switch (saddr->sa_family) { 1013 case AF_INET6: 1014 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 1015 break; 1016 case AF_INET: 1017 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 1018 break; 1019 default: 1020 syslog(LOG_ERR, "request from unknown address family"); 1021 return; 1022 } 1023 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 1024 NULL, 0, 0); 1025 getnameinfo(saddr, saddr->sa_len, numerichost, 1026 sizeof numerichost, NULL, 0, NI_NUMERICHOST); 1027 switch (rqstp->rq_proc) { 1028 case NULLPROC: 1029 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) 1030 syslog(LOG_ERR, "can't send reply"); 1031 return; 1032 case MOUNTPROC_MNT: 1033 if (sport >= IPPORT_RESERVED && resvport_only) { 1034 syslog(LOG_NOTICE, 1035 "mount request from %s from unprivileged port", 1036 numerichost); 1037 svcerr_weakauth(transp); 1038 return; 1039 } 1040 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 1041 syslog(LOG_NOTICE, "undecodable mount request from %s", 1042 numerichost); 1043 svcerr_decode(transp); 1044 return; 1045 } 1046 1047 /* 1048 * Get the real pathname and make sure it is a directory 1049 * or a regular file if the -r option was specified 1050 * and it exists. 1051 */ 1052 if (realpath(rpcpath, dirpath) == NULL || 1053 stat(dirpath, &stb) < 0 || 1054 statfs(dirpath, &fsb) < 0) { 1055 chdir("/"); /* Just in case realpath doesn't */ 1056 syslog(LOG_NOTICE, 1057 "mount request from %s for non existent path %s", 1058 numerichost, dirpath); 1059 if (debug) 1060 warnx("stat failed on %s", dirpath); 1061 bad = ENOENT; /* We will send error reply later */ 1062 } 1063 if (!bad && 1064 !S_ISDIR(stb.st_mode) && 1065 (dir_only || !S_ISREG(stb.st_mode))) { 1066 syslog(LOG_NOTICE, 1067 "mount request from %s for non-directory path %s", 1068 numerichost, dirpath); 1069 if (debug) 1070 warnx("mounting non-directory %s", dirpath); 1071 bad = ENOTDIR; /* We will send error reply later */ 1072 } 1073 1074 /* Check in the exports list */ 1075 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1076 if (bad) 1077 ep = NULL; 1078 else 1079 ep = ex_search(&fsb.f_fsid); 1080 hostset = defset = 0; 1081 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, 1082 &numsecflavors, &secflavorsp) || 1083 ((dp = dirp_search(ep->ex_dirl, dirpath)) && 1084 chk_host(dp, saddr, &defset, &hostset, &numsecflavors, 1085 &secflavorsp)) || 1086 (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 1087 scan_tree(ep->ex_dirl, saddr) == 0))) { 1088 if (bad) { 1089 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 1090 (caddr_t)&bad)) 1091 syslog(LOG_ERR, "can't send reply"); 1092 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1093 return; 1094 } 1095 if (hostset & DP_HOSTSET) { 1096 fhr.fhr_flag = hostset; 1097 fhr.fhr_numsecflavors = numsecflavors; 1098 fhr.fhr_secflavors = secflavorsp; 1099 } else { 1100 fhr.fhr_flag = defset; 1101 fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; 1102 fhr.fhr_secflavors = ep->ex_defsecflavors; 1103 } 1104 fhr.fhr_vers = rqstp->rq_vers; 1105 /* Get the file handle */ 1106 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 1107 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 1108 bad = errno; 1109 syslog(LOG_ERR, "can't get fh for %s", dirpath); 1110 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 1111 (caddr_t)&bad)) 1112 syslog(LOG_ERR, "can't send reply"); 1113 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1114 return; 1115 } 1116 if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, 1117 (caddr_t)&fhr)) 1118 syslog(LOG_ERR, "can't send reply"); 1119 if (!lookup_failed) 1120 add_mlist(host, dirpath); 1121 else 1122 add_mlist(numerichost, dirpath); 1123 if (debug) 1124 warnx("mount successful"); 1125 if (dolog) 1126 syslog(LOG_NOTICE, 1127 "mount request succeeded from %s for %s", 1128 numerichost, dirpath); 1129 } else { 1130 if (!bad) 1131 bad = EACCES; 1132 syslog(LOG_NOTICE, 1133 "mount request denied from %s for %s", 1134 numerichost, dirpath); 1135 } 1136 1137 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, 1138 (caddr_t)&bad)) 1139 syslog(LOG_ERR, "can't send reply"); 1140 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1141 return; 1142 case MOUNTPROC_DUMP: 1143 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) 1144 syslog(LOG_ERR, "can't send reply"); 1145 else if (dolog) 1146 syslog(LOG_NOTICE, 1147 "dump request succeeded from %s", 1148 numerichost); 1149 return; 1150 case MOUNTPROC_UMNT: 1151 if (sport >= IPPORT_RESERVED && resvport_only) { 1152 syslog(LOG_NOTICE, 1153 "umount request from %s from unprivileged port", 1154 numerichost); 1155 svcerr_weakauth(transp); 1156 return; 1157 } 1158 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 1159 syslog(LOG_NOTICE, "undecodable umount request from %s", 1160 numerichost); 1161 svcerr_decode(transp); 1162 return; 1163 } 1164 if (realpath(rpcpath, dirpath) == NULL) { 1165 syslog(LOG_NOTICE, "umount request from %s " 1166 "for non existent path %s", 1167 numerichost, dirpath); 1168 } 1169 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 1170 syslog(LOG_ERR, "can't send reply"); 1171 if (!lookup_failed) 1172 del_mlist(host, dirpath); 1173 del_mlist(numerichost, dirpath); 1174 if (dolog) 1175 syslog(LOG_NOTICE, 1176 "umount request succeeded from %s for %s", 1177 numerichost, dirpath); 1178 return; 1179 case MOUNTPROC_UMNTALL: 1180 if (sport >= IPPORT_RESERVED && resvport_only) { 1181 syslog(LOG_NOTICE, 1182 "umountall request from %s from unprivileged port", 1183 numerichost); 1184 svcerr_weakauth(transp); 1185 return; 1186 } 1187 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 1188 syslog(LOG_ERR, "can't send reply"); 1189 if (!lookup_failed) 1190 del_mlist(host, NULL); 1191 del_mlist(numerichost, NULL); 1192 if (dolog) 1193 syslog(LOG_NOTICE, 1194 "umountall request succeeded from %s", 1195 numerichost); 1196 return; 1197 case MOUNTPROC_EXPORT: 1198 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) 1199 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, 1200 (caddr_t)NULL)) 1201 syslog(LOG_ERR, "can't send reply"); 1202 if (dolog) 1203 syslog(LOG_NOTICE, 1204 "export request succeeded from %s", 1205 numerichost); 1206 return; 1207 default: 1208 svcerr_noproc(transp); 1209 return; 1210 } 1211} 1212 1213/* 1214 * Xdr conversion for a dirpath string 1215 */ 1216static int 1217xdr_dir(XDR *xdrsp, char *dirp) 1218{ 1219 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1220} 1221 1222/* 1223 * Xdr routine to generate file handle reply 1224 */ 1225static int 1226xdr_fhs(XDR *xdrsp, caddr_t cp) 1227{ 1228 struct fhreturn *fhrp = (struct fhreturn *)cp; 1229 u_long ok = 0, len, auth; 1230 int i; 1231 1232 if (!xdr_long(xdrsp, &ok)) 1233 return (0); 1234 switch (fhrp->fhr_vers) { 1235 case 1: 1236 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 1237 case 3: 1238 len = NFSX_V3FH; 1239 if (!xdr_long(xdrsp, &len)) 1240 return (0); 1241 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 1242 return (0); 1243 if (fhrp->fhr_numsecflavors) { 1244 if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors)) 1245 return (0); 1246 for (i = 0; i < fhrp->fhr_numsecflavors; i++) 1247 if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i])) 1248 return (0); 1249 return (1); 1250 } else { 1251 auth = AUTH_SYS; 1252 len = 1; 1253 if (!xdr_long(xdrsp, &len)) 1254 return (0); 1255 return (xdr_long(xdrsp, &auth)); 1256 } 1257 } 1258 return (0); 1259} 1260 1261static int 1262xdr_mlist(XDR *xdrsp, caddr_t cp __unused) 1263{ 1264 struct mountlist *mlp; 1265 int true = 1; 1266 int false = 0; 1267 char *strp; 1268 1269 SLIST_FOREACH(mlp, &mlhead, next) { 1270 if (!xdr_bool(xdrsp, &true)) 1271 return (0); 1272 strp = &mlp->ml_host[0]; 1273 if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) 1274 return (0); 1275 strp = &mlp->ml_dirp[0]; 1276 if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1277 return (0); 1278 } 1279 if (!xdr_bool(xdrsp, &false)) 1280 return (0); 1281 return (1); 1282} 1283 1284/* 1285 * Xdr conversion for export list 1286 */ 1287static int 1288xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief) 1289{ 1290 struct exportlist *ep; 1291 int false = 0; 1292 int putdef; 1293 sigset_t sighup_mask; 1294 1295 sigemptyset(&sighup_mask); 1296 sigaddset(&sighup_mask, SIGHUP); 1297 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 1298 1299 SLIST_FOREACH(ep, &exphead, entries) { 1300 putdef = 0; 1301 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, 1302 &putdef, brief)) 1303 goto errout; 1304 if (ep->ex_defdir && putdef == 0 && 1305 put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, 1306 &putdef, brief)) 1307 goto errout; 1308 } 1309 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1310 if (!xdr_bool(xdrsp, &false)) 1311 return (0); 1312 return (1); 1313errout: 1314 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 1315 return (0); 1316} 1317 1318/* 1319 * Called from xdr_explist() to traverse the tree and export the 1320 * directory paths. 1321 */ 1322static int 1323put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp, 1324 int brief) 1325{ 1326 struct grouplist *grp; 1327 struct hostlist *hp; 1328 int true = 1; 1329 int false = 0; 1330 int gotalldir = 0; 1331 char *strp; 1332 1333 if (dp) { 1334 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) 1335 return (1); 1336 if (!xdr_bool(xdrsp, &true)) 1337 return (1); 1338 strp = dp->dp_dirp; 1339 if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1340 return (1); 1341 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 1342 gotalldir = 1; 1343 *putdefp = 1; 1344 } 1345 if (brief) { 1346 if (!xdr_bool(xdrsp, &true)) 1347 return (1); 1348 strp = "(...)"; 1349 if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) 1350 return (1); 1351 } else if ((dp->dp_flag & DP_DEFSET) == 0 && 1352 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 1353 hp = dp->dp_hosts; 1354 while (hp) { 1355 grp = hp->ht_grp; 1356 if (grp->gr_type == GT_HOST) { 1357 if (!xdr_bool(xdrsp, &true)) 1358 return (1); 1359 strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 1360 if (!xdr_string(xdrsp, &strp, 1361 MNTNAMLEN)) 1362 return (1); 1363 } else if (grp->gr_type == GT_NET) { 1364 if (!xdr_bool(xdrsp, &true)) 1365 return (1); 1366 strp = grp->gr_ptr.gt_net.nt_name; 1367 if (!xdr_string(xdrsp, &strp, 1368 MNTNAMLEN)) 1369 return (1); 1370 } 1371 hp = hp->ht_next; 1372 if (gotalldir && hp == (struct hostlist *)NULL) { 1373 hp = adp->dp_hosts; 1374 gotalldir = 0; 1375 } 1376 } 1377 } 1378 if (!xdr_bool(xdrsp, &false)) 1379 return (1); 1380 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) 1381 return (1); 1382 } 1383 return (0); 1384} 1385 1386static int 1387xdr_explist(XDR *xdrsp, caddr_t cp) 1388{ 1389 1390 return xdr_explist_common(xdrsp, cp, 0); 1391} 1392 1393static int 1394xdr_explist_brief(XDR *xdrsp, caddr_t cp) 1395{ 1396 1397 return xdr_explist_common(xdrsp, cp, 1); 1398} 1399 1400static char *line; 1401static size_t linesize; 1402static FILE *exp_file; 1403 1404/* 1405 * Get the export list from one, currently open file 1406 */ 1407static void 1408get_exportlist_one(void) 1409{ 1410 struct exportlist *ep; 1411 struct grouplist *grp, *tgrp; 1412 struct dirlist *dirhead; 1413 struct statfs fsb; 1414 struct xucred anon; 1415 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 1416 int len, has_host, exflags, got_nondir, dirplen, netgrp; 1417 1418 v4root_phase = 0; 1419 dirhead = (struct dirlist *)NULL; 1420 while (get_line()) { 1421 if (debug) 1422 warnx("got line %s", line); 1423 cp = line; 1424 nextfield(&cp, &endcp); 1425 if (*cp == '#') 1426 goto nextline; 1427 1428 /* 1429 * Set defaults. 1430 */ 1431 has_host = FALSE; 1432 anon = def_anon; 1433 exflags = MNT_EXPORTED; 1434 got_nondir = 0; 1435 opt_flags = 0; 1436 ep = (struct exportlist *)NULL; 1437 dirp = NULL; 1438 1439 /* 1440 * Handle the V4 root dir. 1441 */ 1442 if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') { 1443 /* 1444 * V4: just indicates that it is the v4 root point, 1445 * so skip over that and set v4root_phase. 1446 */ 1447 if (v4root_phase > 0) { 1448 syslog(LOG_ERR, "V4:duplicate line, ignored"); 1449 goto nextline; 1450 } 1451 v4root_phase = 1; 1452 cp += 3; 1453 nextfield(&cp, &endcp); 1454 } 1455 1456 /* 1457 * Create new exports list entry 1458 */ 1459 len = endcp-cp; 1460 tgrp = grp = get_grp(); 1461 while (len > 0) { 1462 if (len > MNTNAMLEN) { 1463 getexp_err(ep, tgrp, "mountpoint too long"); 1464 goto nextline; 1465 } 1466 if (*cp == '-') { 1467 if (ep == (struct exportlist *)NULL) { 1468 getexp_err(ep, tgrp, 1469 "flag before export path definition"); 1470 goto nextline; 1471 } 1472 if (debug) 1473 warnx("doing opt %s", cp); 1474 got_nondir = 1; 1475 if (do_opt(&cp, &endcp, ep, grp, &has_host, 1476 &exflags, &anon)) { 1477 getexp_err(ep, tgrp, NULL); 1478 goto nextline; 1479 } 1480 } else if (*cp == '/') { 1481 savedc = *endcp; 1482 *endcp = '\0'; 1483 if (v4root_phase > 1) { 1484 if (dirp != NULL) { 1485 getexp_err(ep, tgrp, "Multiple V4 dirs"); 1486 goto nextline; 1487 } 1488 } 1489 if (check_dirpath(cp) && 1490 statfs(cp, &fsb) >= 0) { 1491 if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0) 1492 syslog(LOG_ERR, "Warning: exporting of " 1493 "automounted fs %s not supported", cp); 1494 if (got_nondir) { 1495 getexp_err(ep, tgrp, "dirs must be first"); 1496 goto nextline; 1497 } 1498 if (v4root_phase == 1) { 1499 if (dirp != NULL) { 1500 getexp_err(ep, tgrp, "Multiple V4 dirs"); 1501 goto nextline; 1502 } 1503 if (strlen(v4root_dirpath) == 0) { 1504 strlcpy(v4root_dirpath, cp, 1505 sizeof (v4root_dirpath)); 1506 } else if (strcmp(v4root_dirpath, cp) 1507 != 0) { 1508 syslog(LOG_ERR, 1509 "different V4 dirpath %s", cp); 1510 getexp_err(ep, tgrp, NULL); 1511 goto nextline; 1512 } 1513 dirp = cp; 1514 v4root_phase = 2; 1515 got_nondir = 1; 1516 ep = get_exp(); 1517 } else { 1518 if (ep) { 1519 if (ep->ex_fs.val[0] != 1520 fsb.f_fsid.val[0] || 1521 ep->ex_fs.val[1] != 1522 fsb.f_fsid.val[1]) { 1523 getexp_err(ep, tgrp, 1524 "fsid mismatch"); 1525 goto nextline; 1526 } 1527 } else { 1528 /* 1529 * See if this directory is already 1530 * in the list. 1531 */ 1532 ep = ex_search(&fsb.f_fsid); 1533 if (ep == (struct exportlist *)NULL) { 1534 ep = get_exp(); 1535 ep->ex_fs = fsb.f_fsid; 1536 ep->ex_fsdir = strdup(fsb.f_mntonname); 1537 if (ep->ex_fsdir == NULL) 1538 out_of_mem(); 1539 if (debug) 1540 warnx( 1541 "making new ep fs=0x%x,0x%x", 1542 fsb.f_fsid.val[0], 1543 fsb.f_fsid.val[1]); 1544 } else if (debug) 1545 warnx("found ep fs=0x%x,0x%x", 1546 fsb.f_fsid.val[0], 1547 fsb.f_fsid.val[1]); 1548 } 1549 1550 /* 1551 * Add dirpath to export mount point. 1552 */ 1553 dirp = add_expdir(&dirhead, cp, len); 1554 dirplen = len; 1555 } 1556 } else { 1557 getexp_err(ep, tgrp, 1558 "symbolic link in export path or statfs failed"); 1559 goto nextline; 1560 } 1561 *endcp = savedc; 1562 } else { 1563 savedc = *endcp; 1564 *endcp = '\0'; 1565 got_nondir = 1; 1566 if (ep == (struct exportlist *)NULL) { 1567 getexp_err(ep, tgrp, 1568 "host(s) before export path definition"); 1569 goto nextline; 1570 } 1571 1572 /* 1573 * Get the host or netgroup. 1574 */ 1575 setnetgrent(cp); 1576 netgrp = getnetgrent(&hst, &usr, &dom); 1577 do { 1578 if (has_host) { 1579 grp->gr_next = get_grp(); 1580 grp = grp->gr_next; 1581 } 1582 if (netgrp) { 1583 if (hst == 0) { 1584 syslog(LOG_ERR, 1585 "null hostname in netgroup %s, skipping", cp); 1586 grp->gr_type = GT_IGNORE; 1587 } else if (get_host(hst, grp, tgrp)) { 1588 syslog(LOG_ERR, 1589 "bad host %s in netgroup %s, skipping", hst, cp); 1590 grp->gr_type = GT_IGNORE; 1591 } 1592 } else if (get_host(cp, grp, tgrp)) { 1593 syslog(LOG_ERR, "bad host %s, skipping", cp); 1594 grp->gr_type = GT_IGNORE; 1595 } 1596 has_host = TRUE; 1597 } while (netgrp && getnetgrent(&hst, &usr, &dom)); 1598 endnetgrent(); 1599 *endcp = savedc; 1600 } 1601 cp = endcp; 1602 nextfield(&cp, &endcp); 1603 len = endcp - cp; 1604 } 1605 if (check_options(dirhead)) { 1606 getexp_err(ep, tgrp, NULL); 1607 goto nextline; 1608 } 1609 if (!has_host) { 1610 grp->gr_type = GT_DEFAULT; 1611 if (debug) 1612 warnx("adding a default entry"); 1613 1614 /* 1615 * Don't allow a network export coincide with a list of 1616 * host(s) on the same line. 1617 */ 1618 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1619 getexp_err(ep, tgrp, "network/host conflict"); 1620 goto nextline; 1621 1622 /* 1623 * If an export list was specified on this line, make sure 1624 * that we have at least one valid entry, otherwise skip it. 1625 */ 1626 } else { 1627 grp = tgrp; 1628 while (grp && grp->gr_type == GT_IGNORE) 1629 grp = grp->gr_next; 1630 if (! grp) { 1631 getexp_err(ep, tgrp, "no valid entries"); 1632 goto nextline; 1633 } 1634 } 1635 1636 if (v4root_phase == 1) { 1637 getexp_err(ep, tgrp, "V4:root, no dirp, ignored"); 1638 goto nextline; 1639 } 1640 1641 /* 1642 * Loop through hosts, pushing the exports into the kernel. 1643 * After loop, tgrp points to the start of the list and 1644 * grp points to the last entry in the list. 1645 */ 1646 grp = tgrp; 1647 do { 1648 if (do_mount(ep, grp, exflags, &anon, dirp, dirplen, 1649 &fsb)) { 1650 getexp_err(ep, tgrp, NULL); 1651 goto nextline; 1652 } 1653 } while (grp->gr_next && (grp = grp->gr_next)); 1654 1655 /* 1656 * For V4: don't enter in mount lists. 1657 */ 1658 if (v4root_phase > 0 && v4root_phase <= 2) { 1659 /* 1660 * Since these structures aren't used by mountd, 1661 * free them up now. 1662 */ 1663 if (ep != NULL) 1664 free_exp(ep); 1665 while (tgrp != NULL) { 1666 grp = tgrp; 1667 tgrp = tgrp->gr_next; 1668 free_grp(grp); 1669 } 1670 goto nextline; 1671 } 1672 1673 /* 1674 * Success. Update the data structures. 1675 */ 1676 if (has_host) { 1677 hang_dirp(dirhead, tgrp, ep, opt_flags); 1678 grp->gr_next = grphead; 1679 grphead = tgrp; 1680 } else { 1681 hang_dirp(dirhead, (struct grouplist *)NULL, ep, 1682 opt_flags); 1683 free_grp(grp); 1684 } 1685 dirhead = (struct dirlist *)NULL; 1686 if ((ep->ex_flag & EX_LINKED) == 0) { 1687 SLIST_INSERT_HEAD(&exphead, ep, entries); 1688 1689 ep->ex_flag |= EX_LINKED; 1690 } 1691nextline: 1692 v4root_phase = 0; 1693 if (dirhead) { 1694 free_dir(dirhead); 1695 dirhead = (struct dirlist *)NULL; 1696 } 1697 } 1698} 1699 1700/* 1701 * Get the export list from all specified files 1702 */ 1703static void 1704get_exportlist(void) 1705{ 1706 struct exportlist *ep, *ep2; 1707 struct grouplist *grp, *tgrp; 1708 struct export_args export; 1709 struct iovec *iov; 1710 struct statfs *fsp, *mntbufp; 1711 struct xvfsconf vfc; 1712 char errmsg[255]; 1713 int num, i; 1714 int iovlen; 1715 int done; 1716 struct nfsex_args eargs; 1717 1718 if (suspend_nfsd != 0) 1719 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); 1720 v4root_dirpath[0] = '\0'; 1721 bzero(&export, sizeof(export)); 1722 export.ex_flags = MNT_DELEXPORT; 1723 iov = NULL; 1724 iovlen = 0; 1725 bzero(errmsg, sizeof(errmsg)); 1726 1727 /* 1728 * First, get rid of the old list 1729 */ 1730 SLIST_FOREACH_SAFE(ep, &exphead, entries, ep2) { 1731 SLIST_REMOVE(&exphead, ep, exportlist, entries); 1732 free_exp(ep); 1733 } 1734 1735 grp = grphead; 1736 while (grp) { 1737 tgrp = grp; 1738 grp = grp->gr_next; 1739 free_grp(tgrp); 1740 } 1741 grphead = (struct grouplist *)NULL; 1742 1743 /* 1744 * and the old V4 root dir. 1745 */ 1746 bzero(&eargs, sizeof (eargs)); 1747 eargs.export.ex_flags = MNT_DELEXPORT; 1748 if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 && 1749 errno != ENOENT) 1750 syslog(LOG_ERR, "Can't delete exports for V4:"); 1751 1752 /* 1753 * and clear flag that notes if a public fh has been exported. 1754 */ 1755 has_publicfh = 0; 1756 1757 /* 1758 * And delete exports that are in the kernel for all local 1759 * filesystems. 1760 * XXX: Should know how to handle all local exportable filesystems. 1761 */ 1762 num = getmntinfo(&mntbufp, MNT_NOWAIT); 1763 1764 if (num > 0) { 1765 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 1766 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 1767 build_iovec(&iov, &iovlen, "from", NULL, 0); 1768 build_iovec(&iov, &iovlen, "update", NULL, 0); 1769 build_iovec(&iov, &iovlen, "export", &export, sizeof(export)); 1770 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 1771 } 1772 1773 for (i = 0; i < num; i++) { 1774 fsp = &mntbufp[i]; 1775 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 1776 syslog(LOG_ERR, "getvfsbyname() failed for %s", 1777 fsp->f_fstypename); 1778 continue; 1779 } 1780 1781 /* 1782 * We do not need to delete "export" flag from 1783 * filesystems that do not have it set. 1784 */ 1785 if (!(fsp->f_flags & MNT_EXPORTED)) 1786 continue; 1787 /* 1788 * Do not delete export for network filesystem by 1789 * passing "export" arg to nmount(). 1790 * It only makes sense to do this for local filesystems. 1791 */ 1792 if (vfc.vfc_flags & VFCF_NETWORK) 1793 continue; 1794 1795 iov[1].iov_base = fsp->f_fstypename; 1796 iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 1797 iov[3].iov_base = fsp->f_mntonname; 1798 iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 1799 iov[5].iov_base = fsp->f_mntfromname; 1800 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 1801 errmsg[0] = '\0'; 1802 1803 /* 1804 * EXDEV is returned when path exists but is not a 1805 * mount point. May happens if raced with unmount. 1806 */ 1807 if (nmount(iov, iovlen, fsp->f_flags) < 0 && 1808 errno != ENOENT && errno != ENOTSUP && errno != EXDEV) { 1809 syslog(LOG_ERR, 1810 "can't delete exports for %s: %m %s", 1811 fsp->f_mntonname, errmsg); 1812 } 1813 } 1814 1815 if (iov != NULL) { 1816 /* Free strings allocated by strdup() in getmntopts.c */ 1817 free(iov[0].iov_base); /* fstype */ 1818 free(iov[2].iov_base); /* fspath */ 1819 free(iov[4].iov_base); /* from */ 1820 free(iov[6].iov_base); /* update */ 1821 free(iov[8].iov_base); /* export */ 1822 free(iov[10].iov_base); /* errmsg */ 1823 1824 /* free iov, allocated by realloc() */ 1825 free(iov); 1826 iovlen = 0; 1827 } 1828 1829 /* 1830 * Read in the exports file and build the list, calling 1831 * nmount() as we go along to push the export rules into the kernel. 1832 */ 1833 done = 0; 1834 for (i = 0; exnames[i] != NULL; i++) { 1835 if (debug) 1836 warnx("reading exports from %s", exnames[i]); 1837 if ((exp_file = fopen(exnames[i], "r")) == NULL) { 1838 syslog(LOG_WARNING, "can't open %s", exnames[i]); 1839 continue; 1840 } 1841 get_exportlist_one(); 1842 fclose(exp_file); 1843 done++; 1844 } 1845 if (done == 0) { 1846 syslog(LOG_ERR, "can't open any exports file"); 1847 exit(2); 1848 } 1849 1850 /* 1851 * If there was no public fh, clear any previous one set. 1852 */ 1853 if (has_publicfh == 0) 1854 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); 1855 1856 /* Resume the nfsd. If they weren't suspended, this is harmless. */ 1857 (void)nfssvc(NFSSVC_RESUMENFSD, NULL); 1858} 1859 1860/* 1861 * Allocate an export list element 1862 */ 1863static struct exportlist * 1864get_exp(void) 1865{ 1866 struct exportlist *ep; 1867 1868 ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); 1869 if (ep == (struct exportlist *)NULL) 1870 out_of_mem(); 1871 return (ep); 1872} 1873 1874/* 1875 * Allocate a group list element 1876 */ 1877static struct grouplist * 1878get_grp(void) 1879{ 1880 struct grouplist *gp; 1881 1882 gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); 1883 if (gp == (struct grouplist *)NULL) 1884 out_of_mem(); 1885 return (gp); 1886} 1887 1888/* 1889 * Clean up upon an error in get_exportlist(). 1890 */ 1891static void 1892getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) 1893{ 1894 struct grouplist *tgrp; 1895 1896 if (!(opt_flags & OP_QUIET)) { 1897 if (reason != NULL) 1898 syslog(LOG_ERR, "bad exports list line '%s': %s", line, 1899 reason); 1900 else 1901 syslog(LOG_ERR, "bad exports list line '%s'", line); 1902 } 1903 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1904 free_exp(ep); 1905 while (grp) { 1906 tgrp = grp; 1907 grp = grp->gr_next; 1908 free_grp(tgrp); 1909 } 1910} 1911 1912/* 1913 * Search the export list for a matching fs. 1914 */ 1915static struct exportlist * 1916ex_search(fsid_t *fsid) 1917{ 1918 struct exportlist *ep; 1919 1920 SLIST_FOREACH(ep, &exphead, entries) { 1921 if (ep->ex_fs.val[0] == fsid->val[0] && 1922 ep->ex_fs.val[1] == fsid->val[1]) 1923 return (ep); 1924 } 1925 1926 return (ep); 1927} 1928 1929/* 1930 * Add a directory path to the list. 1931 */ 1932static char * 1933add_expdir(struct dirlist **dpp, char *cp, int len) 1934{ 1935 struct dirlist *dp; 1936 1937 dp = malloc(sizeof (struct dirlist)); 1938 if (dp == (struct dirlist *)NULL) 1939 out_of_mem(); 1940 dp->dp_left = *dpp; 1941 dp->dp_right = (struct dirlist *)NULL; 1942 dp->dp_flag = 0; 1943 dp->dp_hosts = (struct hostlist *)NULL; 1944 dp->dp_dirp = strndup(cp, len); 1945 if (dp->dp_dirp == NULL) 1946 out_of_mem(); 1947 *dpp = dp; 1948 return (dp->dp_dirp); 1949} 1950 1951/* 1952 * Hang the dir list element off the dirpath binary tree as required 1953 * and update the entry for host. 1954 */ 1955static void 1956hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 1957 int flags) 1958{ 1959 struct hostlist *hp; 1960 struct dirlist *dp2; 1961 1962 if (flags & OP_ALLDIRS) { 1963 if (ep->ex_defdir) 1964 free((caddr_t)dp); 1965 else 1966 ep->ex_defdir = dp; 1967 if (grp == (struct grouplist *)NULL) { 1968 ep->ex_defdir->dp_flag |= DP_DEFSET; 1969 /* Save the default security flavors list. */ 1970 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 1971 if (ep->ex_numsecflavors > 0) 1972 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 1973 sizeof(ep->ex_secflavors)); 1974 } else while (grp) { 1975 hp = get_ht(); 1976 hp->ht_grp = grp; 1977 hp->ht_next = ep->ex_defdir->dp_hosts; 1978 ep->ex_defdir->dp_hosts = hp; 1979 /* Save the security flavors list for this host set. */ 1980 grp->gr_numsecflavors = ep->ex_numsecflavors; 1981 if (ep->ex_numsecflavors > 0) 1982 memcpy(grp->gr_secflavors, ep->ex_secflavors, 1983 sizeof(ep->ex_secflavors)); 1984 grp = grp->gr_next; 1985 } 1986 } else { 1987 1988 /* 1989 * Loop through the directories adding them to the tree. 1990 */ 1991 while (dp) { 1992 dp2 = dp->dp_left; 1993 add_dlist(&ep->ex_dirl, dp, grp, flags, ep); 1994 dp = dp2; 1995 } 1996 } 1997} 1998 1999/* 2000 * Traverse the binary tree either updating a node that is already there 2001 * for the new directory or adding the new node. 2002 */ 2003static void 2004add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 2005 int flags, struct exportlist *ep) 2006{ 2007 struct dirlist *dp; 2008 struct hostlist *hp; 2009 int cmp; 2010 2011 dp = *dpp; 2012 if (dp) { 2013 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 2014 if (cmp > 0) { 2015 add_dlist(&dp->dp_left, newdp, grp, flags, ep); 2016 return; 2017 } else if (cmp < 0) { 2018 add_dlist(&dp->dp_right, newdp, grp, flags, ep); 2019 return; 2020 } else 2021 free((caddr_t)newdp); 2022 } else { 2023 dp = newdp; 2024 dp->dp_left = (struct dirlist *)NULL; 2025 *dpp = dp; 2026 } 2027 if (grp) { 2028 2029 /* 2030 * Hang all of the host(s) off of the directory point. 2031 */ 2032 do { 2033 hp = get_ht(); 2034 hp->ht_grp = grp; 2035 hp->ht_next = dp->dp_hosts; 2036 dp->dp_hosts = hp; 2037 /* Save the security flavors list for this host set. */ 2038 grp->gr_numsecflavors = ep->ex_numsecflavors; 2039 if (ep->ex_numsecflavors > 0) 2040 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2041 sizeof(ep->ex_secflavors)); 2042 grp = grp->gr_next; 2043 } while (grp); 2044 } else { 2045 dp->dp_flag |= DP_DEFSET; 2046 /* Save the default security flavors list. */ 2047 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2048 if (ep->ex_numsecflavors > 0) 2049 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2050 sizeof(ep->ex_secflavors)); 2051 } 2052} 2053 2054/* 2055 * Search for a dirpath on the export point. 2056 */ 2057static struct dirlist * 2058dirp_search(struct dirlist *dp, char *dirp) 2059{ 2060 int cmp; 2061 2062 if (dp) { 2063 cmp = strcmp(dp->dp_dirp, dirp); 2064 if (cmp > 0) 2065 return (dirp_search(dp->dp_left, dirp)); 2066 else if (cmp < 0) 2067 return (dirp_search(dp->dp_right, dirp)); 2068 else 2069 return (dp); 2070 } 2071 return (dp); 2072} 2073 2074/* 2075 * Scan for a host match in a directory tree. 2076 */ 2077static int 2078chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 2079 int *hostsetp, int *numsecflavors, int **secflavorsp) 2080{ 2081 struct hostlist *hp; 2082 struct grouplist *grp; 2083 struct addrinfo *ai; 2084 2085 if (dp) { 2086 if (dp->dp_flag & DP_DEFSET) 2087 *defsetp = dp->dp_flag; 2088 hp = dp->dp_hosts; 2089 while (hp) { 2090 grp = hp->ht_grp; 2091 switch (grp->gr_type) { 2092 case GT_HOST: 2093 ai = grp->gr_ptr.gt_addrinfo; 2094 for (; ai; ai = ai->ai_next) { 2095 if (!sacmp(ai->ai_addr, saddr, NULL)) { 2096 *hostsetp = 2097 (hp->ht_flag | DP_HOSTSET); 2098 if (numsecflavors != NULL) { 2099 *numsecflavors = 2100 grp->gr_numsecflavors; 2101 *secflavorsp = 2102 grp->gr_secflavors; 2103 } 2104 return (1); 2105 } 2106 } 2107 break; 2108 case GT_NET: 2109 if (!sacmp(saddr, (struct sockaddr *) 2110 &grp->gr_ptr.gt_net.nt_net, 2111 (struct sockaddr *) 2112 &grp->gr_ptr.gt_net.nt_mask)) { 2113 *hostsetp = (hp->ht_flag | DP_HOSTSET); 2114 if (numsecflavors != NULL) { 2115 *numsecflavors = 2116 grp->gr_numsecflavors; 2117 *secflavorsp = 2118 grp->gr_secflavors; 2119 } 2120 return (1); 2121 } 2122 break; 2123 } 2124 hp = hp->ht_next; 2125 } 2126 } 2127 return (0); 2128} 2129 2130/* 2131 * Scan tree for a host that matches the address. 2132 */ 2133static int 2134scan_tree(struct dirlist *dp, struct sockaddr *saddr) 2135{ 2136 int defset, hostset; 2137 2138 if (dp) { 2139 if (scan_tree(dp->dp_left, saddr)) 2140 return (1); 2141 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) 2142 return (1); 2143 if (scan_tree(dp->dp_right, saddr)) 2144 return (1); 2145 } 2146 return (0); 2147} 2148 2149/* 2150 * Traverse the dirlist tree and free it up. 2151 */ 2152static void 2153free_dir(struct dirlist *dp) 2154{ 2155 2156 if (dp) { 2157 free_dir(dp->dp_left); 2158 free_dir(dp->dp_right); 2159 free_host(dp->dp_hosts); 2160 free(dp->dp_dirp); 2161 free(dp); 2162 } 2163} 2164 2165/* 2166 * Parse a colon separated list of security flavors 2167 */ 2168static int 2169parsesec(char *seclist, struct exportlist *ep) 2170{ 2171 char *cp, savedc; 2172 int flavor; 2173 2174 ep->ex_numsecflavors = 0; 2175 for (;;) { 2176 cp = strchr(seclist, ':'); 2177 if (cp) { 2178 savedc = *cp; 2179 *cp = '\0'; 2180 } 2181 2182 if (!strcmp(seclist, "sys")) 2183 flavor = AUTH_SYS; 2184 else if (!strcmp(seclist, "krb5")) 2185 flavor = RPCSEC_GSS_KRB5; 2186 else if (!strcmp(seclist, "krb5i")) 2187 flavor = RPCSEC_GSS_KRB5I; 2188 else if (!strcmp(seclist, "krb5p")) 2189 flavor = RPCSEC_GSS_KRB5P; 2190 else { 2191 if (cp) 2192 *cp = savedc; 2193 syslog(LOG_ERR, "bad sec flavor: %s", seclist); 2194 return (1); 2195 } 2196 if (ep->ex_numsecflavors == MAXSECFLAVORS) { 2197 if (cp) 2198 *cp = savedc; 2199 syslog(LOG_ERR, "too many sec flavors: %s", seclist); 2200 return (1); 2201 } 2202 ep->ex_secflavors[ep->ex_numsecflavors] = flavor; 2203 ep->ex_numsecflavors++; 2204 if (cp) { 2205 *cp = savedc; 2206 seclist = cp + 1; 2207 } else { 2208 break; 2209 } 2210 } 2211 return (0); 2212} 2213 2214/* 2215 * Parse the option string and update fields. 2216 * Option arguments may either be -<option>=<value> or 2217 * -<option> <value> 2218 */ 2219static int 2220do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 2221 int *has_hostp, int *exflagsp, struct xucred *cr) 2222{ 2223 char *cpoptarg, *cpoptend; 2224 char *cp, *endcp, *cpopt, savedc, savedc2; 2225 int allflag, usedarg; 2226 2227 savedc2 = '\0'; 2228 cpopt = *cpp; 2229 cpopt++; 2230 cp = *endcpp; 2231 savedc = *cp; 2232 *cp = '\0'; 2233 while (cpopt && *cpopt) { 2234 allflag = 1; 2235 usedarg = -2; 2236 if ((cpoptend = strchr(cpopt, ','))) { 2237 *cpoptend++ = '\0'; 2238 if ((cpoptarg = strchr(cpopt, '='))) 2239 *cpoptarg++ = '\0'; 2240 } else { 2241 if ((cpoptarg = strchr(cpopt, '='))) 2242 *cpoptarg++ = '\0'; 2243 else { 2244 *cp = savedc; 2245 nextfield(&cp, &endcp); 2246 **endcpp = '\0'; 2247 if (endcp > cp && *cp != '-') { 2248 cpoptarg = cp; 2249 savedc2 = *endcp; 2250 *endcp = '\0'; 2251 usedarg = 0; 2252 } 2253 } 2254 } 2255 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 2256 *exflagsp |= MNT_EXRDONLY; 2257 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 2258 !(allflag = strcmp(cpopt, "mapall")) || 2259 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 2260 usedarg++; 2261 parsecred(cpoptarg, cr); 2262 if (allflag == 0) { 2263 *exflagsp |= MNT_EXPORTANON; 2264 opt_flags |= OP_MAPALL; 2265 } else 2266 opt_flags |= OP_MAPROOT; 2267 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 2268 !strcmp(cpopt, "m"))) { 2269 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 2270 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 2271 return (1); 2272 } 2273 usedarg++; 2274 opt_flags |= OP_MASK; 2275 } else if (cpoptarg && (!strcmp(cpopt, "network") || 2276 !strcmp(cpopt, "n"))) { 2277 if (strchr(cpoptarg, '/') != NULL) { 2278 if (debug) 2279 fprintf(stderr, "setting OP_MASKLEN\n"); 2280 opt_flags |= OP_MASKLEN; 2281 } 2282 if (grp->gr_type != GT_NULL) { 2283 syslog(LOG_ERR, "network/host conflict"); 2284 return (1); 2285 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 2286 syslog(LOG_ERR, "bad net: %s", cpoptarg); 2287 return (1); 2288 } 2289 grp->gr_type = GT_NET; 2290 *has_hostp = 1; 2291 usedarg++; 2292 opt_flags |= OP_NET; 2293 } else if (!strcmp(cpopt, "alldirs")) { 2294 opt_flags |= OP_ALLDIRS; 2295 } else if (!strcmp(cpopt, "public")) { 2296 *exflagsp |= MNT_EXPUBLIC; 2297 } else if (!strcmp(cpopt, "webnfs")) { 2298 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 2299 opt_flags |= OP_MAPALL; 2300 } else if (cpoptarg && !strcmp(cpopt, "index")) { 2301 ep->ex_indexfile = strdup(cpoptarg); 2302 } else if (!strcmp(cpopt, "quiet")) { 2303 opt_flags |= OP_QUIET; 2304 } else if (cpoptarg && !strcmp(cpopt, "sec")) { 2305 if (parsesec(cpoptarg, ep)) 2306 return (1); 2307 opt_flags |= OP_SEC; 2308 usedarg++; 2309 } else { 2310 syslog(LOG_ERR, "bad opt %s", cpopt); 2311 return (1); 2312 } 2313 if (usedarg >= 0) { 2314 *endcp = savedc2; 2315 **endcpp = savedc; 2316 if (usedarg > 0) { 2317 *cpp = cp; 2318 *endcpp = endcp; 2319 } 2320 return (0); 2321 } 2322 cpopt = cpoptend; 2323 } 2324 **endcpp = savedc; 2325 return (0); 2326} 2327 2328/* 2329 * Translate a character string to the corresponding list of network 2330 * addresses for a hostname. 2331 */ 2332static int 2333get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 2334{ 2335 struct grouplist *checkgrp; 2336 struct addrinfo *ai, *tai, hints; 2337 int ecode; 2338 char host[NI_MAXHOST]; 2339 2340 if (grp->gr_type != GT_NULL) { 2341 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 2342 return (1); 2343 } 2344 memset(&hints, 0, sizeof hints); 2345 hints.ai_flags = AI_CANONNAME; 2346 hints.ai_protocol = IPPROTO_UDP; 2347 ecode = getaddrinfo(cp, NULL, &hints, &ai); 2348 if (ecode != 0) { 2349 syslog(LOG_ERR,"can't get address info for host %s", cp); 2350 return 1; 2351 } 2352 grp->gr_ptr.gt_addrinfo = ai; 2353 while (ai != NULL) { 2354 if (ai->ai_canonname == NULL) { 2355 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 2356 sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 2357 strlcpy(host, "?", sizeof(host)); 2358 ai->ai_canonname = strdup(host); 2359 ai->ai_flags |= AI_CANONNAME; 2360 } 2361 if (debug) 2362 fprintf(stderr, "got host %s\n", ai->ai_canonname); 2363 /* 2364 * Sanity check: make sure we don't already have an entry 2365 * for this host in the grouplist. 2366 */ 2367 for (checkgrp = tgrp; checkgrp != NULL; 2368 checkgrp = checkgrp->gr_next) { 2369 if (checkgrp->gr_type != GT_HOST) 2370 continue; 2371 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 2372 tai = tai->ai_next) { 2373 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 2374 continue; 2375 if (debug) 2376 fprintf(stderr, 2377 "ignoring duplicate host %s\n", 2378 ai->ai_canonname); 2379 grp->gr_type = GT_IGNORE; 2380 return (0); 2381 } 2382 } 2383 ai = ai->ai_next; 2384 } 2385 grp->gr_type = GT_HOST; 2386 return (0); 2387} 2388 2389/* 2390 * Free up an exports list component 2391 */ 2392static void 2393free_exp(struct exportlist *ep) 2394{ 2395 2396 if (ep->ex_defdir) { 2397 free_host(ep->ex_defdir->dp_hosts); 2398 free((caddr_t)ep->ex_defdir); 2399 } 2400 if (ep->ex_fsdir) 2401 free(ep->ex_fsdir); 2402 if (ep->ex_indexfile) 2403 free(ep->ex_indexfile); 2404 free_dir(ep->ex_dirl); 2405 free((caddr_t)ep); 2406} 2407 2408/* 2409 * Free hosts. 2410 */ 2411static void 2412free_host(struct hostlist *hp) 2413{ 2414 struct hostlist *hp2; 2415 2416 while (hp) { 2417 hp2 = hp; 2418 hp = hp->ht_next; 2419 free((caddr_t)hp2); 2420 } 2421} 2422 2423static struct hostlist * 2424get_ht(void) 2425{ 2426 struct hostlist *hp; 2427 2428 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 2429 if (hp == (struct hostlist *)NULL) 2430 out_of_mem(); 2431 hp->ht_next = (struct hostlist *)NULL; 2432 hp->ht_flag = 0; 2433 return (hp); 2434} 2435 2436/* 2437 * Out of memory, fatal 2438 */ 2439static void 2440out_of_mem(void) 2441{ 2442 2443 syslog(LOG_ERR, "out of memory"); 2444 exit(2); 2445} 2446 2447/* 2448 * Do the nmount() syscall with the update flag to push the export info into 2449 * the kernel. 2450 */ 2451static int 2452do_mount(struct exportlist *ep, struct grouplist *grp, int exflags, 2453 struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb) 2454{ 2455 struct statfs fsb1; 2456 struct addrinfo *ai; 2457 struct export_args *eap; 2458 char errmsg[255]; 2459 char *cp; 2460 int done; 2461 char savedc; 2462 struct iovec *iov; 2463 int i, iovlen; 2464 int ret; 2465 struct nfsex_args nfsea; 2466 2467 eap = &nfsea.export; 2468 2469 cp = NULL; 2470 savedc = '\0'; 2471 iov = NULL; 2472 iovlen = 0; 2473 ret = 0; 2474 2475 bzero(eap, sizeof (struct export_args)); 2476 bzero(errmsg, sizeof(errmsg)); 2477 eap->ex_flags = exflags; 2478 eap->ex_anon = *anoncrp; 2479 eap->ex_indexfile = ep->ex_indexfile; 2480 if (grp->gr_type == GT_HOST) 2481 ai = grp->gr_ptr.gt_addrinfo; 2482 else 2483 ai = NULL; 2484 eap->ex_numsecflavors = ep->ex_numsecflavors; 2485 for (i = 0; i < eap->ex_numsecflavors; i++) 2486 eap->ex_secflavors[i] = ep->ex_secflavors[i]; 2487 if (eap->ex_numsecflavors == 0) { 2488 eap->ex_numsecflavors = 1; 2489 eap->ex_secflavors[0] = AUTH_SYS; 2490 } 2491 done = FALSE; 2492 2493 if (v4root_phase == 0) { 2494 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 2495 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 2496 build_iovec(&iov, &iovlen, "from", NULL, 0); 2497 build_iovec(&iov, &iovlen, "update", NULL, 0); 2498 build_iovec(&iov, &iovlen, "export", eap, 2499 sizeof (struct export_args)); 2500 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 2501 } 2502 2503 while (!done) { 2504 switch (grp->gr_type) { 2505 case GT_HOST: 2506 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 2507 goto skip; 2508 eap->ex_addr = ai->ai_addr; 2509 eap->ex_addrlen = ai->ai_addrlen; 2510 eap->ex_masklen = 0; 2511 break; 2512 case GT_NET: 2513 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 2514 have_v6 == 0) 2515 goto skip; 2516 eap->ex_addr = 2517 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 2518 eap->ex_addrlen = 2519 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 2520 eap->ex_mask = 2521 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 2522 eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 2523 break; 2524 case GT_DEFAULT: 2525 eap->ex_addr = NULL; 2526 eap->ex_addrlen = 0; 2527 eap->ex_mask = NULL; 2528 eap->ex_masklen = 0; 2529 break; 2530 case GT_IGNORE: 2531 ret = 0; 2532 goto error_exit; 2533 break; 2534 default: 2535 syslog(LOG_ERR, "bad grouptype"); 2536 if (cp) 2537 *cp = savedc; 2538 ret = 1; 2539 goto error_exit; 2540 } 2541 2542 /* 2543 * For V4:, use the nfssvc() syscall, instead of mount(). 2544 */ 2545 if (v4root_phase == 2) { 2546 nfsea.fspec = v4root_dirpath; 2547 if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) { 2548 syslog(LOG_ERR, "Exporting V4: failed"); 2549 return (2); 2550 } 2551 } else { 2552 /* 2553 * XXX: 2554 * Maybe I should just use the fsb->f_mntonname path 2555 * instead of looping back up the dirp to the mount 2556 * point?? 2557 * Also, needs to know how to export all types of local 2558 * exportable filesystems and not just "ufs". 2559 */ 2560 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 2561 iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 2562 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 2563 iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 2564 iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 2565 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 2566 errmsg[0] = '\0'; 2567 2568 while (nmount(iov, iovlen, fsb->f_flags) < 0) { 2569 if (cp) 2570 *cp-- = savedc; 2571 else 2572 cp = dirp + dirplen - 1; 2573 if (opt_flags & OP_QUIET) { 2574 ret = 1; 2575 goto error_exit; 2576 } 2577 if (errno == EPERM) { 2578 if (debug) 2579 warnx("can't change attributes for %s: %s", 2580 dirp, errmsg); 2581 syslog(LOG_ERR, 2582 "can't change attributes for %s: %s", 2583 dirp, errmsg); 2584 ret = 1; 2585 goto error_exit; 2586 } 2587 if (opt_flags & OP_ALLDIRS) { 2588 if (errno == EINVAL) 2589 syslog(LOG_ERR, 2590 "-alldirs requested but %s is not a filesystem mountpoint", 2591 dirp); 2592 else 2593 syslog(LOG_ERR, 2594 "could not remount %s: %m", 2595 dirp); 2596 ret = 1; 2597 goto error_exit; 2598 } 2599 /* back up over the last component */ 2600 while (*cp == '/' && cp > dirp) 2601 cp--; 2602 while (*(cp - 1) != '/' && cp > dirp) 2603 cp--; 2604 if (cp == dirp) { 2605 if (debug) 2606 warnx("mnt unsucc"); 2607 syslog(LOG_ERR, "can't export %s %s", 2608 dirp, errmsg); 2609 ret = 1; 2610 goto error_exit; 2611 } 2612 savedc = *cp; 2613 *cp = '\0'; 2614 /* 2615 * Check that we're still on the same 2616 * filesystem. 2617 */ 2618 if (statfs(dirp, &fsb1) != 0 || 2619 bcmp(&fsb1.f_fsid, &fsb->f_fsid, 2620 sizeof (fsb1.f_fsid)) != 0) { 2621 *cp = savedc; 2622 syslog(LOG_ERR, 2623 "can't export %s %s", dirp, 2624 errmsg); 2625 ret = 1; 2626 goto error_exit; 2627 } 2628 } 2629 } 2630 2631 /* 2632 * For the experimental server: 2633 * If this is the public directory, get the file handle 2634 * and load it into the kernel via the nfssvc() syscall. 2635 */ 2636 if ((exflags & MNT_EXPUBLIC) != 0) { 2637 fhandle_t fh; 2638 char *public_name; 2639 2640 if (eap->ex_indexfile != NULL) 2641 public_name = eap->ex_indexfile; 2642 else 2643 public_name = dirp; 2644 if (getfh(public_name, &fh) < 0) 2645 syslog(LOG_ERR, 2646 "Can't get public fh for %s", public_name); 2647 else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) 2648 syslog(LOG_ERR, 2649 "Can't set public fh for %s", public_name); 2650 else 2651 has_publicfh = 1; 2652 } 2653skip: 2654 if (ai != NULL) 2655 ai = ai->ai_next; 2656 if (ai == NULL) 2657 done = TRUE; 2658 } 2659 if (cp) 2660 *cp = savedc; 2661error_exit: 2662 /* free strings allocated by strdup() in getmntopts.c */ 2663 if (iov != NULL) { 2664 free(iov[0].iov_base); /* fstype */ 2665 free(iov[2].iov_base); /* fspath */ 2666 free(iov[4].iov_base); /* from */ 2667 free(iov[6].iov_base); /* update */ 2668 free(iov[8].iov_base); /* export */ 2669 free(iov[10].iov_base); /* errmsg */ 2670 2671 /* free iov, allocated by realloc() */ 2672 free(iov); 2673 } 2674 return (ret); 2675} 2676 2677/* 2678 * Translate a net address. 2679 * 2680 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 2681 */ 2682static int 2683get_net(char *cp, struct netmsk *net, int maskflg) 2684{ 2685 struct netent *np = NULL; 2686 char *name, *p, *prefp; 2687 struct sockaddr_in sin; 2688 struct sockaddr *sa = NULL; 2689 struct addrinfo hints, *ai = NULL; 2690 char netname[NI_MAXHOST]; 2691 long preflen; 2692 2693 p = prefp = NULL; 2694 if ((opt_flags & OP_MASKLEN) && !maskflg) { 2695 p = strchr(cp, '/'); 2696 *p = '\0'; 2697 prefp = p + 1; 2698 } 2699 2700 /* 2701 * Check for a numeric address first. We wish to avoid 2702 * possible DNS lookups in getnetbyname(). 2703 */ 2704 if (isxdigit(*cp) || *cp == ':') { 2705 memset(&hints, 0, sizeof hints); 2706 /* Ensure the mask and the network have the same family. */ 2707 if (maskflg && (opt_flags & OP_NET)) 2708 hints.ai_family = net->nt_net.ss_family; 2709 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 2710 hints.ai_family = net->nt_mask.ss_family; 2711 else 2712 hints.ai_family = AF_UNSPEC; 2713 hints.ai_flags = AI_NUMERICHOST; 2714 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 2715 sa = ai->ai_addr; 2716 if (sa != NULL && ai->ai_family == AF_INET) { 2717 /* 2718 * The address in `cp' is really a network address, so 2719 * use inet_network() to re-interpret this correctly. 2720 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 2721 */ 2722 bzero(&sin, sizeof sin); 2723 sin.sin_family = AF_INET; 2724 sin.sin_len = sizeof sin; 2725 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 2726 if (debug) 2727 fprintf(stderr, "get_net: v4 addr %s\n", 2728 inet_ntoa(sin.sin_addr)); 2729 sa = (struct sockaddr *)&sin; 2730 } 2731 } 2732 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 2733 bzero(&sin, sizeof sin); 2734 sin.sin_family = AF_INET; 2735 sin.sin_len = sizeof sin; 2736 sin.sin_addr = inet_makeaddr(np->n_net, 0); 2737 sa = (struct sockaddr *)&sin; 2738 } 2739 if (sa == NULL) 2740 goto fail; 2741 2742 if (maskflg) { 2743 /* The specified sockaddr is a mask. */ 2744 if (checkmask(sa) != 0) 2745 goto fail; 2746 bcopy(sa, &net->nt_mask, sa->sa_len); 2747 opt_flags |= OP_HAVEMASK; 2748 } else { 2749 /* The specified sockaddr is a network address. */ 2750 bcopy(sa, &net->nt_net, sa->sa_len); 2751 2752 /* Get a network name for the export list. */ 2753 if (np) { 2754 name = np->n_name; 2755 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 2756 NULL, 0, NI_NUMERICHOST) == 0) { 2757 name = netname; 2758 } else { 2759 goto fail; 2760 } 2761 if ((net->nt_name = strdup(name)) == NULL) 2762 out_of_mem(); 2763 2764 /* 2765 * Extract a mask from either a "/<masklen>" suffix, or 2766 * from the class of an IPv4 address. 2767 */ 2768 if (opt_flags & OP_MASKLEN) { 2769 preflen = strtol(prefp, NULL, 10); 2770 if (preflen < 0L || preflen == LONG_MAX) 2771 goto fail; 2772 bcopy(sa, &net->nt_mask, sa->sa_len); 2773 if (makemask(&net->nt_mask, (int)preflen) != 0) 2774 goto fail; 2775 opt_flags |= OP_HAVEMASK; 2776 *p = '/'; 2777 } else if (sa->sa_family == AF_INET && 2778 (opt_flags & OP_MASK) == 0) { 2779 in_addr_t addr; 2780 2781 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 2782 if (IN_CLASSA(addr)) 2783 preflen = 8; 2784 else if (IN_CLASSB(addr)) 2785 preflen = 16; 2786 else if (IN_CLASSC(addr)) 2787 preflen = 24; 2788 else if (IN_CLASSD(addr)) 2789 preflen = 28; 2790 else 2791 preflen = 32; /* XXX */ 2792 2793 bcopy(sa, &net->nt_mask, sa->sa_len); 2794 makemask(&net->nt_mask, (int)preflen); 2795 opt_flags |= OP_HAVEMASK; 2796 } 2797 } 2798 2799 if (ai) 2800 freeaddrinfo(ai); 2801 return 0; 2802 2803fail: 2804 if (ai) 2805 freeaddrinfo(ai); 2806 return 1; 2807} 2808 2809/* 2810 * Parse out the next white space separated field 2811 */ 2812static void 2813nextfield(char **cp, char **endcp) 2814{ 2815 char *p; 2816 2817 p = *cp; 2818 while (*p == ' ' || *p == '\t') 2819 p++; 2820 if (*p == '\n' || *p == '\0') 2821 *cp = *endcp = p; 2822 else { 2823 *cp = p++; 2824 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 2825 p++; 2826 *endcp = p; 2827 } 2828} 2829 2830/* 2831 * Get an exports file line. Skip over blank lines and handle line 2832 * continuations. 2833 */ 2834static int 2835get_line(void) 2836{ 2837 char *p, *cp; 2838 size_t len; 2839 int totlen, cont_line; 2840 2841 /* 2842 * Loop around ignoring blank lines and getting all continuation lines. 2843 */ 2844 p = line; 2845 totlen = 0; 2846 do { 2847 if ((p = fgetln(exp_file, &len)) == NULL) 2848 return (0); 2849 cp = p + len - 1; 2850 cont_line = 0; 2851 while (cp >= p && 2852 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 2853 if (*cp == '\\') 2854 cont_line = 1; 2855 cp--; 2856 len--; 2857 } 2858 if (cont_line) { 2859 *++cp = ' '; 2860 len++; 2861 } 2862 if (linesize < len + totlen + 1) { 2863 linesize = len + totlen + 1; 2864 line = realloc(line, linesize); 2865 if (line == NULL) 2866 out_of_mem(); 2867 } 2868 memcpy(line + totlen, p, len); 2869 totlen += len; 2870 line[totlen] = '\0'; 2871 } while (totlen == 0 || cont_line); 2872 return (1); 2873} 2874 2875/* 2876 * Parse a description of a credential. 2877 */ 2878static void 2879parsecred(char *namelist, struct xucred *cr) 2880{ 2881 char *name; 2882 int cnt; 2883 char *names; 2884 struct passwd *pw; 2885 struct group *gr; 2886 gid_t groups[XU_NGROUPS + 1]; 2887 int ngroups; 2888 2889 cr->cr_version = XUCRED_VERSION; 2890 /* 2891 * Set up the unprivileged user. 2892 */ 2893 cr->cr_uid = -2; 2894 cr->cr_groups[0] = -2; 2895 cr->cr_ngroups = 1; 2896 /* 2897 * Get the user's password table entry. 2898 */ 2899 names = strsep_quote(&namelist, " \t\n"); 2900 name = strsep(&names, ":"); 2901 /* Bug? name could be NULL here */ 2902 if (isdigit(*name) || *name == '-') 2903 pw = getpwuid(atoi(name)); 2904 else 2905 pw = getpwnam(name); 2906 /* 2907 * Credentials specified as those of a user. 2908 */ 2909 if (names == NULL) { 2910 if (pw == NULL) { 2911 syslog(LOG_ERR, "unknown user: %s", name); 2912 return; 2913 } 2914 cr->cr_uid = pw->pw_uid; 2915 ngroups = XU_NGROUPS + 1; 2916 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 2917 syslog(LOG_ERR, "too many groups"); 2918 /* 2919 * Compress out duplicate. 2920 */ 2921 cr->cr_ngroups = ngroups - 1; 2922 cr->cr_groups[0] = groups[0]; 2923 for (cnt = 2; cnt < ngroups; cnt++) 2924 cr->cr_groups[cnt - 1] = groups[cnt]; 2925 return; 2926 } 2927 /* 2928 * Explicit credential specified as a colon separated list: 2929 * uid:gid:gid:... 2930 */ 2931 if (pw != NULL) 2932 cr->cr_uid = pw->pw_uid; 2933 else if (isdigit(*name) || *name == '-') 2934 cr->cr_uid = atoi(name); 2935 else { 2936 syslog(LOG_ERR, "unknown user: %s", name); 2937 return; 2938 } 2939 cr->cr_ngroups = 0; 2940 while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) { 2941 name = strsep(&names, ":"); 2942 if (isdigit(*name) || *name == '-') { 2943 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 2944 } else { 2945 if ((gr = getgrnam(name)) == NULL) { 2946 syslog(LOG_ERR, "unknown group: %s", name); 2947 continue; 2948 } 2949 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 2950 } 2951 } 2952 if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS) 2953 syslog(LOG_ERR, "too many groups"); 2954} 2955 2956#define STRSIZ (MNTNAMLEN+MNTPATHLEN+50) 2957/* 2958 * Routines that maintain the remote mounttab 2959 */ 2960static void 2961get_mountlist(void) 2962{ 2963 struct mountlist *mlp; 2964 char *host, *dirp, *cp; 2965 char str[STRSIZ]; 2966 FILE *mlfile; 2967 2968 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 2969 if (errno == ENOENT) 2970 return; 2971 else { 2972 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 2973 return; 2974 } 2975 } 2976 while (fgets(str, STRSIZ, mlfile) != NULL) { 2977 cp = str; 2978 host = strsep(&cp, " \t\n"); 2979 dirp = strsep(&cp, " \t\n"); 2980 if (host == NULL || dirp == NULL) 2981 continue; 2982 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2983 if (mlp == (struct mountlist *)NULL) 2984 out_of_mem(); 2985 strncpy(mlp->ml_host, host, MNTNAMLEN); 2986 mlp->ml_host[MNTNAMLEN] = '\0'; 2987 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 2988 mlp->ml_dirp[MNTPATHLEN] = '\0'; 2989 2990 SLIST_INSERT_HEAD(&mlhead, mlp, next); 2991 } 2992 fclose(mlfile); 2993} 2994 2995static void 2996del_mlist(char *hostp, char *dirp) 2997{ 2998 struct mountlist *mlp, *mlp2; 2999 FILE *mlfile; 3000 int fnd = 0; 3001 3002 SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) { 3003 if (!strcmp(mlp->ml_host, hostp) && 3004 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 3005 fnd = 1; 3006 SLIST_REMOVE(&mlhead, mlp, mountlist, next); 3007 free((caddr_t)mlp); 3008 } 3009 } 3010 if (fnd) { 3011 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 3012 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 3013 return; 3014 } 3015 SLIST_FOREACH(mlp, &mlhead, next) { 3016 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3017 } 3018 fclose(mlfile); 3019 } 3020} 3021 3022static void 3023add_mlist(char *hostp, char *dirp) 3024{ 3025 struct mountlist *mlp; 3026 FILE *mlfile; 3027 3028 SLIST_FOREACH(mlp, &mlhead, next) { 3029 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 3030 return; 3031 } 3032 3033 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3034 if (mlp == (struct mountlist *)NULL) 3035 out_of_mem(); 3036 strncpy(mlp->ml_host, hostp, MNTNAMLEN); 3037 mlp->ml_host[MNTNAMLEN] = '\0'; 3038 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3039 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3040 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3041 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 3042 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 3043 return; 3044 } 3045 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3046 fclose(mlfile); 3047} 3048 3049/* 3050 * Free up a group list. 3051 */ 3052static void 3053free_grp(struct grouplist *grp) 3054{ 3055 if (grp->gr_type == GT_HOST) { 3056 if (grp->gr_ptr.gt_addrinfo != NULL) 3057 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 3058 } else if (grp->gr_type == GT_NET) { 3059 if (grp->gr_ptr.gt_net.nt_name) 3060 free(grp->gr_ptr.gt_net.nt_name); 3061 } 3062 free((caddr_t)grp); 3063} 3064 3065#ifdef DEBUG 3066static void 3067SYSLOG(int pri, const char *fmt, ...) 3068{ 3069 va_list ap; 3070 3071 va_start(ap, fmt); 3072 vfprintf(stderr, fmt, ap); 3073 va_end(ap); 3074} 3075#endif /* DEBUG */ 3076 3077/* 3078 * Check options for consistency. 3079 */ 3080static int 3081check_options(struct dirlist *dp) 3082{ 3083 3084 if (v4root_phase == 0 && dp == NULL) 3085 return (1); 3086 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 3087 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 3088 return (1); 3089 } 3090 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 3091 syslog(LOG_ERR, "-mask requires -network"); 3092 return (1); 3093 } 3094 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 3095 syslog(LOG_ERR, "-network requires mask specification"); 3096 return (1); 3097 } 3098 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 3099 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 3100 return (1); 3101 } 3102 if (v4root_phase > 0 && 3103 (opt_flags & 3104 ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) { 3105 syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:"); 3106 return (1); 3107 } 3108 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 3109 syslog(LOG_ERR, "-alldirs has multiple directories"); 3110 return (1); 3111 } 3112 return (0); 3113} 3114 3115/* 3116 * Check an absolute directory path for any symbolic links. Return true 3117 */ 3118static int 3119check_dirpath(char *dirp) 3120{ 3121 char *cp; 3122 int ret = 1; 3123 struct stat sb; 3124 3125 cp = dirp + 1; 3126 while (*cp && ret) { 3127 if (*cp == '/') { 3128 *cp = '\0'; 3129 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 3130 ret = 0; 3131 *cp = '/'; 3132 } 3133 cp++; 3134 } 3135 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 3136 ret = 0; 3137 return (ret); 3138} 3139 3140/* 3141 * Make a netmask according to the specified prefix length. The ss_family 3142 * and other non-address fields must be initialised before calling this. 3143 */ 3144static int 3145makemask(struct sockaddr_storage *ssp, int bitlen) 3146{ 3147 u_char *p; 3148 int bits, i, len; 3149 3150 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 3151 return (-1); 3152 if (bitlen > len * CHAR_BIT) 3153 return (-1); 3154 3155 for (i = 0; i < len; i++) { 3156 bits = MIN(CHAR_BIT, bitlen); 3157 *p++ = (u_char)~0 << (CHAR_BIT - bits); 3158 bitlen -= bits; 3159 } 3160 return 0; 3161} 3162 3163/* 3164 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 3165 * is acceptable (i.e. of the form 1...10....0). 3166 */ 3167static int 3168checkmask(struct sockaddr *sa) 3169{ 3170 u_char *mask; 3171 int i, len; 3172 3173 if ((mask = sa_rawaddr(sa, &len)) == NULL) 3174 return (-1); 3175 3176 for (i = 0; i < len; i++) 3177 if (mask[i] != 0xff) 3178 break; 3179 if (i < len) { 3180 if (~mask[i] & (u_char)(~mask[i] + 1)) 3181 return (-1); 3182 i++; 3183 } 3184 for (; i < len; i++) 3185 if (mask[i] != 0) 3186 return (-1); 3187 return (0); 3188} 3189 3190/* 3191 * Compare two sockaddrs according to a specified mask. Return zero if 3192 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 3193 * If samask is NULL, perform a full comparison. 3194 */ 3195static int 3196sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 3197{ 3198 unsigned char *p1, *p2, *mask; 3199 int len, i; 3200 3201 if (sa1->sa_family != sa2->sa_family || 3202 (p1 = sa_rawaddr(sa1, &len)) == NULL || 3203 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 3204 return (1); 3205 3206 switch (sa1->sa_family) { 3207 case AF_INET6: 3208 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 3209 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 3210 return (1); 3211 break; 3212 } 3213 3214 /* Simple binary comparison if no mask specified. */ 3215 if (samask == NULL) 3216 return (memcmp(p1, p2, len)); 3217 3218 /* Set up the mask, and do a mask-based comparison. */ 3219 if (sa1->sa_family != samask->sa_family || 3220 (mask = sa_rawaddr(samask, NULL)) == NULL) 3221 return (1); 3222 3223 for (i = 0; i < len; i++) 3224 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 3225 return (1); 3226 return (0); 3227} 3228 3229/* 3230 * Return a pointer to the part of the sockaddr that contains the 3231 * raw address, and set *nbytes to its length in bytes. Returns 3232 * NULL if the address family is unknown. 3233 */ 3234static void * 3235sa_rawaddr(struct sockaddr *sa, int *nbytes) { 3236 void *p; 3237 int len; 3238 3239 switch (sa->sa_family) { 3240 case AF_INET: 3241 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 3242 p = &((struct sockaddr_in *)sa)->sin_addr; 3243 break; 3244 case AF_INET6: 3245 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 3246 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 3247 break; 3248 default: 3249 p = NULL; 3250 len = 0; 3251 } 3252 3253 if (nbytes != NULL) 3254 *nbytes = len; 3255 return (p); 3256} 3257 3258static void 3259huphandler(int sig __unused) 3260{ 3261 3262 got_sighup = 1; 3263} 3264 3265static void 3266terminate(int sig __unused) 3267{ 3268 pidfile_remove(pfh); 3269 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 3270 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 3271 exit (0); 3272} 3273