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