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