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