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