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