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