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