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