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