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