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 num, i; 1892 int iovlen; 1893 struct nfsex_args eargs; 1894 FILE *debug_file; 1895 1896 if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) { 1897 fclose(debug_file); 1898 logdebug = 1; 1899 } else 1900 logdebug = 0; 1901 LOGDEBUG("passno=%d", passno); 1902 v4root_dirpath[0] = '\0'; 1903 free_v4rootexp(); 1904 if (passno == 1) { 1905 /* 1906 * Save the current lists as old ones, so that the new lists 1907 * can be compared with the old ones in the 2nd pass. 1908 */ 1909 for (i = 0; i < exphashsize; i++) { 1910 SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]); 1911 SLIST_INIT(&exphead[i]); 1912 } 1913 1914 /* Note that the public fh has not yet been set. */ 1915 has_set_publicfh = 0; 1916 1917 /* Read the export file(s) and process them */ 1918 read_exportfile(passno); 1919 } else { 1920 /* 1921 * Just make the old lists empty. 1922 * exphashsize == 0 for the first call, before oldexphead 1923 * has been initialized-->loop won't be executed. 1924 */ 1925 for (i = 0; i < exphashsize; i++) 1926 SLIST_INIT(&oldexphead[i]); 1927 } 1928 1929 bzero(&export, sizeof(export)); 1930 export.ex_flags = MNT_DELEXPORT; 1931 iov = NULL; 1932 iovlen = 0; 1933 bzero(errmsg, sizeof(errmsg)); 1934 1935 if (suspend_nfsd != 0) 1936 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); 1937 /* 1938 * Delete the old V4 root dir. 1939 */ 1940 bzero(&eargs, sizeof (eargs)); 1941 eargs.export.ex_flags = MNT_DELEXPORT; 1942 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 && 1943 errno != ENOENT) 1944 syslog(LOG_ERR, "Can't delete exports for V4:"); 1945 1946 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 1947 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 1948 build_iovec(&iov, &iovlen, "from", NULL, 0); 1949 build_iovec(&iov, &iovlen, "update", NULL, 0); 1950 build_iovec(&iov, &iovlen, "export", &export, 1951 sizeof(export)); 1952 build_iovec(&iov, &iovlen, "errmsg", errmsg, 1953 sizeof(errmsg)); 1954 1955 /* 1956 * For passno == 1, compare the old and new lists updating the kernel 1957 * exports for any cases that have changed. 1958 * This call is doing the second pass through the lists. 1959 * If it fails, fall back on the bulk reload. 1960 */ 1961 if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) == 1962 0) { 1963 LOGDEBUG("compareok"); 1964 /* Free up the old lists. */ 1965 free_exports(oldexphead); 1966 } else { 1967 LOGDEBUG("doing passno=0"); 1968 /* 1969 * Clear flag that notes if a public fh has been exported. 1970 * It is set by do_mount() if MNT_EXPUBLIC is set for the entry. 1971 */ 1972 has_publicfh = 0; 1973 1974 /* exphead == NULL if not yet allocated (first call). */ 1975 if (exphead != NULL) { 1976 /* 1977 * First, get rid of the old lists. 1978 */ 1979 free_exports(exphead); 1980 free_exports(oldexphead); 1981 } 1982 1983 /* 1984 * And delete exports that are in the kernel for all local 1985 * filesystems. 1986 * XXX: Should know how to handle all local exportable 1987 * filesystems. 1988 */ 1989 num = getmntinfo(&mntbufp, MNT_NOWAIT); 1990 1991 /* Allocate hash tables, for first call. */ 1992 if (exphead == NULL) { 1993 /* Target an average linked list length of 10. */ 1994 exphashsize = num / 10; 1995 if (exphashsize < 1) 1996 exphashsize = 1; 1997 else if (exphashsize > 100000) 1998 exphashsize = 100000; 1999 exphead = malloc(exphashsize * sizeof(*exphead)); 2000 oldexphead = malloc(exphashsize * sizeof(*oldexphead)); 2001 if (exphead == NULL || oldexphead == NULL) 2002 errx(1, "Can't malloc hash tables"); 2003 2004 for (i = 0; i < exphashsize; i++) { 2005 SLIST_INIT(&exphead[i]); 2006 SLIST_INIT(&oldexphead[i]); 2007 } 2008 } 2009 2010 for (i = 0; i < num; i++) 2011 delete_export(iov, iovlen, &mntbufp[i], errmsg); 2012 2013 2014 /* Read the export file(s) and process them */ 2015 read_exportfile(0); 2016 } 2017 2018 if (iov != NULL) { 2019 /* Free strings allocated by strdup() in getmntopts.c */ 2020 free(iov[0].iov_base); /* fstype */ 2021 free(iov[2].iov_base); /* fspath */ 2022 free(iov[4].iov_base); /* from */ 2023 free(iov[6].iov_base); /* update */ 2024 free(iov[8].iov_base); /* export */ 2025 free(iov[10].iov_base); /* errmsg */ 2026 2027 /* free iov, allocated by realloc() */ 2028 free(iov); 2029 iovlen = 0; 2030 } 2031 2032 /* 2033 * If there was no public fh, clear any previous one set. 2034 */ 2035 if (has_publicfh == 0) { 2036 LOGDEBUG("clear public fh"); 2037 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); 2038 } 2039 2040 /* Resume the nfsd. If they weren't suspended, this is harmless. */ 2041 (void)nfssvc(NFSSVC_RESUMENFSD, NULL); 2042 LOGDEBUG("eo get_exportlist"); 2043 } 2044 2045 /* 2046 * Insert an export entry in the appropriate list. 2047 */ 2048 static void 2049 insert_exports(struct exportlist *ep, struct exportlisthead *exhp) 2050 { 2051 uint32_t i; 2052 2053 i = EXPHASH(&ep->ex_fs); 2054 LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i); 2055 SLIST_INSERT_HEAD(&exhp[i], ep, entries); 2056 } 2057 2058 /* 2059 * Free up the exports lists passed in as arguments. 2060 */ 2061 static void 2062 free_exports(struct exportlisthead *exhp) 2063 { 2064 struct exportlist *ep, *ep2; 2065 int i; 2066 2067 for (i = 0; i < exphashsize; i++) { 2068 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { 2069 SLIST_REMOVE(&exhp[i], ep, exportlist, entries); 2070 free_exp(ep); 2071 } 2072 SLIST_INIT(&exhp[i]); 2073 } 2074 } 2075 2076 /* 2077 * Read the exports file(s) and call get_exportlist_one() for each line. 2078 */ 2079 static void 2080 read_exportfile(int passno) 2081 { 2082 int done, i; 2083 2084 /* 2085 * Read in the exports file and build the list, calling 2086 * nmount() as we go along to push the export rules into the kernel. 2087 */ 2088 done = 0; 2089 for (i = 0; exnames[i] != NULL; i++) { 2090 if (debug) 2091 warnx("reading exports from %s", exnames[i]); 2092 if ((exp_file = fopen(exnames[i], "r")) == NULL) { 2093 syslog(LOG_WARNING, "can't open %s", exnames[i]); 2094 continue; 2095 } 2096 get_exportlist_one(passno); 2097 fclose(exp_file); 2098 done++; 2099 } 2100 if (done == 0) { 2101 syslog(LOG_ERR, "can't open any exports file"); 2102 exit(2); 2103 } 2104 } 2105 2106 /* 2107 * Compare the export lists against the old ones and do nmount() operations 2108 * for any cases that have changed. This avoids doing nmount() for entries 2109 * that have not changed. 2110 * Return 0 upon success, 1 otherwise. 2111 */ 2112 static int 2113 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) 2114 { 2115 struct exportlist *ep, *oep; 2116 struct grouplist *grp; 2117 struct statfs fs, ofs; 2118 int i, ret; 2119 2120 /* 2121 * Loop through the current list and look for an entry in the old 2122 * list. 2123 * If found, check to see if it the same. 2124 * If it is not the same, delete and re-export. 2125 * Then mark it done on the old list. 2126 * else (not found) 2127 * export it. 2128 * Any entries left in the old list after processing must have their 2129 * exports deleted. 2130 */ 2131 for (i = 0; i < exphashsize; i++) 2132 SLIST_FOREACH(ep, &exphead[i], entries) { 2133 LOGDEBUG("foreach ep=%s", ep->ex_fsdir); 2134 oep = ex_search(&ep->ex_fs, oldexphead); 2135 if (oep != NULL) { 2136 /* 2137 * Check the mount paths are the same. 2138 * If not, return 1 so that the reload of the 2139 * exports will be done in bulk, the 2140 * passno == 0 way. 2141 */ 2142 LOGDEBUG("found old exp"); 2143 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2144 return (1); 2145 LOGDEBUG("same fsdir"); 2146 /* 2147 * Test to see if the entry is the same. 2148 * If not the same delete exports and 2149 * re-export. 2150 */ 2151 if (compare_export(ep, oep) != 0) { 2152 /* 2153 * Clear has_publicfh if if was set 2154 * in the old exports, but only if it 2155 * has not been set during processing of 2156 * the exports for this pass, as 2157 * indicated by has_set_publicfh. 2158 */ 2159 if (has_set_publicfh == 0 && 2160 (oep->ex_flag & EX_PUBLICFH) != 0) 2161 has_publicfh = 0; 2162 2163 /* Delete and re-export. */ 2164 if (statfs(ep->ex_fsdir, &fs) < 0) 2165 return (1); 2166 delete_export(iov, iovlen, &fs, errmsg); 2167 ret = do_export_mount(ep, &fs); 2168 if (ret != 0) 2169 return (ret); 2170 } 2171 oep->ex_flag |= EX_DONE; 2172 LOGDEBUG("exdone"); 2173 } else { 2174 LOGDEBUG("not found so export"); 2175 /* Not found, so do export. */ 2176 if (statfs(ep->ex_fsdir, &fs) < 0) 2177 return (1); 2178 ret = do_export_mount(ep, &fs); 2179 if (ret != 0) 2180 return (ret); 2181 } 2182 } 2183 2184 /* Delete exports not done. */ 2185 for (i = 0; i < exphashsize; i++) 2186 SLIST_FOREACH(oep, &oldexphead[i], entries) { 2187 if ((oep->ex_flag & EX_DONE) == 0) { 2188 LOGDEBUG("not done delete=%s", oep->ex_fsdir); 2189 if (statfs(oep->ex_fsdir, &ofs) >= 0 && 2190 fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) { 2191 LOGDEBUG("do delete"); 2192 /* 2193 * Clear has_publicfh if if was set 2194 * in the old exports, but only if it 2195 * has not been set during processing of 2196 * the exports for this pass, as 2197 * indicated by has_set_publicfh. 2198 */ 2199 if (has_set_publicfh == 0 && 2200 (oep->ex_flag & EX_PUBLICFH) != 0) 2201 has_publicfh = 0; 2202 2203 delete_export(iov, iovlen, &ofs, 2204 errmsg); 2205 } 2206 } 2207 } 2208 2209 /* Do the V4 root exports, as required. */ 2210 grp = NULL; 2211 if (v4root_ep != NULL) 2212 grp = v4root_ep->ex_grphead; 2213 v4root_phase = 2; 2214 while (v4root_ep != NULL && grp != NULL) { 2215 LOGDEBUG("v4root expath=%s", v4root_dirpath); 2216 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, 2217 v4root_dirpath, strlen(v4root_dirpath), &fs, 2218 grp->gr_numsecflavors, grp->gr_secflavors); 2219 if (ret != 0) { 2220 v4root_phase = 0; 2221 return (ret); 2222 } 2223 grp = grp->gr_next; 2224 } 2225 v4root_phase = 0; 2226 free_v4rootexp(); 2227 return (0); 2228 } 2229 2230 /* 2231 * Compare old and current exportlist entries for the fsid and return 0 2232 * if they are the same, 1 otherwise. 2233 */ 2234 static int 2235 compare_export(struct exportlist *ep, struct exportlist *oep) 2236 { 2237 struct grouplist *grp, *ogrp; 2238 2239 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) 2240 return (1); 2241 if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) 2242 return (1); 2243 if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || 2244 (ep->ex_defdir == NULL && oep->ex_defdir != NULL)) 2245 return (1); 2246 if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != 2247 (oep->ex_defdir->dp_flag & DP_DEFSET)) 2248 return (1); 2249 if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != 2250 oep->ex_defnumsecflavors || ep->ex_defexflags != 2251 oep->ex_defexflags || compare_cred(&ep->ex_defanon, 2252 &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, 2253 oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) 2254 return (1); 2255 2256 /* Now, check all the groups. */ 2257 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2258 ogrp->gr_flag = 0; 2259 for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { 2260 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = 2261 ogrp->gr_next) 2262 if ((ogrp->gr_flag & GR_FND) == 0 && 2263 grp->gr_numsecflavors == ogrp->gr_numsecflavors && 2264 grp->gr_exflags == ogrp->gr_exflags && 2265 compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && 2266 compare_secflavor(grp->gr_secflavors, 2267 ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) 2268 break; 2269 if (ogrp != NULL) 2270 ogrp->gr_flag |= GR_FND; 2271 else 2272 return (1); 2273 } 2274 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) 2275 if ((ogrp->gr_flag & GR_FND) == 0) 2276 return (1); 2277 return (0); 2278 } 2279 2280 /* 2281 * This algorithm compares two arrays of "n" items. It returns 0 if they are 2282 * the "same" and 1 otherwise. Although suboptimal, it is always safe to 2283 * return 1, which makes compare_nmount_export() reload the exports entry. 2284 * "same" refers to having the same set of values in the two arrays. 2285 * The arrays are in no particular order and duplicates (multiple entries 2286 * in an array with the same value) is allowed. 2287 * The algorithm is inefficient, but the common case of indentical arrays is 2288 * handled first and "n" is normally fairly small. 2289 * Since the two functions need the same algorithm but for arrays of 2290 * different types (gid_t vs int), this is done as a macro. 2291 */ 2292 #define COMPARE_ARRAYS(a1, a2, n) \ 2293 do { \ 2294 int fnd, fndarray[(n)], i, j; \ 2295 /* Handle common case of identical arrays. */ \ 2296 for (i = 0; i < (n); i++) \ 2297 if ((a1)[i] != (a2)[i]) \ 2298 break; \ 2299 if (i == (n)) \ 2300 return (0); \ 2301 for (i = 0; i < (n); i++) \ 2302 fndarray[i] = 0; \ 2303 for (i = 0; i < (n); i++) { \ 2304 fnd = 0; \ 2305 for (j = 0; j < (n); j++) { \ 2306 if ((a1)[i] == (a2)[j]) { \ 2307 fndarray[j] = 1; \ 2308 fnd = 1; \ 2309 } \ 2310 } \ 2311 if (fnd == 0) \ 2312 return (1); \ 2313 } \ 2314 for (i = 0; i < (n); i++) \ 2315 if (fndarray[i] == 0) \ 2316 return (1); \ 2317 return (0); \ 2318 } while (0) 2319 2320 /* 2321 * Compare two struct expcred's. Return 0 if the same and 1 otherwise. 2322 */ 2323 static int 2324 compare_cred(struct expcred *cr0, struct expcred *cr1) 2325 { 2326 2327 if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) 2328 return (1); 2329 2330 COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups); 2331 } 2332 2333 /* 2334 * Compare two lists of security flavors. Return 0 if the same and 1 otherwise. 2335 */ 2336 static int 2337 compare_secflavor(int *sec1, int *sec2, int nsec) 2338 { 2339 2340 COMPARE_ARRAYS(sec1, sec2, nsec); 2341 } 2342 2343 /* 2344 * Delete an exports entry. 2345 */ 2346 static void 2347 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) 2348 { 2349 struct xvfsconf vfc; 2350 2351 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 2352 syslog(LOG_ERR, "getvfsbyname() failed for %s", 2353 fsp->f_fstypename); 2354 return; 2355 } 2356 2357 /* 2358 * We do not need to delete "export" flag from 2359 * filesystems that do not have it set. 2360 */ 2361 if (!(fsp->f_flags & MNT_EXPORTED)) 2362 return; 2363 /* 2364 * Do not delete export for network filesystem by 2365 * passing "export" arg to nmount(). 2366 * It only makes sense to do this for local filesystems. 2367 */ 2368 if (vfc.vfc_flags & VFCF_NETWORK) 2369 return; 2370 2371 iov[1].iov_base = fsp->f_fstypename; 2372 iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 2373 iov[3].iov_base = fsp->f_mntonname; 2374 iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 2375 iov[5].iov_base = fsp->f_mntfromname; 2376 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 2377 errmsg[0] = '\0'; 2378 2379 /* 2380 * EXDEV is returned when path exists but is not a 2381 * mount point. May happens if raced with unmount. 2382 */ 2383 if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && 2384 errno != ENOTSUP && errno != EXDEV) { 2385 syslog(LOG_ERR, 2386 "can't delete exports for %s: %m %s", 2387 fsp->f_mntonname, errmsg); 2388 } 2389 } 2390 2391 /* 2392 * Allocate an export list element 2393 */ 2394 static struct exportlist * 2395 get_exp(void) 2396 { 2397 struct exportlist *ep; 2398 2399 ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); 2400 if (ep == (struct exportlist *)NULL) 2401 out_of_mem(); 2402 return (ep); 2403 } 2404 2405 /* 2406 * Allocate a group list element 2407 */ 2408 static struct grouplist * 2409 get_grp(void) 2410 { 2411 struct grouplist *gp; 2412 2413 gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); 2414 if (gp == (struct grouplist *)NULL) 2415 out_of_mem(); 2416 return (gp); 2417 } 2418 2419 /* 2420 * Clean up upon an error in get_exportlist(). 2421 */ 2422 static void 2423 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) 2424 { 2425 struct grouplist *tgrp; 2426 2427 if (!(opt_flags & OP_QUIET)) { 2428 if (reason != NULL) 2429 syslog(LOG_ERR, "bad exports list line '%s': %s", line, 2430 reason); 2431 else 2432 syslog(LOG_ERR, "bad exports list line '%s'", line); 2433 } 2434 if (ep && (ep->ex_flag & EX_LINKED) == 0) 2435 free_exp(ep); 2436 while (grp) { 2437 tgrp = grp; 2438 grp = grp->gr_next; 2439 free_grp(tgrp); 2440 } 2441 } 2442 2443 /* 2444 * Search the export list for a matching fs. 2445 */ 2446 static struct exportlist * 2447 ex_search(fsid_t *fsid, struct exportlisthead *exhp) 2448 { 2449 struct exportlist *ep; 2450 uint32_t i; 2451 2452 i = EXPHASH(fsid); 2453 SLIST_FOREACH(ep, &exhp[i], entries) { 2454 if (fsidcmp(&ep->ex_fs, fsid) == 0) 2455 return (ep); 2456 } 2457 2458 return (ep); 2459 } 2460 2461 /* 2462 * Add a directory path to the list. 2463 */ 2464 static char * 2465 add_expdir(struct dirlist **dpp, char *cp, int len) 2466 { 2467 struct dirlist *dp; 2468 2469 dp = malloc(sizeof (struct dirlist)); 2470 if (dp == (struct dirlist *)NULL) 2471 out_of_mem(); 2472 dp->dp_left = *dpp; 2473 dp->dp_right = (struct dirlist *)NULL; 2474 dp->dp_flag = 0; 2475 dp->dp_hosts = (struct hostlist *)NULL; 2476 dp->dp_dirp = strndup(cp, len); 2477 if (dp->dp_dirp == NULL) 2478 out_of_mem(); 2479 *dpp = dp; 2480 return (dp->dp_dirp); 2481 } 2482 2483 /* 2484 * Hang the dir list element off the dirpath binary tree as required 2485 * and update the entry for host. 2486 */ 2487 static void 2488 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 2489 int flags, struct expcred *anoncrp, uint64_t exflags) 2490 { 2491 struct hostlist *hp; 2492 struct dirlist *dp2; 2493 2494 if (flags & OP_ALLDIRS) { 2495 if (ep->ex_defdir) 2496 free((caddr_t)dp); 2497 else 2498 ep->ex_defdir = dp; 2499 if (grp == (struct grouplist *)NULL) { 2500 ep->ex_flag |= EX_DEFSET; 2501 ep->ex_defdir->dp_flag |= DP_DEFSET; 2502 /* Save the default security flavors list. */ 2503 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2504 if (ep->ex_numsecflavors > 0) 2505 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2506 sizeof(ep->ex_secflavors)); 2507 cp_cred(&ep->ex_defanon, anoncrp); 2508 ep->ex_defexflags = exflags; 2509 } else while (grp) { 2510 hp = get_ht(); 2511 hp->ht_grp = grp; 2512 hp->ht_next = ep->ex_defdir->dp_hosts; 2513 ep->ex_defdir->dp_hosts = hp; 2514 /* Save the security flavors list for this host set. */ 2515 grp->gr_numsecflavors = ep->ex_numsecflavors; 2516 if (ep->ex_numsecflavors > 0) 2517 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2518 sizeof(ep->ex_secflavors)); 2519 grp = grp->gr_next; 2520 } 2521 } else { 2522 2523 /* 2524 * Loop through the directories adding them to the tree. 2525 */ 2526 while (dp) { 2527 dp2 = dp->dp_left; 2528 add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, 2529 exflags); 2530 dp = dp2; 2531 } 2532 } 2533 } 2534 2535 /* 2536 * Traverse the binary tree either updating a node that is already there 2537 * for the new directory or adding the new node. 2538 */ 2539 static void 2540 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 2541 int flags, struct exportlist *ep, struct expcred *anoncrp, 2542 uint64_t exflags) 2543 { 2544 struct dirlist *dp; 2545 struct hostlist *hp; 2546 int cmp; 2547 2548 dp = *dpp; 2549 if (dp) { 2550 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 2551 if (cmp > 0) { 2552 add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, 2553 exflags); 2554 return; 2555 } else if (cmp < 0) { 2556 add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, 2557 exflags); 2558 return; 2559 } else 2560 free((caddr_t)newdp); 2561 } else { 2562 dp = newdp; 2563 dp->dp_left = (struct dirlist *)NULL; 2564 *dpp = dp; 2565 } 2566 if (grp) { 2567 2568 /* 2569 * Hang all of the host(s) off of the directory point. 2570 */ 2571 do { 2572 hp = get_ht(); 2573 hp->ht_grp = grp; 2574 hp->ht_next = dp->dp_hosts; 2575 dp->dp_hosts = hp; 2576 /* Save the security flavors list for this host set. */ 2577 grp->gr_numsecflavors = ep->ex_numsecflavors; 2578 if (ep->ex_numsecflavors > 0) 2579 memcpy(grp->gr_secflavors, ep->ex_secflavors, 2580 sizeof(ep->ex_secflavors)); 2581 grp = grp->gr_next; 2582 } while (grp); 2583 } else { 2584 ep->ex_flag |= EX_DEFSET; 2585 dp->dp_flag |= DP_DEFSET; 2586 /* Save the default security flavors list. */ 2587 ep->ex_defnumsecflavors = ep->ex_numsecflavors; 2588 if (ep->ex_numsecflavors > 0) 2589 memcpy(ep->ex_defsecflavors, ep->ex_secflavors, 2590 sizeof(ep->ex_secflavors)); 2591 cp_cred(&ep->ex_defanon, anoncrp); 2592 ep->ex_defexflags = exflags; 2593 } 2594 } 2595 2596 /* 2597 * Search for a dirpath on the export point. 2598 */ 2599 static struct dirlist * 2600 dirp_search(struct dirlist *dp, char *dirp) 2601 { 2602 int cmp; 2603 2604 if (dp) { 2605 cmp = strcmp(dp->dp_dirp, dirp); 2606 if (cmp > 0) 2607 return (dirp_search(dp->dp_left, dirp)); 2608 else if (cmp < 0) 2609 return (dirp_search(dp->dp_right, dirp)); 2610 else 2611 return (dp); 2612 } 2613 return (dp); 2614 } 2615 2616 /* 2617 * Scan for a host match in a directory tree. 2618 */ 2619 static int 2620 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 2621 int *hostsetp, int *numsecflavors, int **secflavorsp) 2622 { 2623 struct hostlist *hp; 2624 struct grouplist *grp; 2625 struct addrinfo *ai; 2626 2627 if (dp) { 2628 if (dp->dp_flag & DP_DEFSET) 2629 *defsetp = dp->dp_flag; 2630 hp = dp->dp_hosts; 2631 while (hp) { 2632 grp = hp->ht_grp; 2633 switch (grp->gr_type) { 2634 case GT_HOST: 2635 ai = grp->gr_ptr.gt_addrinfo; 2636 for (; ai; ai = ai->ai_next) { 2637 if (!sacmp(ai->ai_addr, saddr, NULL)) { 2638 *hostsetp = 2639 (hp->ht_flag | DP_HOSTSET); 2640 if (numsecflavors != NULL) { 2641 *numsecflavors = 2642 grp->gr_numsecflavors; 2643 *secflavorsp = 2644 grp->gr_secflavors; 2645 } 2646 return (1); 2647 } 2648 } 2649 break; 2650 case GT_NET: 2651 if (!sacmp(saddr, (struct sockaddr *) 2652 &grp->gr_ptr.gt_net.nt_net, 2653 (struct sockaddr *) 2654 &grp->gr_ptr.gt_net.nt_mask)) { 2655 *hostsetp = (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 break; 2665 } 2666 hp = hp->ht_next; 2667 } 2668 } 2669 return (0); 2670 } 2671 2672 /* 2673 * Scan tree for a host that matches the address. 2674 */ 2675 static int 2676 scan_tree(struct dirlist *dp, struct sockaddr *saddr) 2677 { 2678 int defset, hostset; 2679 2680 if (dp) { 2681 if (scan_tree(dp->dp_left, saddr)) 2682 return (1); 2683 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) 2684 return (1); 2685 if (scan_tree(dp->dp_right, saddr)) 2686 return (1); 2687 } 2688 return (0); 2689 } 2690 2691 /* 2692 * Traverse the dirlist tree and free it up. 2693 */ 2694 static void 2695 free_dir(struct dirlist *dp) 2696 { 2697 2698 if (dp) { 2699 free_dir(dp->dp_left); 2700 free_dir(dp->dp_right); 2701 free_host(dp->dp_hosts); 2702 free(dp->dp_dirp); 2703 free(dp); 2704 } 2705 } 2706 2707 /* 2708 * Parse a colon separated list of security flavors 2709 */ 2710 static int 2711 parsesec(char *seclist, struct exportlist *ep) 2712 { 2713 char *cp, savedc; 2714 int flavor; 2715 2716 ep->ex_numsecflavors = 0; 2717 for (;;) { 2718 cp = strchr(seclist, ':'); 2719 if (cp) { 2720 savedc = *cp; 2721 *cp = '\0'; 2722 } 2723 2724 if (!strcmp(seclist, "sys")) 2725 flavor = AUTH_SYS; 2726 else if (!strcmp(seclist, "krb5")) 2727 flavor = RPCSEC_GSS_KRB5; 2728 else if (!strcmp(seclist, "krb5i")) 2729 flavor = RPCSEC_GSS_KRB5I; 2730 else if (!strcmp(seclist, "krb5p")) 2731 flavor = RPCSEC_GSS_KRB5P; 2732 else { 2733 if (cp) 2734 *cp = savedc; 2735 syslog(LOG_ERR, "bad sec flavor: %s", seclist); 2736 return (1); 2737 } 2738 if (ep->ex_numsecflavors == MAXSECFLAVORS) { 2739 if (cp) 2740 *cp = savedc; 2741 syslog(LOG_ERR, "too many sec flavors: %s", seclist); 2742 return (1); 2743 } 2744 ep->ex_secflavors[ep->ex_numsecflavors] = flavor; 2745 ep->ex_numsecflavors++; 2746 if (cp) { 2747 *cp = savedc; 2748 seclist = cp + 1; 2749 } else { 2750 break; 2751 } 2752 } 2753 return (0); 2754 } 2755 2756 /* 2757 * Parse the option string and update fields. 2758 * Option arguments may either be -<option>=<value> or 2759 * -<option> <value> 2760 */ 2761 static int 2762 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 2763 int *has_hostp, uint64_t *exflagsp, struct expcred *cr) 2764 { 2765 char *cpoptarg, *cpoptend; 2766 char *cp, *endcp, *cpopt, savedc, savedc2; 2767 int allflag, usedarg; 2768 2769 savedc2 = '\0'; 2770 cpopt = *cpp; 2771 cpopt++; 2772 cp = *endcpp; 2773 savedc = *cp; 2774 *cp = '\0'; 2775 while (cpopt && *cpopt) { 2776 allflag = 1; 2777 usedarg = -2; 2778 if ((cpoptend = strchr(cpopt, ','))) { 2779 *cpoptend++ = '\0'; 2780 if ((cpoptarg = strchr(cpopt, '='))) 2781 *cpoptarg++ = '\0'; 2782 } else { 2783 if ((cpoptarg = strchr(cpopt, '='))) 2784 *cpoptarg++ = '\0'; 2785 else { 2786 *cp = savedc; 2787 nextfield(&cp, &endcp); 2788 **endcpp = '\0'; 2789 if (endcp > cp && *cp != '-') { 2790 cpoptarg = cp; 2791 savedc2 = *endcp; 2792 *endcp = '\0'; 2793 usedarg = 0; 2794 } 2795 } 2796 } 2797 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 2798 *exflagsp |= MNT_EXRDONLY; 2799 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 2800 !(allflag = strcmp(cpopt, "mapall")) || 2801 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 2802 usedarg++; 2803 parsecred(cpoptarg, cr); 2804 if (allflag == 0) { 2805 *exflagsp |= MNT_EXPORTANON; 2806 opt_flags |= OP_MAPALL; 2807 } else 2808 opt_flags |= OP_MAPROOT; 2809 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 2810 !strcmp(cpopt, "m"))) { 2811 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 2812 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 2813 return (1); 2814 } 2815 usedarg++; 2816 opt_flags |= OP_MASK; 2817 } else if (cpoptarg && (!strcmp(cpopt, "network") || 2818 !strcmp(cpopt, "n"))) { 2819 if (strchr(cpoptarg, '/') != NULL) { 2820 if (debug) 2821 fprintf(stderr, "setting OP_MASKLEN\n"); 2822 opt_flags |= OP_MASKLEN; 2823 } 2824 if (grp->gr_type != GT_NULL) { 2825 syslog(LOG_ERR, "network/host conflict"); 2826 return (1); 2827 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 2828 syslog(LOG_ERR, "bad net: %s", cpoptarg); 2829 return (1); 2830 } 2831 grp->gr_type = GT_NET; 2832 *has_hostp = 1; 2833 usedarg++; 2834 opt_flags |= OP_NET; 2835 } else if (!strcmp(cpopt, "alldirs")) { 2836 opt_flags |= OP_ALLDIRS; 2837 } else if (!strcmp(cpopt, "public")) { 2838 *exflagsp |= MNT_EXPUBLIC; 2839 } else if (!strcmp(cpopt, "webnfs")) { 2840 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 2841 opt_flags |= OP_MAPALL; 2842 } else if (cpoptarg && !strcmp(cpopt, "index")) { 2843 ep->ex_indexfile = strdup(cpoptarg); 2844 } else if (!strcmp(cpopt, "quiet")) { 2845 opt_flags |= OP_QUIET; 2846 } else if (cpoptarg && !strcmp(cpopt, "sec")) { 2847 if (parsesec(cpoptarg, ep)) 2848 return (1); 2849 opt_flags |= OP_SEC; 2850 usedarg++; 2851 } else if (!strcmp(cpopt, "tls")) { 2852 *exflagsp |= MNT_EXTLS; 2853 } else if (!strcmp(cpopt, "tlscert")) { 2854 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT); 2855 } else if (!strcmp(cpopt, "tlscertuser")) { 2856 *exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT | 2857 MNT_EXTLSCERTUSER); 2858 } else { 2859 syslog(LOG_ERR, "bad opt %s", cpopt); 2860 return (1); 2861 } 2862 if (usedarg >= 0) { 2863 *endcp = savedc2; 2864 **endcpp = savedc; 2865 if (usedarg > 0) { 2866 *cpp = cp; 2867 *endcpp = endcp; 2868 } 2869 return (0); 2870 } 2871 cpopt = cpoptend; 2872 } 2873 **endcpp = savedc; 2874 return (0); 2875 } 2876 2877 /* 2878 * Translate a character string to the corresponding list of network 2879 * addresses for a hostname. 2880 */ 2881 static int 2882 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 2883 { 2884 struct grouplist *checkgrp; 2885 struct addrinfo *ai, *tai, hints; 2886 int ecode; 2887 char host[NI_MAXHOST]; 2888 2889 if (grp->gr_type != GT_NULL) { 2890 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 2891 return (1); 2892 } 2893 memset(&hints, 0, sizeof hints); 2894 hints.ai_flags = AI_CANONNAME; 2895 hints.ai_protocol = IPPROTO_UDP; 2896 ecode = getaddrinfo(cp, NULL, &hints, &ai); 2897 if (ecode != 0) { 2898 syslog(LOG_ERR,"can't get address info for host %s", cp); 2899 return 1; 2900 } 2901 grp->gr_ptr.gt_addrinfo = ai; 2902 while (ai != NULL) { 2903 if (ai->ai_canonname == NULL) { 2904 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 2905 sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 2906 strlcpy(host, "?", sizeof(host)); 2907 ai->ai_canonname = strdup(host); 2908 ai->ai_flags |= AI_CANONNAME; 2909 } 2910 if (debug) 2911 fprintf(stderr, "got host %s\n", ai->ai_canonname); 2912 /* 2913 * Sanity check: make sure we don't already have an entry 2914 * for this host in the grouplist. 2915 */ 2916 for (checkgrp = tgrp; checkgrp != NULL; 2917 checkgrp = checkgrp->gr_next) { 2918 if (checkgrp->gr_type != GT_HOST) 2919 continue; 2920 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 2921 tai = tai->ai_next) { 2922 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 2923 continue; 2924 if (debug) 2925 fprintf(stderr, 2926 "ignoring duplicate host %s\n", 2927 ai->ai_canonname); 2928 grp->gr_type = GT_IGNORE; 2929 return (0); 2930 } 2931 } 2932 ai = ai->ai_next; 2933 } 2934 grp->gr_type = GT_HOST; 2935 return (0); 2936 } 2937 2938 /* 2939 * Free up an exports list component 2940 */ 2941 static void 2942 free_exp(struct exportlist *ep) 2943 { 2944 struct grouplist *grp, *tgrp; 2945 2946 if (ep->ex_defdir) { 2947 free_host(ep->ex_defdir->dp_hosts); 2948 free((caddr_t)ep->ex_defdir); 2949 } 2950 if (ep->ex_fsdir) 2951 free(ep->ex_fsdir); 2952 if (ep->ex_indexfile) 2953 free(ep->ex_indexfile); 2954 free_dir(ep->ex_dirl); 2955 grp = ep->ex_grphead; 2956 while (grp) { 2957 tgrp = grp; 2958 grp = grp->gr_next; 2959 free_grp(tgrp); 2960 } 2961 if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps) 2962 free(ep->ex_defanon.cr_groups); 2963 free((caddr_t)ep); 2964 } 2965 2966 /* 2967 * Free up the v4root exports. 2968 */ 2969 static void 2970 free_v4rootexp(void) 2971 { 2972 2973 if (v4root_ep != NULL) { 2974 free_exp(v4root_ep); 2975 v4root_ep = NULL; 2976 } 2977 } 2978 2979 /* 2980 * Free hosts. 2981 */ 2982 static void 2983 free_host(struct hostlist *hp) 2984 { 2985 struct hostlist *hp2; 2986 2987 while (hp) { 2988 hp2 = hp; 2989 hp = hp->ht_next; 2990 free((caddr_t)hp2); 2991 } 2992 } 2993 2994 static struct hostlist * 2995 get_ht(void) 2996 { 2997 struct hostlist *hp; 2998 2999 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 3000 if (hp == (struct hostlist *)NULL) 3001 out_of_mem(); 3002 hp->ht_next = (struct hostlist *)NULL; 3003 hp->ht_flag = 0; 3004 return (hp); 3005 } 3006 3007 /* 3008 * Out of memory, fatal 3009 */ 3010 static void 3011 out_of_mem(void) 3012 { 3013 3014 syslog(LOG_ERR, "out of memory"); 3015 exit(2); 3016 } 3017 3018 /* 3019 * Call do_mount() from the struct exportlist, for each case needed. 3020 */ 3021 static int 3022 do_export_mount(struct exportlist *ep, struct statfs *fsp) 3023 { 3024 struct grouplist *grp, defgrp; 3025 int ret; 3026 size_t dirlen; 3027 3028 LOGDEBUG("do_export_mount=%s", ep->ex_fsdir); 3029 dirlen = strlen(ep->ex_fsdir); 3030 if ((ep->ex_flag & EX_DEFSET) != 0) { 3031 defgrp.gr_type = GT_DEFAULT; 3032 defgrp.gr_next = NULL; 3033 /* We have an entry for all other hosts/nets. */ 3034 LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags); 3035 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon, 3036 ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors, 3037 ep->ex_defsecflavors); 3038 if (ret != 0) 3039 return (ret); 3040 } 3041 3042 /* Do a mount for each group. */ 3043 grp = ep->ex_grphead; 3044 while (grp != NULL) { 3045 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx", 3046 grp->gr_type, (uintmax_t)grp->gr_exflags); 3047 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon, 3048 ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors, 3049 grp->gr_secflavors); 3050 if (ret != 0) 3051 return (ret); 3052 grp = grp->gr_next; 3053 } 3054 return (0); 3055 } 3056 3057 /* 3058 * Do the nmount() syscall with the update flag to push the export info into 3059 * the kernel. 3060 */ 3061 static int 3062 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags, 3063 struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb, 3064 int numsecflavors, int *secflavors) 3065 { 3066 struct statfs fsb1; 3067 struct addrinfo *ai; 3068 struct export_args *eap; 3069 char errmsg[255]; 3070 char *cp; 3071 int done; 3072 char savedc; 3073 struct iovec *iov; 3074 int i, iovlen; 3075 int ret; 3076 struct nfsex_args nfsea; 3077 3078 eap = &nfsea.export; 3079 3080 cp = NULL; 3081 savedc = '\0'; 3082 iov = NULL; 3083 iovlen = 0; 3084 ret = 0; 3085 3086 bzero(eap, sizeof (struct export_args)); 3087 bzero(errmsg, sizeof(errmsg)); 3088 eap->ex_flags = exflags; 3089 eap->ex_uid = anoncrp->cr_uid; 3090 eap->ex_ngroups = anoncrp->cr_ngroups; 3091 if (eap->ex_ngroups > 0) { 3092 eap->ex_groups = malloc(eap->ex_ngroups * sizeof(gid_t)); 3093 memcpy(eap->ex_groups, anoncrp->cr_groups, eap->ex_ngroups * 3094 sizeof(gid_t)); 3095 } 3096 LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags); 3097 eap->ex_indexfile = ep->ex_indexfile; 3098 if (grp->gr_type == GT_HOST) 3099 ai = grp->gr_ptr.gt_addrinfo; 3100 else 3101 ai = NULL; 3102 eap->ex_numsecflavors = numsecflavors; 3103 LOGDEBUG("do_mount numsec=%d", numsecflavors); 3104 for (i = 0; i < eap->ex_numsecflavors; i++) 3105 eap->ex_secflavors[i] = secflavors[i]; 3106 if (eap->ex_numsecflavors == 0) { 3107 eap->ex_numsecflavors = 1; 3108 eap->ex_secflavors[0] = AUTH_SYS; 3109 } 3110 done = FALSE; 3111 3112 if (v4root_phase == 0) { 3113 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 3114 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 3115 build_iovec(&iov, &iovlen, "from", NULL, 0); 3116 build_iovec(&iov, &iovlen, "update", NULL, 0); 3117 build_iovec(&iov, &iovlen, "export", eap, 3118 sizeof (struct export_args)); 3119 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 3120 } 3121 3122 while (!done) { 3123 switch (grp->gr_type) { 3124 case GT_HOST: 3125 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 3126 goto skip; 3127 eap->ex_addr = ai->ai_addr; 3128 eap->ex_addrlen = ai->ai_addrlen; 3129 eap->ex_masklen = 0; 3130 break; 3131 case GT_NET: 3132 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 3133 have_v6 == 0) 3134 goto skip; 3135 eap->ex_addr = 3136 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 3137 eap->ex_addrlen = 3138 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 3139 eap->ex_mask = 3140 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 3141 eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 3142 break; 3143 case GT_DEFAULT: 3144 eap->ex_addr = NULL; 3145 eap->ex_addrlen = 0; 3146 eap->ex_mask = NULL; 3147 eap->ex_masklen = 0; 3148 break; 3149 case GT_IGNORE: 3150 ret = 0; 3151 goto error_exit; 3152 break; 3153 default: 3154 syslog(LOG_ERR, "bad grouptype"); 3155 if (cp) 3156 *cp = savedc; 3157 ret = 1; 3158 goto error_exit; 3159 } 3160 3161 /* 3162 * For V4:, use the nfssvc() syscall, instead of mount(). 3163 */ 3164 if (v4root_phase == 2) { 3165 nfsea.fspec = v4root_dirpath; 3166 if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, 3167 (caddr_t)&nfsea) < 0) { 3168 syslog(LOG_ERR, "Exporting V4: failed"); 3169 ret = 2; 3170 goto error_exit; 3171 } 3172 } else { 3173 /* 3174 * XXX: 3175 * Maybe I should just use the fsb->f_mntonname path 3176 * instead of looping back up the dirp to the mount 3177 * point?? 3178 * Also, needs to know how to export all types of local 3179 * exportable filesystems and not just "ufs". 3180 */ 3181 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 3182 iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 3183 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 3184 iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 3185 iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 3186 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 3187 errmsg[0] = '\0'; 3188 3189 while (nmount(iov, iovlen, fsb->f_flags) < 0) { 3190 if (cp) 3191 *cp-- = savedc; 3192 else 3193 cp = dirp + dirplen - 1; 3194 if (opt_flags & OP_QUIET) { 3195 ret = 1; 3196 goto error_exit; 3197 } 3198 if (errno == EPERM) { 3199 if (debug) 3200 warnx("can't change attributes for %s: %s", 3201 dirp, errmsg); 3202 syslog(LOG_ERR, 3203 "can't change attributes for %s: %s", 3204 dirp, errmsg); 3205 ret = 1; 3206 goto error_exit; 3207 } 3208 if (opt_flags & OP_ALLDIRS) { 3209 if (errno == EINVAL) 3210 syslog(LOG_ERR, 3211 "-alldirs requested but %s is not a filesystem mountpoint", 3212 dirp); 3213 else 3214 syslog(LOG_ERR, 3215 "could not remount %s: %m", 3216 dirp); 3217 ret = 1; 3218 goto error_exit; 3219 } 3220 /* back up over the last component */ 3221 while (cp > dirp && *cp == '/') 3222 cp--; 3223 while (cp > dirp && *(cp - 1) != '/') 3224 cp--; 3225 if (cp == dirp) { 3226 if (debug) 3227 warnx("mnt unsucc"); 3228 syslog(LOG_ERR, "can't export %s %s", 3229 dirp, errmsg); 3230 ret = 1; 3231 goto error_exit; 3232 } 3233 savedc = *cp; 3234 *cp = '\0'; 3235 /* 3236 * Check that we're still on the same 3237 * filesystem. 3238 */ 3239 if (statfs(dirp, &fsb1) != 0 || 3240 fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) { 3241 *cp = savedc; 3242 syslog(LOG_ERR, 3243 "can't export %s %s", dirp, 3244 errmsg); 3245 ret = 1; 3246 goto error_exit; 3247 } 3248 } 3249 } 3250 3251 /* 3252 * For the experimental server: 3253 * If this is the public directory, get the file handle 3254 * and load it into the kernel via the nfssvc() syscall. 3255 */ 3256 if ((exflags & MNT_EXPUBLIC) != 0) { 3257 fhandle_t fh; 3258 char *public_name; 3259 3260 if (eap->ex_indexfile != NULL) 3261 public_name = eap->ex_indexfile; 3262 else 3263 public_name = dirp; 3264 if (getfh(public_name, &fh) < 0) 3265 syslog(LOG_ERR, 3266 "Can't get public fh for %s", public_name); 3267 else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) 3268 syslog(LOG_ERR, 3269 "Can't set public fh for %s", public_name); 3270 else { 3271 has_publicfh = 1; 3272 has_set_publicfh = 1; 3273 ep->ex_flag |= EX_PUBLICFH; 3274 } 3275 } 3276 skip: 3277 if (ai != NULL) 3278 ai = ai->ai_next; 3279 if (ai == NULL) 3280 done = TRUE; 3281 } 3282 if (cp) 3283 *cp = savedc; 3284 error_exit: 3285 free(eap->ex_groups); 3286 /* free strings allocated by strdup() in getmntopts.c */ 3287 if (iov != NULL) { 3288 free(iov[0].iov_base); /* fstype */ 3289 free(iov[2].iov_base); /* fspath */ 3290 free(iov[4].iov_base); /* from */ 3291 free(iov[6].iov_base); /* update */ 3292 free(iov[8].iov_base); /* export */ 3293 free(iov[10].iov_base); /* errmsg */ 3294 3295 /* free iov, allocated by realloc() */ 3296 free(iov); 3297 } 3298 return (ret); 3299 } 3300 3301 /* 3302 * Translate a net address. 3303 * 3304 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 3305 */ 3306 static int 3307 get_net(char *cp, struct netmsk *net, int maskflg) 3308 { 3309 struct netent *np = NULL; 3310 char *name, *p, *prefp; 3311 struct sockaddr_in sin; 3312 struct sockaddr *sa = NULL; 3313 struct addrinfo hints, *ai = NULL; 3314 char netname[NI_MAXHOST]; 3315 long preflen; 3316 3317 p = prefp = NULL; 3318 if ((opt_flags & OP_MASKLEN) && !maskflg) { 3319 p = strchr(cp, '/'); 3320 *p = '\0'; 3321 prefp = p + 1; 3322 } 3323 3324 /* 3325 * Check for a numeric address first. We wish to avoid 3326 * possible DNS lookups in getnetbyname(). 3327 */ 3328 if (isxdigit(*cp) || *cp == ':') { 3329 memset(&hints, 0, sizeof hints); 3330 /* Ensure the mask and the network have the same family. */ 3331 if (maskflg && (opt_flags & OP_NET)) 3332 hints.ai_family = net->nt_net.ss_family; 3333 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 3334 hints.ai_family = net->nt_mask.ss_family; 3335 else 3336 hints.ai_family = AF_UNSPEC; 3337 hints.ai_flags = AI_NUMERICHOST; 3338 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 3339 sa = ai->ai_addr; 3340 if (sa != NULL && ai->ai_family == AF_INET) { 3341 /* 3342 * The address in `cp' is really a network address, so 3343 * use inet_network() to re-interpret this correctly. 3344 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 3345 */ 3346 bzero(&sin, sizeof sin); 3347 sin.sin_family = AF_INET; 3348 sin.sin_len = sizeof sin; 3349 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 3350 if (debug) 3351 fprintf(stderr, "get_net: v4 addr %s\n", 3352 inet_ntoa(sin.sin_addr)); 3353 sa = (struct sockaddr *)&sin; 3354 } 3355 } 3356 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 3357 bzero(&sin, sizeof sin); 3358 sin.sin_family = AF_INET; 3359 sin.sin_len = sizeof sin; 3360 sin.sin_addr = inet_makeaddr(np->n_net, 0); 3361 sa = (struct sockaddr *)&sin; 3362 } 3363 if (sa == NULL) 3364 goto fail; 3365 3366 if (maskflg) { 3367 /* The specified sockaddr is a mask. */ 3368 if (checkmask(sa) != 0) 3369 goto fail; 3370 bcopy(sa, &net->nt_mask, sa->sa_len); 3371 opt_flags |= OP_HAVEMASK; 3372 } else { 3373 /* The specified sockaddr is a network address. */ 3374 bcopy(sa, &net->nt_net, sa->sa_len); 3375 3376 /* Get a network name for the export list. */ 3377 if (np) { 3378 name = np->n_name; 3379 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 3380 NULL, 0, NI_NUMERICHOST) == 0) { 3381 name = netname; 3382 } else { 3383 goto fail; 3384 } 3385 if ((net->nt_name = strdup(name)) == NULL) 3386 out_of_mem(); 3387 3388 /* 3389 * Extract a mask from either a "/<masklen>" suffix, or 3390 * from the class of an IPv4 address. 3391 */ 3392 if (opt_flags & OP_MASKLEN) { 3393 preflen = strtol(prefp, NULL, 10); 3394 if (preflen < 0L || preflen == LONG_MAX) 3395 goto fail; 3396 bcopy(sa, &net->nt_mask, sa->sa_len); 3397 if (makemask(&net->nt_mask, (int)preflen) != 0) 3398 goto fail; 3399 opt_flags |= OP_HAVEMASK; 3400 *p = '/'; 3401 } else if (sa->sa_family == AF_INET && 3402 (opt_flags & OP_MASK) == 0) { 3403 in_addr_t addr; 3404 3405 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 3406 if (IN_CLASSA(addr)) 3407 preflen = 8; 3408 else if (IN_CLASSB(addr)) 3409 preflen = 16; 3410 else if (IN_CLASSC(addr)) 3411 preflen = 24; 3412 else if (IN_CLASSD(addr)) 3413 preflen = 28; 3414 else 3415 preflen = 32; /* XXX */ 3416 3417 bcopy(sa, &net->nt_mask, sa->sa_len); 3418 makemask(&net->nt_mask, (int)preflen); 3419 opt_flags |= OP_HAVEMASK; 3420 } 3421 } 3422 3423 if (ai) 3424 freeaddrinfo(ai); 3425 return 0; 3426 3427 fail: 3428 if (ai) 3429 freeaddrinfo(ai); 3430 return 1; 3431 } 3432 3433 /* 3434 * Parse out the next white space separated field 3435 */ 3436 static void 3437 nextfield(char **cp, char **endcp) 3438 { 3439 char *p; 3440 char quot = 0; 3441 3442 p = *cp; 3443 while (*p == ' ' || *p == '\t') 3444 p++; 3445 *cp = p; 3446 while (*p != '\0') { 3447 if (quot) { 3448 if (*p == quot) 3449 quot = 0; 3450 } else { 3451 if (*p == '\\' && *(p + 1) != '\0') 3452 p++; 3453 else if (*p == '\'' || *p == '"') 3454 quot = *p; 3455 else if (*p == ' ' || *p == '\t') 3456 break; 3457 } 3458 p++; 3459 }; 3460 *endcp = p; 3461 } 3462 3463 /* 3464 * Get an exports file line. Skip over blank lines and handle line 3465 * continuations. 3466 */ 3467 static int 3468 get_line(void) 3469 { 3470 char *p, *cp; 3471 size_t len; 3472 int totlen, cont_line; 3473 3474 /* 3475 * Loop around ignoring blank lines and getting all continuation lines. 3476 */ 3477 p = line; 3478 totlen = 0; 3479 do { 3480 if ((p = fgetln(exp_file, &len)) == NULL) 3481 return (0); 3482 cp = p + len - 1; 3483 cont_line = 0; 3484 while (cp >= p && 3485 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 3486 if (*cp == '\\') 3487 cont_line = 1; 3488 cp--; 3489 len--; 3490 } 3491 if (cont_line) { 3492 *++cp = ' '; 3493 len++; 3494 } 3495 if (linesize < len + totlen + 1) { 3496 linesize = len + totlen + 1; 3497 line = realloc(line, linesize); 3498 if (line == NULL) 3499 out_of_mem(); 3500 } 3501 memcpy(line + totlen, p, len); 3502 totlen += len; 3503 line[totlen] = '\0'; 3504 } while (totlen == 0 || cont_line); 3505 return (1); 3506 } 3507 3508 /* 3509 * Parse a description of a credential. 3510 */ 3511 static void 3512 parsecred(char *namelist, struct expcred *cr) 3513 { 3514 char *name; 3515 int inpos; 3516 char *names; 3517 struct passwd *pw; 3518 struct group *gr; 3519 gid_t groups[NGROUPS_MAX + 1]; 3520 int ngroups; 3521 3522 /* 3523 * Set up the unprivileged user. 3524 */ 3525 cr->cr_groups = cr->cr_smallgrps; 3526 cr->cr_uid = UID_NOBODY; 3527 cr->cr_groups[0] = GID_NOGROUP; 3528 cr->cr_ngroups = 1; 3529 /* 3530 * Get the user's password table entry. 3531 */ 3532 names = namelist; 3533 name = strsep_quote(&names, ":"); 3534 /* Bug? name could be NULL here */ 3535 if (isdigit(*name) || *name == '-') 3536 pw = getpwuid(atoi(name)); 3537 else 3538 pw = getpwnam(name); 3539 /* 3540 * Credentials specified as those of a user. 3541 */ 3542 if (names == NULL) { 3543 if (pw == NULL) { 3544 syslog(LOG_ERR, "unknown user: %s", name); 3545 return; 3546 } 3547 cr->cr_uid = pw->pw_uid; 3548 ngroups = NGROUPS_MAX + 1; 3549 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) { 3550 syslog(LOG_ERR, "too many groups"); 3551 ngroups = NGROUPS_MAX + 1; 3552 } 3553 3554 /* 3555 * Compress out duplicate. 3556 */ 3557 if (ngroups > 1 && groups[0] == groups[1]) { 3558 ngroups--; 3559 inpos = 2; 3560 } else 3561 inpos = 1; 3562 if (ngroups > NGROUPS_MAX) 3563 ngroups = NGROUPS_MAX; 3564 if (ngroups > SMALLNGROUPS) 3565 cr->cr_groups = malloc(ngroups * sizeof(gid_t)); 3566 cr->cr_ngroups = ngroups; 3567 cr->cr_groups[0] = groups[0]; 3568 memcpy(&cr->cr_groups[1], &groups[inpos], (ngroups - 1) * 3569 sizeof(gid_t)); 3570 return; 3571 } 3572 /* 3573 * Explicit credential specified as a colon separated list: 3574 * uid:gid:gid:... 3575 */ 3576 if (pw != NULL) 3577 cr->cr_uid = pw->pw_uid; 3578 else if (isdigit(*name) || *name == '-') 3579 cr->cr_uid = atoi(name); 3580 else { 3581 syslog(LOG_ERR, "unknown user: %s", name); 3582 return; 3583 } 3584 cr->cr_ngroups = 0; 3585 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS_MAX) { 3586 name = strsep_quote(&names, ":"); 3587 if (isdigit(*name) || *name == '-') { 3588 groups[cr->cr_ngroups++] = atoi(name); 3589 } else { 3590 if ((gr = getgrnam(name)) == NULL) { 3591 syslog(LOG_ERR, "unknown group: %s", name); 3592 continue; 3593 } 3594 groups[cr->cr_ngroups++] = gr->gr_gid; 3595 } 3596 } 3597 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS_MAX) 3598 syslog(LOG_ERR, "too many groups"); 3599 if (cr->cr_ngroups > SMALLNGROUPS) 3600 cr->cr_groups = malloc(cr->cr_ngroups * sizeof(gid_t)); 3601 memcpy(cr->cr_groups, groups, cr->cr_ngroups * sizeof(gid_t)); 3602 } 3603 3604 #define STRSIZ (MNTNAMLEN+MNTPATHLEN+50) 3605 /* 3606 * Routines that maintain the remote mounttab 3607 */ 3608 static void 3609 get_mountlist(void) 3610 { 3611 struct mountlist *mlp; 3612 char *host, *dirp, *cp; 3613 char str[STRSIZ]; 3614 FILE *mlfile; 3615 3616 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 3617 if (errno == ENOENT) 3618 return; 3619 else { 3620 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 3621 return; 3622 } 3623 } 3624 while (fgets(str, STRSIZ, mlfile) != NULL) { 3625 cp = str; 3626 host = strsep(&cp, " \t\n"); 3627 dirp = strsep(&cp, " \t\n"); 3628 if (host == NULL || dirp == NULL) 3629 continue; 3630 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3631 if (mlp == (struct mountlist *)NULL) 3632 out_of_mem(); 3633 strncpy(mlp->ml_host, host, MNTNAMLEN); 3634 mlp->ml_host[MNTNAMLEN] = '\0'; 3635 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3636 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3637 3638 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3639 } 3640 fclose(mlfile); 3641 } 3642 3643 static void 3644 del_mlist(char *hostp, char *dirp) 3645 { 3646 struct mountlist *mlp, *mlp2; 3647 FILE *mlfile; 3648 int fnd = 0; 3649 3650 SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) { 3651 if (!strcmp(mlp->ml_host, hostp) && 3652 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 3653 fnd = 1; 3654 SLIST_REMOVE(&mlhead, mlp, mountlist, next); 3655 free((caddr_t)mlp); 3656 } 3657 } 3658 if (fnd) { 3659 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 3660 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 3661 return; 3662 } 3663 SLIST_FOREACH(mlp, &mlhead, next) { 3664 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3665 } 3666 fclose(mlfile); 3667 } 3668 } 3669 3670 static void 3671 add_mlist(char *hostp, char *dirp) 3672 { 3673 struct mountlist *mlp; 3674 FILE *mlfile; 3675 3676 SLIST_FOREACH(mlp, &mlhead, next) { 3677 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 3678 return; 3679 } 3680 3681 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 3682 if (mlp == (struct mountlist *)NULL) 3683 out_of_mem(); 3684 strncpy(mlp->ml_host, hostp, MNTNAMLEN); 3685 mlp->ml_host[MNTNAMLEN] = '\0'; 3686 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN); 3687 mlp->ml_dirp[MNTPATHLEN] = '\0'; 3688 SLIST_INSERT_HEAD(&mlhead, mlp, next); 3689 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 3690 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 3691 return; 3692 } 3693 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 3694 fclose(mlfile); 3695 } 3696 3697 /* 3698 * Free up a group list. 3699 */ 3700 static void 3701 free_grp(struct grouplist *grp) 3702 { 3703 if (grp->gr_type == GT_HOST) { 3704 if (grp->gr_ptr.gt_addrinfo != NULL) 3705 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 3706 } else if (grp->gr_type == GT_NET) { 3707 if (grp->gr_ptr.gt_net.nt_name) 3708 free(grp->gr_ptr.gt_net.nt_name); 3709 } 3710 if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps) 3711 free(grp->gr_anon.cr_groups); 3712 free((caddr_t)grp); 3713 } 3714 3715 #ifdef DEBUG 3716 static void 3717 SYSLOG(int pri, const char *fmt, ...) 3718 { 3719 va_list ap; 3720 3721 va_start(ap, fmt); 3722 vfprintf(stderr, fmt, ap); 3723 va_end(ap); 3724 } 3725 #endif /* DEBUG */ 3726 3727 /* 3728 * Check options for consistency. 3729 */ 3730 static int 3731 check_options(struct dirlist *dp) 3732 { 3733 3734 if (v4root_phase == 0 && dp == NULL) 3735 return (1); 3736 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 3737 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 3738 return (1); 3739 } 3740 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 3741 syslog(LOG_ERR, "-mask requires -network"); 3742 return (1); 3743 } 3744 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 3745 syslog(LOG_ERR, "-network requires mask specification"); 3746 return (1); 3747 } 3748 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 3749 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 3750 return (1); 3751 } 3752 if (v4root_phase > 0 && 3753 (opt_flags & 3754 ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) { 3755 syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:"); 3756 return (1); 3757 } 3758 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 3759 syslog(LOG_ERR, "-alldirs has multiple directories"); 3760 return (1); 3761 } 3762 return (0); 3763 } 3764 3765 /* 3766 * Check an absolute directory path for any symbolic links. Return true 3767 */ 3768 static int 3769 check_dirpath(char *dirp) 3770 { 3771 char *cp; 3772 int ret = 1; 3773 struct stat sb; 3774 3775 cp = dirp + 1; 3776 while (*cp && ret) { 3777 if (*cp == '/') { 3778 *cp = '\0'; 3779 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 3780 ret = 0; 3781 *cp = '/'; 3782 } 3783 cp++; 3784 } 3785 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 3786 ret = 0; 3787 return (ret); 3788 } 3789 3790 /* 3791 * Make a netmask according to the specified prefix length. The ss_family 3792 * and other non-address fields must be initialised before calling this. 3793 */ 3794 static int 3795 makemask(struct sockaddr_storage *ssp, int bitlen) 3796 { 3797 u_char *p; 3798 int bits, i, len; 3799 3800 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 3801 return (-1); 3802 if (bitlen > len * CHAR_BIT) 3803 return (-1); 3804 3805 for (i = 0; i < len; i++) { 3806 bits = MIN(CHAR_BIT, bitlen); 3807 *p++ = (u_char)~0 << (CHAR_BIT - bits); 3808 bitlen -= bits; 3809 } 3810 return 0; 3811 } 3812 3813 /* 3814 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 3815 * is acceptable (i.e. of the form 1...10....0). 3816 */ 3817 static int 3818 checkmask(struct sockaddr *sa) 3819 { 3820 u_char *mask; 3821 int i, len; 3822 3823 if ((mask = sa_rawaddr(sa, &len)) == NULL) 3824 return (-1); 3825 3826 for (i = 0; i < len; i++) 3827 if (mask[i] != 0xff) 3828 break; 3829 if (i < len) { 3830 if (~mask[i] & (u_char)(~mask[i] + 1)) 3831 return (-1); 3832 i++; 3833 } 3834 for (; i < len; i++) 3835 if (mask[i] != 0) 3836 return (-1); 3837 return (0); 3838 } 3839 3840 /* 3841 * Compare two sockaddrs according to a specified mask. Return zero if 3842 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 3843 * If samask is NULL, perform a full comparison. 3844 */ 3845 static int 3846 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 3847 { 3848 unsigned char *p1, *p2, *mask; 3849 int len, i; 3850 3851 if (sa1->sa_family != sa2->sa_family || 3852 (p1 = sa_rawaddr(sa1, &len)) == NULL || 3853 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 3854 return (1); 3855 3856 switch (sa1->sa_family) { 3857 case AF_INET6: 3858 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 3859 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 3860 return (1); 3861 break; 3862 } 3863 3864 /* Simple binary comparison if no mask specified. */ 3865 if (samask == NULL) 3866 return (memcmp(p1, p2, len)); 3867 3868 /* Set up the mask, and do a mask-based comparison. */ 3869 if (sa1->sa_family != samask->sa_family || 3870 (mask = sa_rawaddr(samask, NULL)) == NULL) 3871 return (1); 3872 3873 for (i = 0; i < len; i++) 3874 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 3875 return (1); 3876 return (0); 3877 } 3878 3879 /* 3880 * Return a pointer to the part of the sockaddr that contains the 3881 * raw address, and set *nbytes to its length in bytes. Returns 3882 * NULL if the address family is unknown. 3883 */ 3884 static void * 3885 sa_rawaddr(struct sockaddr *sa, int *nbytes) { 3886 void *p; 3887 int len; 3888 3889 switch (sa->sa_family) { 3890 case AF_INET: 3891 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 3892 p = &((struct sockaddr_in *)sa)->sin_addr; 3893 break; 3894 case AF_INET6: 3895 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 3896 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 3897 break; 3898 default: 3899 p = NULL; 3900 len = 0; 3901 } 3902 3903 if (nbytes != NULL) 3904 *nbytes = len; 3905 return (p); 3906 } 3907 3908 static void 3909 huphandler(int sig __unused) 3910 { 3911 3912 got_sighup = 1; 3913 } 3914 3915 static void 3916 terminate(int sig __unused) 3917 { 3918 pidfile_remove(pfh); 3919 rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); 3920 rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); 3921 exit (0); 3922 } 3923 3924 static void 3925 cp_cred(struct expcred *outcr, struct expcred *incr) 3926 { 3927 3928 outcr->cr_uid = incr->cr_uid; 3929 outcr->cr_ngroups = incr->cr_ngroups; 3930 if (outcr->cr_ngroups > SMALLNGROUPS) 3931 outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t)); 3932 else 3933 outcr->cr_groups = outcr->cr_smallgrps; 3934 memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups * 3935 sizeof(gid_t)); 3936 } 3937