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