mountd.c revision 164394
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 * 4. 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 34static 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 41static 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: head/usr.sbin/mountd/mountd.c 164394 2006-11-18 22:43:20Z rodrigc $"); 47 48#include <sys/param.h> 49#include <sys/mount.h> 50#include <sys/fcntl.h> 51#include <sys/stat.h> 52#include <sys/syslog.h> 53#include <sys/sysctl.h> 54#include <sys/linker.h> 55#include <sys/module.h> 56 57#include <rpc/rpc.h> 58#include <rpc/rpc_com.h> 59#include <rpc/pmap_clnt.h> 60#include <rpc/pmap_prot.h> 61#include <rpcsvc/mount.h> 62#include <nfs/rpcv2.h> 63#include <nfs/nfsproto.h> 64#include <nfsserver/nfs.h> 65 66#include <arpa/inet.h> 67 68#include <ctype.h> 69#include <err.h> 70#include <errno.h> 71#include <grp.h> 72#include <libutil.h> 73#include <limits.h> 74#include <netdb.h> 75#include <pwd.h> 76#include <signal.h> 77#include <stdio.h> 78#include <stdlib.h> 79#include <string.h> 80#include <unistd.h> 81#include "pathnames.h" 82#include "mntopts.h" 83 84#ifdef DEBUG 85#include <stdarg.h> 86#endif 87 88/* 89 * Structures for keeping the mount list and export list 90 */ 91struct mountlist { 92 struct mountlist *ml_next; 93 char ml_host[RPCMNT_NAMELEN+1]; 94 char ml_dirp[RPCMNT_PATHLEN+1]; 95}; 96 97struct dirlist { 98 struct dirlist *dp_left; 99 struct dirlist *dp_right; 100 int dp_flag; 101 struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 102 char dp_dirp[1]; /* Actually malloc'd to size of dir */ 103}; 104/* dp_flag bits */ 105#define DP_DEFSET 0x1 106#define DP_HOSTSET 0x2 107 108struct exportlist { 109 struct exportlist *ex_next; 110 struct dirlist *ex_dirl; 111 struct dirlist *ex_defdir; 112 int ex_flag; 113 fsid_t ex_fs; 114 char *ex_fsdir; 115 char *ex_indexfile; 116}; 117/* ex_flag bits */ 118#define EX_LINKED 0x1 119 120struct netmsk { 121 struct sockaddr_storage nt_net; 122 struct sockaddr_storage nt_mask; 123 char *nt_name; 124}; 125 126union grouptypes { 127 struct addrinfo *gt_addrinfo; 128 struct netmsk gt_net; 129}; 130 131struct grouplist { 132 int gr_type; 133 union grouptypes gr_ptr; 134 struct grouplist *gr_next; 135}; 136/* Group types */ 137#define GT_NULL 0x0 138#define GT_HOST 0x1 139#define GT_NET 0x2 140#define GT_DEFAULT 0x3 141#define GT_IGNORE 0x5 142 143struct hostlist { 144 int ht_flag; /* Uses DP_xx bits */ 145 struct grouplist *ht_grp; 146 struct hostlist *ht_next; 147}; 148 149struct fhreturn { 150 int fhr_flag; 151 int fhr_vers; 152 nfsfh_t fhr_fh; 153}; 154 155/* Global defs */ 156char *add_expdir(struct dirlist **, char *, int); 157void add_dlist(struct dirlist **, struct dirlist *, 158 struct grouplist *, int); 159void add_mlist(char *, char *); 160int check_dirpath(char *); 161int check_options(struct dirlist *); 162int checkmask(struct sockaddr *sa); 163int chk_host(struct dirlist *, struct sockaddr *, int *, int *); 164void del_mlist(char *hostp, char *dirp); 165struct dirlist *dirp_search(struct dirlist *, char *); 166int do_mount(struct exportlist *, struct grouplist *, int, 167 struct xucred *, char *, int, struct statfs *); 168int do_opt(char **, char **, struct exportlist *, struct grouplist *, 169 int *, int *, struct xucred *); 170struct exportlist *ex_search(fsid_t *); 171struct exportlist *get_exp(void); 172void free_dir(struct dirlist *); 173void free_exp(struct exportlist *); 174void free_grp(struct grouplist *); 175void free_host(struct hostlist *); 176void get_exportlist(void); 177int get_host(char *, struct grouplist *, struct grouplist *); 178struct hostlist *get_ht(void); 179int get_line(void); 180void get_mountlist(void); 181int get_net(char *, struct netmsk *, int); 182void getexp_err(struct exportlist *, struct grouplist *); 183struct grouplist *get_grp(void); 184void hang_dirp(struct dirlist *, struct grouplist *, 185 struct exportlist *, int); 186void huphandler(int sig); 187int makemask(struct sockaddr_storage *ssp, int bitlen); 188void mntsrv(struct svc_req *, SVCXPRT *); 189void nextfield(char **, char **); 190void out_of_mem(void); 191void parsecred(char *, struct xucred *); 192int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int); 193void *sa_rawaddr(struct sockaddr *sa, int *nbytes); 194int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, 195 struct sockaddr *samask); 196int scan_tree(struct dirlist *, struct sockaddr *); 197static void usage(void); 198int xdr_dir(XDR *, char *); 199int xdr_explist(XDR *, caddr_t); 200int xdr_explist_brief(XDR *, caddr_t); 201int xdr_fhs(XDR *, caddr_t); 202int xdr_mlist(XDR *, caddr_t); 203void terminate(int); 204 205struct exportlist *exphead; 206struct mountlist *mlhead; 207struct grouplist *grphead; 208char exname[MAXPATHLEN]; 209struct xucred def_anon = { 210 XUCRED_VERSION, 211 (uid_t)-2, 212 1, 213 { (gid_t)-2 }, 214 NULL 215}; 216int force_v2 = 0; 217int resvport_only = 1; 218int dir_only = 1; 219int dolog = 0; 220int got_sighup = 0; 221 222int opt_flags; 223static int have_v6 = 1; 224 225struct pidfh *pfh = NULL; 226/* Bits for opt_flags above */ 227#define OP_MAPROOT 0x01 228#define OP_MAPALL 0x02 229/* 0x4 free */ 230#define OP_MASK 0x08 231#define OP_NET 0x10 232#define OP_ALLDIRS 0x40 233#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ 234#define OP_QUIET 0x100 235#define OP_MASKLEN 0x200 236 237#ifdef DEBUG 238int debug = 1; 239void SYSLOG(int, const char *, ...) __printflike(2, 3); 240#define syslog SYSLOG 241#else 242int debug = 0; 243#endif 244 245/* 246 * Mountd server for NFS mount protocol as described in: 247 * NFS: Network File System Protocol Specification, RFC1094, Appendix A 248 * The optional arguments are the exports file name 249 * default: _PATH_EXPORTS 250 * and "-n" to allow nonroot mount. 251 */ 252int 253main(argc, argv) 254 int argc; 255 char **argv; 256{ 257 fd_set readfds; 258 struct sockaddr_in sin; 259 struct sockaddr_in6 sin6; 260 char *endptr; 261 SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp; 262 struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf; 263 pid_t otherpid; 264 int udpsock, tcpsock, udp6sock, tcp6sock; 265 int xcreated = 0, s; 266 int maxrec = RPC_MAXDATASIZE; 267 int one = 1; 268 int c, r; 269 in_port_t svcport = 0; 270 271 udp6conf = tcp6conf = NULL; 272 udp6sock = tcp6sock = 0; 273 274 /* Check that another mountd isn't already running. */ 275 pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); 276 if (pfh == NULL) { 277 if (errno == EEXIST) 278 errx(1, "mountd already running, pid: %d.", otherpid); 279 warn("cannot open or create pidfile"); 280 } 281 282 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 283 if (s < 0) 284 have_v6 = 0; 285 else 286 close(s); 287 if (modfind("nfsserver") < 0) { 288 /* Not present in kernel, try loading it */ 289 if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0) 290 errx(1, "NFS server is not available or loadable"); 291 } 292 293 while ((c = getopt(argc, argv, "2dlnp:r")) != -1) 294 switch (c) { 295 case '2': 296 force_v2 = 1; 297 break; 298 case 'n': 299 resvport_only = 0; 300 break; 301 case 'r': 302 dir_only = 0; 303 break; 304 case 'd': 305 debug = debug ? 0 : 1; 306 break; 307 case 'l': 308 dolog = 1; 309 break; 310 case 'p': 311 endptr = NULL; 312 svcport = (in_port_t)strtoul(optarg, &endptr, 10); 313 if (endptr == NULL || *endptr != '\0' || 314 svcport == 0 || svcport >= IPPORT_MAX) 315 usage(); 316 break; 317 default: 318 usage(); 319 }; 320 argc -= optind; 321 argv += optind; 322 grphead = (struct grouplist *)NULL; 323 exphead = (struct exportlist *)NULL; 324 mlhead = (struct mountlist *)NULL; 325 if (argc == 1) { 326 strncpy(exname, *argv, MAXPATHLEN-1); 327 exname[MAXPATHLEN-1] = '\0'; 328 } else 329 strcpy(exname, _PATH_EXPORTS); 330 openlog("mountd", LOG_PID, LOG_DAEMON); 331 if (debug) 332 warnx("getting export list"); 333 get_exportlist(); 334 if (debug) 335 warnx("getting mount list"); 336 get_mountlist(); 337 if (debug) 338 warnx("here we go"); 339 if (debug == 0) { 340 daemon(0, 0); 341 signal(SIGINT, SIG_IGN); 342 signal(SIGQUIT, SIG_IGN); 343 } 344 signal(SIGHUP, huphandler); 345 signal(SIGTERM, terminate); 346 signal(SIGPIPE, SIG_IGN); 347 348 pidfile_write(pfh); 349 350 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); 351 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); 352 udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 353 tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 354 udpconf = getnetconfigent("udp"); 355 tcpconf = getnetconfigent("tcp"); 356 357 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); 358 359 if (!have_v6) 360 goto skip_v6; 361 udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 362 tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); 363 /* 364 * We're doing host-based access checks here, so don't allow 365 * v4-in-v6 to confuse things. The kernel will disable it 366 * by default on NFS sockets too. 367 */ 368 if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6, 369 IPV6_V6ONLY, &one, sizeof one) < 0) { 370 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); 371 exit(1); 372 } 373 if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6, 374 IPV6_V6ONLY, &one, sizeof one) < 0) { 375 syslog(LOG_ERR, "can't disable v4-in-v6 on TCP socket"); 376 exit(1); 377 } 378 udp6conf = getnetconfigent("udp6"); 379 tcp6conf = getnetconfigent("tcp6"); 380 381skip_v6: 382 if (!resvport_only) { 383 if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL, 384 &resvport_only, sizeof(resvport_only)) != 0 && 385 errno != ENOENT) { 386 syslog(LOG_ERR, "sysctl: %m"); 387 exit(1); 388 } 389 } 390 if (svcport != 0) { 391 bzero(&sin, sizeof(struct sockaddr_in)); 392 sin.sin_len = sizeof(struct sockaddr_in); 393 sin.sin_family = AF_INET; 394 sin.sin_port = htons(svcport); 395 396 bzero(&sin6, sizeof(struct sockaddr_in6)); 397 sin6.sin6_len = sizeof(struct sockaddr_in6); 398 sin6.sin6_family = AF_INET6; 399 sin6.sin6_port = htons(svcport); 400 } 401 if (udpsock != -1 && udpconf != NULL) { 402 if (svcport != 0) { 403 r = bindresvport(udpsock, &sin); 404 if (r != 0) { 405 syslog(LOG_ERR, "bindresvport: %m"); 406 exit(1); 407 } 408 } else 409 (void)bindresvport(udpsock, NULL); 410 udptransp = svc_dg_create(udpsock, 0, 0); 411 if (udptransp != NULL) { 412 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1, 413 mntsrv, udpconf)) 414 syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service"); 415 else 416 xcreated++; 417 if (!force_v2) { 418 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3, 419 mntsrv, udpconf)) 420 syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service"); 421 else 422 xcreated++; 423 } 424 } else 425 syslog(LOG_WARNING, "can't create UDP services"); 426 427 } 428 if (tcpsock != -1 && tcpconf != NULL) { 429 if (svcport != 0) { 430 r = bindresvport(tcpsock, &sin); 431 if (r != 0) { 432 syslog(LOG_ERR, "bindresvport: %m"); 433 exit(1); 434 } 435 } else 436 (void)bindresvport(tcpsock, NULL); 437 listen(tcpsock, SOMAXCONN); 438 tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 439 if (tcptransp != NULL) { 440 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1, 441 mntsrv, tcpconf)) 442 syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service"); 443 else 444 xcreated++; 445 if (!force_v2) { 446 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3, 447 mntsrv, tcpconf)) 448 syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service"); 449 else 450 xcreated++; 451 } 452 } else 453 syslog(LOG_WARNING, "can't create TCP service"); 454 455 } 456 if (have_v6 && udp6sock != -1 && udp6conf != NULL) { 457 if (svcport != 0) { 458 r = bindresvport_sa(udp6sock, 459 (struct sockaddr *)&sin6); 460 if (r != 0) { 461 syslog(LOG_ERR, "bindresvport_sa: %m"); 462 exit(1); 463 } 464 } else 465 (void)bindresvport_sa(udp6sock, NULL); 466 udp6transp = svc_dg_create(udp6sock, 0, 0); 467 if (udp6transp != NULL) { 468 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1, 469 mntsrv, udp6conf)) 470 syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service"); 471 else 472 xcreated++; 473 if (!force_v2) { 474 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3, 475 mntsrv, udp6conf)) 476 syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service"); 477 else 478 xcreated++; 479 } 480 } else 481 syslog(LOG_WARNING, "can't create UDP6 service"); 482 483 } 484 if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) { 485 if (svcport != 0) { 486 r = bindresvport_sa(tcp6sock, 487 (struct sockaddr *)&sin6); 488 if (r != 0) { 489 syslog(LOG_ERR, "bindresvport_sa: %m"); 490 exit(1); 491 } 492 } else 493 (void)bindresvport_sa(tcp6sock, NULL); 494 listen(tcp6sock, SOMAXCONN); 495 tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 496 if (tcp6transp != NULL) { 497 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1, 498 mntsrv, tcp6conf)) 499 syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service"); 500 else 501 xcreated++; 502 if (!force_v2) { 503 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3, 504 mntsrv, tcp6conf)) 505 syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service"); 506 else 507 xcreated++; 508 } 509 } else 510 syslog(LOG_WARNING, "can't create TCP6 service"); 511 512 } 513 if (xcreated == 0) { 514 syslog(LOG_ERR, "could not create any services"); 515 exit(1); 516 } 517 518 /* Expand svc_run() here so that we can call get_exportlist(). */ 519 for (;;) { 520 if (got_sighup) { 521 get_exportlist(); 522 got_sighup = 0; 523 } 524 readfds = svc_fdset; 525 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) { 526 case -1: 527 if (errno == EINTR) 528 continue; 529 syslog(LOG_ERR, "mountd died: select: %m"); 530 exit(1); 531 case 0: 532 continue; 533 default: 534 svc_getreqset(&readfds); 535 } 536 } 537} 538 539static void 540usage() 541{ 542 fprintf(stderr, 543 "usage: mountd [-2] [-d] [-l] [-n] [-p <port>] [-r] " 544 "[export_file]\n"); 545 exit(1); 546} 547 548/* 549 * The mount rpc service 550 */ 551void 552mntsrv(rqstp, transp) 553 struct svc_req *rqstp; 554 SVCXPRT *transp; 555{ 556 struct exportlist *ep; 557 struct dirlist *dp; 558 struct fhreturn fhr; 559 struct stat stb; 560 struct statfs fsb; 561 char host[NI_MAXHOST], numerichost[NI_MAXHOST]; 562 int lookup_failed = 1; 563 struct sockaddr *saddr; 564 u_short sport; 565 char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; 566 int bad = 0, defset, hostset; 567 sigset_t sighup_mask; 568 569 sigemptyset(&sighup_mask); 570 sigaddset(&sighup_mask, SIGHUP); 571 saddr = svc_getrpccaller(transp)->buf; 572 switch (saddr->sa_family) { 573 case AF_INET6: 574 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 575 break; 576 case AF_INET: 577 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 578 break; 579 default: 580 syslog(LOG_ERR, "request from unknown address family"); 581 return; 582 } 583 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 584 NULL, 0, 0); 585 getnameinfo(saddr, saddr->sa_len, numerichost, 586 sizeof numerichost, NULL, 0, NI_NUMERICHOST); 587 switch (rqstp->rq_proc) { 588 case NULLPROC: 589 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) 590 syslog(LOG_ERR, "can't send reply"); 591 return; 592 case RPCMNT_MOUNT: 593 if (sport >= IPPORT_RESERVED && resvport_only) { 594 syslog(LOG_NOTICE, 595 "mount request from %s from unprivileged port", 596 numerichost); 597 svcerr_weakauth(transp); 598 return; 599 } 600 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 601 syslog(LOG_NOTICE, "undecodable mount request from %s", 602 numerichost); 603 svcerr_decode(transp); 604 return; 605 } 606 607 /* 608 * Get the real pathname and make sure it is a directory 609 * or a regular file if the -r option was specified 610 * and it exists. 611 */ 612 if (realpath(rpcpath, dirpath) == NULL || 613 stat(dirpath, &stb) < 0 || 614 (!S_ISDIR(stb.st_mode) && 615 (dir_only || !S_ISREG(stb.st_mode))) || 616 statfs(dirpath, &fsb) < 0) { 617 chdir("/"); /* Just in case realpath doesn't */ 618 syslog(LOG_NOTICE, 619 "mount request from %s for non existent path %s", 620 numerichost, dirpath); 621 if (debug) 622 warnx("stat failed on %s", dirpath); 623 bad = ENOENT; /* We will send error reply later */ 624 } 625 626 /* Check in the exports list */ 627 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 628 ep = ex_search(&fsb.f_fsid); 629 hostset = defset = 0; 630 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || 631 ((dp = dirp_search(ep->ex_dirl, dirpath)) && 632 chk_host(dp, saddr, &defset, &hostset)) || 633 (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 634 scan_tree(ep->ex_dirl, saddr) == 0))) { 635 if (bad) { 636 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 637 (caddr_t)&bad)) 638 syslog(LOG_ERR, "can't send reply"); 639 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 640 return; 641 } 642 if (hostset & DP_HOSTSET) 643 fhr.fhr_flag = hostset; 644 else 645 fhr.fhr_flag = defset; 646 fhr.fhr_vers = rqstp->rq_vers; 647 /* Get the file handle */ 648 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 649 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 650 bad = errno; 651 syslog(LOG_ERR, "can't get fh for %s", dirpath); 652 if (!svc_sendreply(transp, (xdrproc_t)xdr_long, 653 (caddr_t)&bad)) 654 syslog(LOG_ERR, "can't send reply"); 655 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 656 return; 657 } 658 if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, 659 (caddr_t)&fhr)) 660 syslog(LOG_ERR, "can't send reply"); 661 if (!lookup_failed) 662 add_mlist(host, dirpath); 663 else 664 add_mlist(numerichost, dirpath); 665 if (debug) 666 warnx("mount successful"); 667 if (dolog) 668 syslog(LOG_NOTICE, 669 "mount request succeeded from %s for %s", 670 numerichost, dirpath); 671 } else { 672 bad = EACCES; 673 syslog(LOG_NOTICE, 674 "mount request denied from %s for %s", 675 numerichost, dirpath); 676 } 677 678 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, 679 (caddr_t)&bad)) 680 syslog(LOG_ERR, "can't send reply"); 681 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 682 return; 683 case RPCMNT_DUMP: 684 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) 685 syslog(LOG_ERR, "can't send reply"); 686 else if (dolog) 687 syslog(LOG_NOTICE, 688 "dump request succeeded from %s", 689 numerichost); 690 return; 691 case RPCMNT_UMOUNT: 692 if (sport >= IPPORT_RESERVED && resvport_only) { 693 syslog(LOG_NOTICE, 694 "umount request from %s from unprivileged port", 695 numerichost); 696 svcerr_weakauth(transp); 697 return; 698 } 699 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { 700 syslog(LOG_NOTICE, "undecodable umount request from %s", 701 numerichost); 702 svcerr_decode(transp); 703 return; 704 } 705 if (realpath(rpcpath, dirpath) == NULL) { 706 syslog(LOG_NOTICE, "umount request from %s " 707 "for non existent path %s", 708 numerichost, dirpath); 709 } 710 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 711 syslog(LOG_ERR, "can't send reply"); 712 if (!lookup_failed) 713 del_mlist(host, dirpath); 714 del_mlist(numerichost, dirpath); 715 if (dolog) 716 syslog(LOG_NOTICE, 717 "umount request succeeded from %s for %s", 718 numerichost, dirpath); 719 return; 720 case RPCMNT_UMNTALL: 721 if (sport >= IPPORT_RESERVED && resvport_only) { 722 syslog(LOG_NOTICE, 723 "umountall request from %s from unprivileged port", 724 numerichost); 725 svcerr_weakauth(transp); 726 return; 727 } 728 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) 729 syslog(LOG_ERR, "can't send reply"); 730 if (!lookup_failed) 731 del_mlist(host, NULL); 732 del_mlist(numerichost, NULL); 733 if (dolog) 734 syslog(LOG_NOTICE, 735 "umountall request succeeded from %s", 736 numerichost); 737 return; 738 case RPCMNT_EXPORT: 739 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) 740 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, 741 (caddr_t)NULL)) 742 syslog(LOG_ERR, "can't send reply"); 743 if (dolog) 744 syslog(LOG_NOTICE, 745 "export request succeeded from %s", 746 numerichost); 747 return; 748 default: 749 svcerr_noproc(transp); 750 return; 751 } 752} 753 754/* 755 * Xdr conversion for a dirpath string 756 */ 757int 758xdr_dir(xdrsp, dirp) 759 XDR *xdrsp; 760 char *dirp; 761{ 762 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 763} 764 765/* 766 * Xdr routine to generate file handle reply 767 */ 768int 769xdr_fhs(xdrsp, cp) 770 XDR *xdrsp; 771 caddr_t cp; 772{ 773 struct fhreturn *fhrp = (struct fhreturn *)cp; 774 u_long ok = 0, len, auth; 775 776 if (!xdr_long(xdrsp, &ok)) 777 return (0); 778 switch (fhrp->fhr_vers) { 779 case 1: 780 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 781 case 3: 782 len = NFSX_V3FH; 783 if (!xdr_long(xdrsp, &len)) 784 return (0); 785 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 786 return (0); 787 auth = RPCAUTH_UNIX; 788 len = 1; 789 if (!xdr_long(xdrsp, &len)) 790 return (0); 791 return (xdr_long(xdrsp, &auth)); 792 }; 793 return (0); 794} 795 796int 797xdr_mlist(xdrsp, cp) 798 XDR *xdrsp; 799 caddr_t cp; 800{ 801 struct mountlist *mlp; 802 int true = 1; 803 int false = 0; 804 char *strp; 805 806 mlp = mlhead; 807 while (mlp) { 808 if (!xdr_bool(xdrsp, &true)) 809 return (0); 810 strp = &mlp->ml_host[0]; 811 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 812 return (0); 813 strp = &mlp->ml_dirp[0]; 814 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 815 return (0); 816 mlp = mlp->ml_next; 817 } 818 if (!xdr_bool(xdrsp, &false)) 819 return (0); 820 return (1); 821} 822 823/* 824 * Xdr conversion for export list 825 */ 826int 827xdr_explist_common(xdrsp, cp, brief) 828 XDR *xdrsp; 829 caddr_t cp; 830 int brief; 831{ 832 struct exportlist *ep; 833 int false = 0; 834 int putdef; 835 sigset_t sighup_mask; 836 837 sigemptyset(&sighup_mask); 838 sigaddset(&sighup_mask, SIGHUP); 839 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 840 ep = exphead; 841 while (ep) { 842 putdef = 0; 843 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, 844 &putdef, brief)) 845 goto errout; 846 if (ep->ex_defdir && putdef == 0 && 847 put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, 848 &putdef, brief)) 849 goto errout; 850 ep = ep->ex_next; 851 } 852 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 853 if (!xdr_bool(xdrsp, &false)) 854 return (0); 855 return (1); 856errout: 857 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 858 return (0); 859} 860 861/* 862 * Called from xdr_explist() to traverse the tree and export the 863 * directory paths. 864 */ 865int 866put_exlist(dp, xdrsp, adp, putdefp, brief) 867 struct dirlist *dp; 868 XDR *xdrsp; 869 struct dirlist *adp; 870 int *putdefp; 871 int brief; 872{ 873 struct grouplist *grp; 874 struct hostlist *hp; 875 int true = 1; 876 int false = 0; 877 int gotalldir = 0; 878 char *strp; 879 880 if (dp) { 881 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) 882 return (1); 883 if (!xdr_bool(xdrsp, &true)) 884 return (1); 885 strp = dp->dp_dirp; 886 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 887 return (1); 888 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 889 gotalldir = 1; 890 *putdefp = 1; 891 } 892 if (brief) { 893 if (!xdr_bool(xdrsp, &true)) 894 return (1); 895 strp = "(...)"; 896 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 897 return (1); 898 } else if ((dp->dp_flag & DP_DEFSET) == 0 && 899 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 900 hp = dp->dp_hosts; 901 while (hp) { 902 grp = hp->ht_grp; 903 if (grp->gr_type == GT_HOST) { 904 if (!xdr_bool(xdrsp, &true)) 905 return (1); 906 strp = grp->gr_ptr.gt_addrinfo->ai_canonname; 907 if (!xdr_string(xdrsp, &strp, 908 RPCMNT_NAMELEN)) 909 return (1); 910 } else if (grp->gr_type == GT_NET) { 911 if (!xdr_bool(xdrsp, &true)) 912 return (1); 913 strp = grp->gr_ptr.gt_net.nt_name; 914 if (!xdr_string(xdrsp, &strp, 915 RPCMNT_NAMELEN)) 916 return (1); 917 } 918 hp = hp->ht_next; 919 if (gotalldir && hp == (struct hostlist *)NULL) { 920 hp = adp->dp_hosts; 921 gotalldir = 0; 922 } 923 } 924 } 925 if (!xdr_bool(xdrsp, &false)) 926 return (1); 927 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) 928 return (1); 929 } 930 return (0); 931} 932 933int 934xdr_explist(xdrsp, cp) 935 XDR *xdrsp; 936 caddr_t cp; 937{ 938 939 return xdr_explist_common(xdrsp, cp, 0); 940} 941 942int 943xdr_explist_brief(xdrsp, cp) 944 XDR *xdrsp; 945 caddr_t cp; 946{ 947 948 return xdr_explist_common(xdrsp, cp, 1); 949} 950 951char *line; 952int linesize; 953FILE *exp_file; 954 955/* 956 * Get the export list 957 */ 958void 959get_exportlist() 960{ 961 struct exportlist *ep, *ep2; 962 struct grouplist *grp, *tgrp; 963 struct exportlist **epp; 964 struct export_args export; 965 struct dirlist *dirhead; 966 struct iovec *iov; 967 struct statfs fsb, *fsp, *mntbufp; 968 struct xucred anon; 969 struct xvfsconf vfc; 970 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; 971 char errmsg[255]; 972 int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; 973 int iovlen; 974 975 bzero(&export, sizeof(export)); 976 export.ex_flags = MNT_DELEXPORT; 977 dirp = NULL; 978 dirplen = 0; 979 iov = NULL; 980 iovlen = 0; 981 bzero(errmsg, sizeof(errmsg)); 982 983 /* 984 * First, get rid of the old list 985 */ 986 ep = exphead; 987 while (ep) { 988 ep2 = ep; 989 ep = ep->ex_next; 990 free_exp(ep2); 991 } 992 exphead = (struct exportlist *)NULL; 993 994 grp = grphead; 995 while (grp) { 996 tgrp = grp; 997 grp = grp->gr_next; 998 free_grp(tgrp); 999 } 1000 grphead = (struct grouplist *)NULL; 1001 1002 /* 1003 * And delete exports that are in the kernel for all local 1004 * filesystems. 1005 * XXX: Should know how to handle all local exportable filesystems. 1006 */ 1007 num = getmntinfo(&mntbufp, MNT_NOWAIT); 1008 1009 if (num > 0) { 1010 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 1011 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 1012 build_iovec(&iov, &iovlen, "from", NULL, 0); 1013 build_iovec(&iov, &iovlen, "update", NULL, 0); 1014 build_iovec(&iov, &iovlen, "export", &export, sizeof(export)); 1015 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 1016 } 1017 1018 for (i = 0; i < num; i++) { 1019 fsp = &mntbufp[i]; 1020 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { 1021 syslog(LOG_ERR, "getvfsbyname() failed for %s", 1022 fsp->f_fstypename); 1023 continue; 1024 } 1025 1026 /* 1027 * Do not delete export for network filesystem by 1028 * passing "export" arg to nmount(). 1029 * It only makes sense to do this for local filesystems. 1030 */ 1031 if (vfc.vfc_flags & VFCF_NETWORK) 1032 continue; 1033 1034 iov[1].iov_base = fsp->f_fstypename; 1035 iov[1].iov_len = strlen(fsp->f_fstypename) + 1; 1036 iov[3].iov_base = fsp->f_mntonname; 1037 iov[3].iov_len = strlen(fsp->f_mntonname) + 1; 1038 iov[5].iov_base = fsp->f_mntfromname; 1039 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; 1040 1041 /* 1042 * Kick out MNT_ROOTFS. It should not be passed from 1043 * userland to kernel. It should only be used 1044 * internally in the kernel. 1045 */ 1046 if (fsp->f_flags & MNT_ROOTFS) { 1047 fsp->f_flags &= ~MNT_ROOTFS; 1048 } 1049 1050 if (nmount(iov, iovlen, fsp->f_flags) < 0 && 1051 errno != ENOENT && errno != ENOTSUP) { 1052 syslog(LOG_ERR, 1053 "can't delete exports for %s: %m %s", 1054 fsp->f_mntonname, errmsg); 1055 } 1056 } 1057 1058 if (iov != NULL) { 1059 /* Free strings allocated by strdup() in getmntopts.c */ 1060 free(iov[0].iov_base); /* fstype */ 1061 free(iov[2].iov_base); /* fspath */ 1062 free(iov[4].iov_base); /* from */ 1063 free(iov[6].iov_base); /* update */ 1064 free(iov[8].iov_base); /* export */ 1065 free(iov[10].iov_base); /* errmsg */ 1066 1067 /* free iov, allocated by realloc() */ 1068 free(iov); 1069 iovlen = 0; 1070 } 1071 1072 /* 1073 * Read in the exports file and build the list, calling 1074 * nmount() as we go along to push the export rules into the kernel. 1075 */ 1076 if ((exp_file = fopen(exname, "r")) == NULL) { 1077 syslog(LOG_ERR, "can't open %s", exname); 1078 exit(2); 1079 } 1080 dirhead = (struct dirlist *)NULL; 1081 while (get_line()) { 1082 if (debug) 1083 warnx("got line %s", line); 1084 cp = line; 1085 nextfield(&cp, &endcp); 1086 if (*cp == '#') 1087 goto nextline; 1088 1089 /* 1090 * Set defaults. 1091 */ 1092 has_host = FALSE; 1093 anon = def_anon; 1094 exflags = MNT_EXPORTED; 1095 got_nondir = 0; 1096 opt_flags = 0; 1097 ep = (struct exportlist *)NULL; 1098 1099 /* 1100 * Create new exports list entry 1101 */ 1102 len = endcp-cp; 1103 tgrp = grp = get_grp(); 1104 while (len > 0) { 1105 if (len > RPCMNT_NAMELEN) { 1106 getexp_err(ep, tgrp); 1107 goto nextline; 1108 } 1109 if (*cp == '-') { 1110 if (ep == (struct exportlist *)NULL) { 1111 getexp_err(ep, tgrp); 1112 goto nextline; 1113 } 1114 if (debug) 1115 warnx("doing opt %s", cp); 1116 got_nondir = 1; 1117 if (do_opt(&cp, &endcp, ep, grp, &has_host, 1118 &exflags, &anon)) { 1119 getexp_err(ep, tgrp); 1120 goto nextline; 1121 } 1122 } else if (*cp == '/') { 1123 savedc = *endcp; 1124 *endcp = '\0'; 1125 if (check_dirpath(cp) && 1126 statfs(cp, &fsb) >= 0) { 1127 if (got_nondir) { 1128 syslog(LOG_ERR, "dirs must be first"); 1129 getexp_err(ep, tgrp); 1130 goto nextline; 1131 } 1132 if (ep) { 1133 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || 1134 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { 1135 getexp_err(ep, tgrp); 1136 goto nextline; 1137 } 1138 } else { 1139 /* 1140 * See if this directory is already 1141 * in the list. 1142 */ 1143 ep = ex_search(&fsb.f_fsid); 1144 if (ep == (struct exportlist *)NULL) { 1145 ep = get_exp(); 1146 ep->ex_fs = fsb.f_fsid; 1147 ep->ex_fsdir = (char *) 1148 malloc(strlen(fsb.f_mntonname) + 1); 1149 if (ep->ex_fsdir) 1150 strcpy(ep->ex_fsdir, 1151 fsb.f_mntonname); 1152 else 1153 out_of_mem(); 1154 if (debug) 1155 warnx("making new ep fs=0x%x,0x%x", 1156 fsb.f_fsid.val[0], 1157 fsb.f_fsid.val[1]); 1158 } else if (debug) 1159 warnx("found ep fs=0x%x,0x%x", 1160 fsb.f_fsid.val[0], 1161 fsb.f_fsid.val[1]); 1162 } 1163 1164 /* 1165 * Add dirpath to export mount point. 1166 */ 1167 dirp = add_expdir(&dirhead, cp, len); 1168 dirplen = len; 1169 } else { 1170 getexp_err(ep, tgrp); 1171 goto nextline; 1172 } 1173 *endcp = savedc; 1174 } else { 1175 savedc = *endcp; 1176 *endcp = '\0'; 1177 got_nondir = 1; 1178 if (ep == (struct exportlist *)NULL) { 1179 getexp_err(ep, tgrp); 1180 goto nextline; 1181 } 1182 1183 /* 1184 * Get the host or netgroup. 1185 */ 1186 setnetgrent(cp); 1187 netgrp = getnetgrent(&hst, &usr, &dom); 1188 do { 1189 if (has_host) { 1190 grp->gr_next = get_grp(); 1191 grp = grp->gr_next; 1192 } 1193 if (netgrp) { 1194 if (hst == 0) { 1195 syslog(LOG_ERR, 1196 "null hostname in netgroup %s, skipping", cp); 1197 grp->gr_type = GT_IGNORE; 1198 } else if (get_host(hst, grp, tgrp)) { 1199 syslog(LOG_ERR, 1200 "bad host %s in netgroup %s, skipping", hst, cp); 1201 grp->gr_type = GT_IGNORE; 1202 } 1203 } else if (get_host(cp, grp, tgrp)) { 1204 syslog(LOG_ERR, "bad host %s, skipping", cp); 1205 grp->gr_type = GT_IGNORE; 1206 } 1207 has_host = TRUE; 1208 } while (netgrp && getnetgrent(&hst, &usr, &dom)); 1209 endnetgrent(); 1210 *endcp = savedc; 1211 } 1212 cp = endcp; 1213 nextfield(&cp, &endcp); 1214 len = endcp - cp; 1215 } 1216 if (check_options(dirhead)) { 1217 getexp_err(ep, tgrp); 1218 goto nextline; 1219 } 1220 if (!has_host) { 1221 grp->gr_type = GT_DEFAULT; 1222 if (debug) 1223 warnx("adding a default entry"); 1224 1225 /* 1226 * Don't allow a network export coincide with a list of 1227 * host(s) on the same line. 1228 */ 1229 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1230 syslog(LOG_ERR, "network/host conflict"); 1231 getexp_err(ep, tgrp); 1232 goto nextline; 1233 1234 /* 1235 * If an export list was specified on this line, make sure 1236 * that we have at least one valid entry, otherwise skip it. 1237 */ 1238 } else { 1239 grp = tgrp; 1240 while (grp && grp->gr_type == GT_IGNORE) 1241 grp = grp->gr_next; 1242 if (! grp) { 1243 getexp_err(ep, tgrp); 1244 goto nextline; 1245 } 1246 } 1247 1248 /* 1249 * Loop through hosts, pushing the exports into the kernel. 1250 * After loop, tgrp points to the start of the list and 1251 * grp points to the last entry in the list. 1252 */ 1253 grp = tgrp; 1254 do { 1255 if (do_mount(ep, grp, exflags, &anon, dirp, dirplen, 1256 &fsb)) { 1257 getexp_err(ep, tgrp); 1258 goto nextline; 1259 } 1260 } while (grp->gr_next && (grp = grp->gr_next)); 1261 1262 /* 1263 * Success. Update the data structures. 1264 */ 1265 if (has_host) { 1266 hang_dirp(dirhead, tgrp, ep, opt_flags); 1267 grp->gr_next = grphead; 1268 grphead = tgrp; 1269 } else { 1270 hang_dirp(dirhead, (struct grouplist *)NULL, ep, 1271 opt_flags); 1272 free_grp(grp); 1273 } 1274 dirhead = (struct dirlist *)NULL; 1275 if ((ep->ex_flag & EX_LINKED) == 0) { 1276 ep2 = exphead; 1277 epp = &exphead; 1278 1279 /* 1280 * Insert in the list in alphabetical order. 1281 */ 1282 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { 1283 epp = &ep2->ex_next; 1284 ep2 = ep2->ex_next; 1285 } 1286 if (ep2) 1287 ep->ex_next = ep2; 1288 *epp = ep; 1289 ep->ex_flag |= EX_LINKED; 1290 } 1291nextline: 1292 if (dirhead) { 1293 free_dir(dirhead); 1294 dirhead = (struct dirlist *)NULL; 1295 } 1296 } 1297 fclose(exp_file); 1298} 1299 1300/* 1301 * Allocate an export list element 1302 */ 1303struct exportlist * 1304get_exp() 1305{ 1306 struct exportlist *ep; 1307 1308 ep = (struct exportlist *)malloc(sizeof (struct exportlist)); 1309 if (ep == (struct exportlist *)NULL) 1310 out_of_mem(); 1311 memset(ep, 0, sizeof(struct exportlist)); 1312 return (ep); 1313} 1314 1315/* 1316 * Allocate a group list element 1317 */ 1318struct grouplist * 1319get_grp() 1320{ 1321 struct grouplist *gp; 1322 1323 gp = (struct grouplist *)malloc(sizeof (struct grouplist)); 1324 if (gp == (struct grouplist *)NULL) 1325 out_of_mem(); 1326 memset(gp, 0, sizeof(struct grouplist)); 1327 return (gp); 1328} 1329 1330/* 1331 * Clean up upon an error in get_exportlist(). 1332 */ 1333void 1334getexp_err(ep, grp) 1335 struct exportlist *ep; 1336 struct grouplist *grp; 1337{ 1338 struct grouplist *tgrp; 1339 1340 if (!(opt_flags & OP_QUIET)) 1341 syslog(LOG_ERR, "bad exports list line %s", line); 1342 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1343 free_exp(ep); 1344 while (grp) { 1345 tgrp = grp; 1346 grp = grp->gr_next; 1347 free_grp(tgrp); 1348 } 1349} 1350 1351/* 1352 * Search the export list for a matching fs. 1353 */ 1354struct exportlist * 1355ex_search(fsid) 1356 fsid_t *fsid; 1357{ 1358 struct exportlist *ep; 1359 1360 ep = exphead; 1361 while (ep) { 1362 if (ep->ex_fs.val[0] == fsid->val[0] && 1363 ep->ex_fs.val[1] == fsid->val[1]) 1364 return (ep); 1365 ep = ep->ex_next; 1366 } 1367 return (ep); 1368} 1369 1370/* 1371 * Add a directory path to the list. 1372 */ 1373char * 1374add_expdir(dpp, cp, len) 1375 struct dirlist **dpp; 1376 char *cp; 1377 int len; 1378{ 1379 struct dirlist *dp; 1380 1381 dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); 1382 if (dp == (struct dirlist *)NULL) 1383 out_of_mem(); 1384 dp->dp_left = *dpp; 1385 dp->dp_right = (struct dirlist *)NULL; 1386 dp->dp_flag = 0; 1387 dp->dp_hosts = (struct hostlist *)NULL; 1388 strcpy(dp->dp_dirp, cp); 1389 *dpp = dp; 1390 return (dp->dp_dirp); 1391} 1392 1393/* 1394 * Hang the dir list element off the dirpath binary tree as required 1395 * and update the entry for host. 1396 */ 1397void 1398hang_dirp(dp, grp, ep, flags) 1399 struct dirlist *dp; 1400 struct grouplist *grp; 1401 struct exportlist *ep; 1402 int flags; 1403{ 1404 struct hostlist *hp; 1405 struct dirlist *dp2; 1406 1407 if (flags & OP_ALLDIRS) { 1408 if (ep->ex_defdir) 1409 free((caddr_t)dp); 1410 else 1411 ep->ex_defdir = dp; 1412 if (grp == (struct grouplist *)NULL) { 1413 ep->ex_defdir->dp_flag |= DP_DEFSET; 1414 } else while (grp) { 1415 hp = get_ht(); 1416 hp->ht_grp = grp; 1417 hp->ht_next = ep->ex_defdir->dp_hosts; 1418 ep->ex_defdir->dp_hosts = hp; 1419 grp = grp->gr_next; 1420 } 1421 } else { 1422 1423 /* 1424 * Loop through the directories adding them to the tree. 1425 */ 1426 while (dp) { 1427 dp2 = dp->dp_left; 1428 add_dlist(&ep->ex_dirl, dp, grp, flags); 1429 dp = dp2; 1430 } 1431 } 1432} 1433 1434/* 1435 * Traverse the binary tree either updating a node that is already there 1436 * for the new directory or adding the new node. 1437 */ 1438void 1439add_dlist(dpp, newdp, grp, flags) 1440 struct dirlist **dpp; 1441 struct dirlist *newdp; 1442 struct grouplist *grp; 1443 int flags; 1444{ 1445 struct dirlist *dp; 1446 struct hostlist *hp; 1447 int cmp; 1448 1449 dp = *dpp; 1450 if (dp) { 1451 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 1452 if (cmp > 0) { 1453 add_dlist(&dp->dp_left, newdp, grp, flags); 1454 return; 1455 } else if (cmp < 0) { 1456 add_dlist(&dp->dp_right, newdp, grp, flags); 1457 return; 1458 } else 1459 free((caddr_t)newdp); 1460 } else { 1461 dp = newdp; 1462 dp->dp_left = (struct dirlist *)NULL; 1463 *dpp = dp; 1464 } 1465 if (grp) { 1466 1467 /* 1468 * Hang all of the host(s) off of the directory point. 1469 */ 1470 do { 1471 hp = get_ht(); 1472 hp->ht_grp = grp; 1473 hp->ht_next = dp->dp_hosts; 1474 dp->dp_hosts = hp; 1475 grp = grp->gr_next; 1476 } while (grp); 1477 } else { 1478 dp->dp_flag |= DP_DEFSET; 1479 } 1480} 1481 1482/* 1483 * Search for a dirpath on the export point. 1484 */ 1485struct dirlist * 1486dirp_search(dp, dirp) 1487 struct dirlist *dp; 1488 char *dirp; 1489{ 1490 int cmp; 1491 1492 if (dp) { 1493 cmp = strcmp(dp->dp_dirp, dirp); 1494 if (cmp > 0) 1495 return (dirp_search(dp->dp_left, dirp)); 1496 else if (cmp < 0) 1497 return (dirp_search(dp->dp_right, dirp)); 1498 else 1499 return (dp); 1500 } 1501 return (dp); 1502} 1503 1504/* 1505 * Scan for a host match in a directory tree. 1506 */ 1507int 1508chk_host(dp, saddr, defsetp, hostsetp) 1509 struct dirlist *dp; 1510 struct sockaddr *saddr; 1511 int *defsetp; 1512 int *hostsetp; 1513{ 1514 struct hostlist *hp; 1515 struct grouplist *grp; 1516 struct addrinfo *ai; 1517 1518 if (dp) { 1519 if (dp->dp_flag & DP_DEFSET) 1520 *defsetp = dp->dp_flag; 1521 hp = dp->dp_hosts; 1522 while (hp) { 1523 grp = hp->ht_grp; 1524 switch (grp->gr_type) { 1525 case GT_HOST: 1526 ai = grp->gr_ptr.gt_addrinfo; 1527 for (; ai; ai = ai->ai_next) { 1528 if (!sacmp(ai->ai_addr, saddr, NULL)) { 1529 *hostsetp = 1530 (hp->ht_flag | DP_HOSTSET); 1531 return (1); 1532 } 1533 } 1534 break; 1535 case GT_NET: 1536 if (!sacmp(saddr, (struct sockaddr *) 1537 &grp->gr_ptr.gt_net.nt_net, 1538 (struct sockaddr *) 1539 &grp->gr_ptr.gt_net.nt_mask)) { 1540 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1541 return (1); 1542 } 1543 break; 1544 } 1545 hp = hp->ht_next; 1546 } 1547 } 1548 return (0); 1549} 1550 1551/* 1552 * Scan tree for a host that matches the address. 1553 */ 1554int 1555scan_tree(dp, saddr) 1556 struct dirlist *dp; 1557 struct sockaddr *saddr; 1558{ 1559 int defset, hostset; 1560 1561 if (dp) { 1562 if (scan_tree(dp->dp_left, saddr)) 1563 return (1); 1564 if (chk_host(dp, saddr, &defset, &hostset)) 1565 return (1); 1566 if (scan_tree(dp->dp_right, saddr)) 1567 return (1); 1568 } 1569 return (0); 1570} 1571 1572/* 1573 * Traverse the dirlist tree and free it up. 1574 */ 1575void 1576free_dir(dp) 1577 struct dirlist *dp; 1578{ 1579 1580 if (dp) { 1581 free_dir(dp->dp_left); 1582 free_dir(dp->dp_right); 1583 free_host(dp->dp_hosts); 1584 free((caddr_t)dp); 1585 } 1586} 1587 1588/* 1589 * Parse the option string and update fields. 1590 * Option arguments may either be -<option>=<value> or 1591 * -<option> <value> 1592 */ 1593int 1594do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) 1595 char **cpp, **endcpp; 1596 struct exportlist *ep; 1597 struct grouplist *grp; 1598 int *has_hostp; 1599 int *exflagsp; 1600 struct xucred *cr; 1601{ 1602 char *cpoptarg, *cpoptend; 1603 char *cp, *endcp, *cpopt, savedc, savedc2; 1604 int allflag, usedarg; 1605 1606 savedc2 = '\0'; 1607 cpopt = *cpp; 1608 cpopt++; 1609 cp = *endcpp; 1610 savedc = *cp; 1611 *cp = '\0'; 1612 while (cpopt && *cpopt) { 1613 allflag = 1; 1614 usedarg = -2; 1615 if ((cpoptend = strchr(cpopt, ','))) { 1616 *cpoptend++ = '\0'; 1617 if ((cpoptarg = strchr(cpopt, '='))) 1618 *cpoptarg++ = '\0'; 1619 } else { 1620 if ((cpoptarg = strchr(cpopt, '='))) 1621 *cpoptarg++ = '\0'; 1622 else { 1623 *cp = savedc; 1624 nextfield(&cp, &endcp); 1625 **endcpp = '\0'; 1626 if (endcp > cp && *cp != '-') { 1627 cpoptarg = cp; 1628 savedc2 = *endcp; 1629 *endcp = '\0'; 1630 usedarg = 0; 1631 } 1632 } 1633 } 1634 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 1635 *exflagsp |= MNT_EXRDONLY; 1636 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 1637 !(allflag = strcmp(cpopt, "mapall")) || 1638 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 1639 usedarg++; 1640 parsecred(cpoptarg, cr); 1641 if (allflag == 0) { 1642 *exflagsp |= MNT_EXPORTANON; 1643 opt_flags |= OP_MAPALL; 1644 } else 1645 opt_flags |= OP_MAPROOT; 1646 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 1647 !strcmp(cpopt, "m"))) { 1648 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 1649 syslog(LOG_ERR, "bad mask: %s", cpoptarg); 1650 return (1); 1651 } 1652 usedarg++; 1653 opt_flags |= OP_MASK; 1654 } else if (cpoptarg && (!strcmp(cpopt, "network") || 1655 !strcmp(cpopt, "n"))) { 1656 if (strchr(cpoptarg, '/') != NULL) { 1657 if (debug) 1658 fprintf(stderr, "setting OP_MASKLEN\n"); 1659 opt_flags |= OP_MASKLEN; 1660 } 1661 if (grp->gr_type != GT_NULL) { 1662 syslog(LOG_ERR, "network/host conflict"); 1663 return (1); 1664 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 1665 syslog(LOG_ERR, "bad net: %s", cpoptarg); 1666 return (1); 1667 } 1668 grp->gr_type = GT_NET; 1669 *has_hostp = 1; 1670 usedarg++; 1671 opt_flags |= OP_NET; 1672 } else if (!strcmp(cpopt, "alldirs")) { 1673 opt_flags |= OP_ALLDIRS; 1674 } else if (!strcmp(cpopt, "public")) { 1675 *exflagsp |= MNT_EXPUBLIC; 1676 } else if (!strcmp(cpopt, "webnfs")) { 1677 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON); 1678 opt_flags |= OP_MAPALL; 1679 } else if (cpoptarg && !strcmp(cpopt, "index")) { 1680 ep->ex_indexfile = strdup(cpoptarg); 1681 } else if (!strcmp(cpopt, "quiet")) { 1682 opt_flags |= OP_QUIET; 1683 } else { 1684 syslog(LOG_ERR, "bad opt %s", cpopt); 1685 return (1); 1686 } 1687 if (usedarg >= 0) { 1688 *endcp = savedc2; 1689 **endcpp = savedc; 1690 if (usedarg > 0) { 1691 *cpp = cp; 1692 *endcpp = endcp; 1693 } 1694 return (0); 1695 } 1696 cpopt = cpoptend; 1697 } 1698 **endcpp = savedc; 1699 return (0); 1700} 1701 1702/* 1703 * Translate a character string to the corresponding list of network 1704 * addresses for a hostname. 1705 */ 1706int 1707get_host(cp, grp, tgrp) 1708 char *cp; 1709 struct grouplist *grp; 1710 struct grouplist *tgrp; 1711{ 1712 struct grouplist *checkgrp; 1713 struct addrinfo *ai, *tai, hints; 1714 int ecode; 1715 char host[NI_MAXHOST]; 1716 1717 if (grp->gr_type != GT_NULL) { 1718 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); 1719 return (1); 1720 } 1721 memset(&hints, 0, sizeof hints); 1722 hints.ai_flags = AI_CANONNAME; 1723 hints.ai_protocol = IPPROTO_UDP; 1724 ecode = getaddrinfo(cp, NULL, &hints, &ai); 1725 if (ecode != 0) { 1726 syslog(LOG_ERR,"can't get address info for host %s", cp); 1727 return 1; 1728 } 1729 grp->gr_ptr.gt_addrinfo = ai; 1730 while (ai != NULL) { 1731 if (ai->ai_canonname == NULL) { 1732 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 1733 sizeof host, NULL, 0, NI_NUMERICHOST) != 0) 1734 strlcpy(host, "?", sizeof(host)); 1735 ai->ai_canonname = strdup(host); 1736 ai->ai_flags |= AI_CANONNAME; 1737 } 1738 if (debug) 1739 fprintf(stderr, "got host %s\n", ai->ai_canonname); 1740 /* 1741 * Sanity check: make sure we don't already have an entry 1742 * for this host in the grouplist. 1743 */ 1744 for (checkgrp = tgrp; checkgrp != NULL; 1745 checkgrp = checkgrp->gr_next) { 1746 if (checkgrp->gr_type != GT_HOST) 1747 continue; 1748 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL; 1749 tai = tai->ai_next) { 1750 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0) 1751 continue; 1752 if (debug) 1753 fprintf(stderr, 1754 "ignoring duplicate host %s\n", 1755 ai->ai_canonname); 1756 grp->gr_type = GT_IGNORE; 1757 return (0); 1758 } 1759 } 1760 ai = ai->ai_next; 1761 } 1762 grp->gr_type = GT_HOST; 1763 return (0); 1764} 1765 1766/* 1767 * Free up an exports list component 1768 */ 1769void 1770free_exp(ep) 1771 struct exportlist *ep; 1772{ 1773 1774 if (ep->ex_defdir) { 1775 free_host(ep->ex_defdir->dp_hosts); 1776 free((caddr_t)ep->ex_defdir); 1777 } 1778 if (ep->ex_fsdir) 1779 free(ep->ex_fsdir); 1780 if (ep->ex_indexfile) 1781 free(ep->ex_indexfile); 1782 free_dir(ep->ex_dirl); 1783 free((caddr_t)ep); 1784} 1785 1786/* 1787 * Free hosts. 1788 */ 1789void 1790free_host(hp) 1791 struct hostlist *hp; 1792{ 1793 struct hostlist *hp2; 1794 1795 while (hp) { 1796 hp2 = hp; 1797 hp = hp->ht_next; 1798 free((caddr_t)hp2); 1799 } 1800} 1801 1802struct hostlist * 1803get_ht() 1804{ 1805 struct hostlist *hp; 1806 1807 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 1808 if (hp == (struct hostlist *)NULL) 1809 out_of_mem(); 1810 hp->ht_next = (struct hostlist *)NULL; 1811 hp->ht_flag = 0; 1812 return (hp); 1813} 1814 1815/* 1816 * Out of memory, fatal 1817 */ 1818void 1819out_of_mem() 1820{ 1821 1822 syslog(LOG_ERR, "out of memory"); 1823 exit(2); 1824} 1825 1826/* 1827 * Do the nmount() syscall with the update flag to push the export info into 1828 * the kernel. 1829 */ 1830int 1831do_mount(struct exportlist *ep, struct grouplist *grp, int exflags, 1832 struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb) 1833{ 1834 struct statfs fsb1; 1835 struct addrinfo *ai; 1836 struct export_args eap; 1837 char errmsg[255]; 1838 char *cp; 1839 int done; 1840 char savedc; 1841 struct iovec *iov; 1842 int iovlen; 1843 int ret; 1844 1845 cp = NULL; 1846 savedc = '\0'; 1847 iov = NULL; 1848 iovlen = 0; 1849 ret = 0; 1850 1851 bzero(&eap, sizeof(eap)); 1852 bzero(errmsg, sizeof(errmsg)); 1853 eap.ex_flags = exflags; 1854 eap.ex_anon = *anoncrp; 1855 eap.ex_indexfile = ep->ex_indexfile; 1856 if (grp->gr_type == GT_HOST) 1857 ai = grp->gr_ptr.gt_addrinfo; 1858 else 1859 ai = NULL; 1860 done = FALSE; 1861 1862 build_iovec(&iov, &iovlen, "fstype", NULL, 0); 1863 build_iovec(&iov, &iovlen, "fspath", NULL, 0); 1864 build_iovec(&iov, &iovlen, "from", NULL, 0); 1865 build_iovec(&iov, &iovlen, "update", NULL, 0); 1866 build_iovec(&iov, &iovlen, "export", &eap, sizeof(eap)); 1867 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 1868 1869 while (!done) { 1870 switch (grp->gr_type) { 1871 case GT_HOST: 1872 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0) 1873 goto skip; 1874 eap.ex_addr = ai->ai_addr; 1875 eap.ex_addrlen = ai->ai_addrlen; 1876 eap.ex_masklen = 0; 1877 break; 1878 case GT_NET: 1879 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 && 1880 have_v6 == 0) 1881 goto skip; 1882 eap.ex_addr = 1883 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net; 1884 eap.ex_addrlen = 1885 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len; 1886 eap.ex_mask = 1887 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask; 1888 eap.ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len; 1889 break; 1890 case GT_DEFAULT: 1891 eap.ex_addr = NULL; 1892 eap.ex_addrlen = 0; 1893 eap.ex_mask = NULL; 1894 eap.ex_masklen = 0; 1895 break; 1896 case GT_IGNORE: 1897 ret = 0; 1898 goto error_exit; 1899 break; 1900 default: 1901 syslog(LOG_ERR, "bad grouptype"); 1902 if (cp) 1903 *cp = savedc; 1904 ret = 1; 1905 goto error_exit; 1906 }; 1907 1908 /* 1909 * XXX: 1910 * Maybe I should just use the fsb->f_mntonname path instead 1911 * of looping back up the dirp to the mount point?? 1912 * Also, needs to know how to export all types of local 1913 * exportable filesystems and not just "ufs". 1914 */ 1915 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */ 1916 iov[1].iov_len = strlen(fsb->f_fstypename) + 1; 1917 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */ 1918 iov[3].iov_len = strlen(fsb->f_mntonname) + 1; 1919 iov[5].iov_base = fsb->f_mntfromname; /* "from" */ 1920 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1; 1921 1922 /* 1923 * Remount the filesystem, but chop off the MNT_ROOTFS flag 1924 * as it is used internally (and will result in an error if 1925 * specified) 1926 */ 1927 while (nmount(iov, iovlen, fsb->f_flags & ~MNT_ROOTFS) < 0) { 1928 if (cp) 1929 *cp-- = savedc; 1930 else 1931 cp = dirp + dirplen - 1; 1932 if (opt_flags & OP_QUIET) { 1933 ret = 1; 1934 goto error_exit; 1935 } 1936 if (errno == EPERM) { 1937 if (debug) 1938 warnx("can't change attributes for %s", 1939 dirp); 1940 syslog(LOG_ERR, 1941 "can't change attributes for %s", dirp); 1942 ret = 1; 1943 goto error_exit; 1944 } 1945 if (opt_flags & OP_ALLDIRS) { 1946 if (errno == EINVAL) 1947 syslog(LOG_ERR, 1948 "-alldirs requested but %s is not a filesystem mountpoint", 1949 dirp); 1950 else 1951 syslog(LOG_ERR, 1952 "could not remount %s: %m", 1953 dirp); 1954 ret = 1; 1955 goto error_exit; 1956 } 1957 /* back up over the last component */ 1958 while (*cp == '/' && cp > dirp) 1959 cp--; 1960 while (*(cp - 1) != '/' && cp > dirp) 1961 cp--; 1962 if (cp == dirp) { 1963 if (debug) 1964 warnx("mnt unsucc"); 1965 syslog(LOG_ERR, "can't export %s", dirp); 1966 ret = 1; 1967 goto error_exit; 1968 } 1969 savedc = *cp; 1970 *cp = '\0'; 1971 /* Check that we're still on the same filesystem. */ 1972 if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid, 1973 &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) { 1974 *cp = savedc; 1975 syslog(LOG_ERR, "can't export %s", dirp); 1976 ret = 1; 1977 goto error_exit; 1978 } 1979 } 1980skip: 1981 if (ai != NULL) 1982 ai = ai->ai_next; 1983 if (ai == NULL) 1984 done = TRUE; 1985 } 1986 if (cp) 1987 *cp = savedc; 1988error_exit: 1989 /* free strings allocated by strdup() in getmntopts.c */ 1990 if (iov != NULL) { 1991 free(iov[0].iov_base); /* fstype */ 1992 free(iov[2].iov_base); /* fspath */ 1993 free(iov[4].iov_base); /* from */ 1994 free(iov[6].iov_base); /* update */ 1995 free(iov[8].iov_base); /* export */ 1996 free(iov[10].iov_base); /* errmsg */ 1997 1998 /* free iov, allocated by realloc() */ 1999 free(iov); 2000 } 2001 return (ret); 2002} 2003 2004/* 2005 * Translate a net address. 2006 * 2007 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address. 2008 */ 2009int 2010get_net(cp, net, maskflg) 2011 char *cp; 2012 struct netmsk *net; 2013 int maskflg; 2014{ 2015 struct netent *np = NULL; 2016 char *name, *p, *prefp; 2017 struct sockaddr_in sin; 2018 struct sockaddr *sa = NULL; 2019 struct addrinfo hints, *ai = NULL; 2020 char netname[NI_MAXHOST]; 2021 long preflen; 2022 2023 p = prefp = NULL; 2024 if ((opt_flags & OP_MASKLEN) && !maskflg) { 2025 p = strchr(cp, '/'); 2026 *p = '\0'; 2027 prefp = p + 1; 2028 } 2029 2030 /* 2031 * Check for a numeric address first. We wish to avoid 2032 * possible DNS lookups in getnetbyname(). 2033 */ 2034 if (isxdigit(*cp) || *cp == ':') { 2035 memset(&hints, 0, sizeof hints); 2036 /* Ensure the mask and the network have the same family. */ 2037 if (maskflg && (opt_flags & OP_NET)) 2038 hints.ai_family = net->nt_net.ss_family; 2039 else if (!maskflg && (opt_flags & OP_HAVEMASK)) 2040 hints.ai_family = net->nt_mask.ss_family; 2041 else 2042 hints.ai_family = AF_UNSPEC; 2043 hints.ai_flags = AI_NUMERICHOST; 2044 if (getaddrinfo(cp, NULL, &hints, &ai) == 0) 2045 sa = ai->ai_addr; 2046 if (sa != NULL && ai->ai_family == AF_INET) { 2047 /* 2048 * The address in `cp' is really a network address, so 2049 * use inet_network() to re-interpret this correctly. 2050 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1. 2051 */ 2052 bzero(&sin, sizeof sin); 2053 sin.sin_family = AF_INET; 2054 sin.sin_len = sizeof sin; 2055 sin.sin_addr = inet_makeaddr(inet_network(cp), 0); 2056 if (debug) 2057 fprintf(stderr, "get_net: v4 addr %s\n", 2058 inet_ntoa(sin.sin_addr)); 2059 sa = (struct sockaddr *)&sin; 2060 } 2061 } 2062 if (sa == NULL && (np = getnetbyname(cp)) != NULL) { 2063 bzero(&sin, sizeof sin); 2064 sin.sin_family = AF_INET; 2065 sin.sin_len = sizeof sin; 2066 sin.sin_addr = inet_makeaddr(np->n_net, 0); 2067 sa = (struct sockaddr *)&sin; 2068 } 2069 if (sa == NULL) 2070 goto fail; 2071 2072 if (maskflg) { 2073 /* The specified sockaddr is a mask. */ 2074 if (checkmask(sa) != 0) 2075 goto fail; 2076 bcopy(sa, &net->nt_mask, sa->sa_len); 2077 opt_flags |= OP_HAVEMASK; 2078 } else { 2079 /* The specified sockaddr is a network address. */ 2080 bcopy(sa, &net->nt_net, sa->sa_len); 2081 2082 /* Get a network name for the export list. */ 2083 if (np) { 2084 name = np->n_name; 2085 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, 2086 NULL, 0, NI_NUMERICHOST) == 0) { 2087 name = netname; 2088 } else { 2089 goto fail; 2090 } 2091 if ((net->nt_name = strdup(name)) == NULL) 2092 out_of_mem(); 2093 2094 /* 2095 * Extract a mask from either a "/<masklen>" suffix, or 2096 * from the class of an IPv4 address. 2097 */ 2098 if (opt_flags & OP_MASKLEN) { 2099 preflen = strtol(prefp, NULL, 10); 2100 if (preflen < 0L || preflen == LONG_MAX) 2101 goto fail; 2102 bcopy(sa, &net->nt_mask, sa->sa_len); 2103 if (makemask(&net->nt_mask, (int)preflen) != 0) 2104 goto fail; 2105 opt_flags |= OP_HAVEMASK; 2106 *p = '/'; 2107 } else if (sa->sa_family == AF_INET && 2108 (opt_flags & OP_MASK) == 0) { 2109 in_addr_t addr; 2110 2111 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 2112 if (IN_CLASSA(addr)) 2113 preflen = 8; 2114 else if (IN_CLASSB(addr)) 2115 preflen = 16; 2116 else if (IN_CLASSC(addr)) 2117 preflen = 24; 2118 else if (IN_CLASSD(addr)) 2119 preflen = 28; 2120 else 2121 preflen = 32; /* XXX */ 2122 2123 bcopy(sa, &net->nt_mask, sa->sa_len); 2124 makemask(&net->nt_mask, (int)preflen); 2125 opt_flags |= OP_HAVEMASK; 2126 } 2127 } 2128 2129 if (ai) 2130 freeaddrinfo(ai); 2131 return 0; 2132 2133fail: 2134 if (ai) 2135 freeaddrinfo(ai); 2136 return 1; 2137} 2138 2139/* 2140 * Parse out the next white space separated field 2141 */ 2142void 2143nextfield(cp, endcp) 2144 char **cp; 2145 char **endcp; 2146{ 2147 char *p; 2148 2149 p = *cp; 2150 while (*p == ' ' || *p == '\t') 2151 p++; 2152 if (*p == '\n' || *p == '\0') 2153 *cp = *endcp = p; 2154 else { 2155 *cp = p++; 2156 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 2157 p++; 2158 *endcp = p; 2159 } 2160} 2161 2162/* 2163 * Get an exports file line. Skip over blank lines and handle line 2164 * continuations. 2165 */ 2166int 2167get_line() 2168{ 2169 char *p, *cp; 2170 size_t len; 2171 int totlen, cont_line; 2172 2173 /* 2174 * Loop around ignoring blank lines and getting all continuation lines. 2175 */ 2176 p = line; 2177 totlen = 0; 2178 do { 2179 if ((p = fgetln(exp_file, &len)) == NULL) 2180 return (0); 2181 cp = p + len - 1; 2182 cont_line = 0; 2183 while (cp >= p && 2184 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) { 2185 if (*cp == '\\') 2186 cont_line = 1; 2187 cp--; 2188 len--; 2189 } 2190 if (cont_line) { 2191 *++cp = ' '; 2192 len++; 2193 } 2194 if (linesize < len + totlen + 1) { 2195 linesize = len + totlen + 1; 2196 line = realloc(line, linesize); 2197 if (line == NULL) 2198 out_of_mem(); 2199 } 2200 memcpy(line + totlen, p, len); 2201 totlen += len; 2202 line[totlen] = '\0'; 2203 } while (totlen == 0 || cont_line); 2204 return (1); 2205} 2206 2207/* 2208 * Parse a description of a credential. 2209 */ 2210void 2211parsecred(namelist, cr) 2212 char *namelist; 2213 struct xucred *cr; 2214{ 2215 char *name; 2216 int cnt; 2217 char *names; 2218 struct passwd *pw; 2219 struct group *gr; 2220 gid_t groups[NGROUPS + 1]; 2221 int ngroups; 2222 2223 cr->cr_version = XUCRED_VERSION; 2224 /* 2225 * Set up the unprivileged user. 2226 */ 2227 cr->cr_uid = -2; 2228 cr->cr_groups[0] = -2; 2229 cr->cr_ngroups = 1; 2230 /* 2231 * Get the user's password table entry. 2232 */ 2233 names = strsep(&namelist, " \t\n"); 2234 name = strsep(&names, ":"); 2235 if (isdigit(*name) || *name == '-') 2236 pw = getpwuid(atoi(name)); 2237 else 2238 pw = getpwnam(name); 2239 /* 2240 * Credentials specified as those of a user. 2241 */ 2242 if (names == NULL) { 2243 if (pw == NULL) { 2244 syslog(LOG_ERR, "unknown user: %s", name); 2245 return; 2246 } 2247 cr->cr_uid = pw->pw_uid; 2248 ngroups = NGROUPS + 1; 2249 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 2250 syslog(LOG_ERR, "too many groups"); 2251 /* 2252 * Compress out duplicate. 2253 */ 2254 cr->cr_ngroups = ngroups - 1; 2255 cr->cr_groups[0] = groups[0]; 2256 for (cnt = 2; cnt < ngroups; cnt++) 2257 cr->cr_groups[cnt - 1] = groups[cnt]; 2258 return; 2259 } 2260 /* 2261 * Explicit credential specified as a colon separated list: 2262 * uid:gid:gid:... 2263 */ 2264 if (pw != NULL) 2265 cr->cr_uid = pw->pw_uid; 2266 else if (isdigit(*name) || *name == '-') 2267 cr->cr_uid = atoi(name); 2268 else { 2269 syslog(LOG_ERR, "unknown user: %s", name); 2270 return; 2271 } 2272 cr->cr_ngroups = 0; 2273 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 2274 name = strsep(&names, ":"); 2275 if (isdigit(*name) || *name == '-') { 2276 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 2277 } else { 2278 if ((gr = getgrnam(name)) == NULL) { 2279 syslog(LOG_ERR, "unknown group: %s", name); 2280 continue; 2281 } 2282 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 2283 } 2284 } 2285 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 2286 syslog(LOG_ERR, "too many groups"); 2287} 2288 2289#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 2290/* 2291 * Routines that maintain the remote mounttab 2292 */ 2293void 2294get_mountlist() 2295{ 2296 struct mountlist *mlp, **mlpp; 2297 char *host, *dirp, *cp; 2298 char str[STRSIZ]; 2299 FILE *mlfile; 2300 2301 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 2302 if (errno == ENOENT) 2303 return; 2304 else { 2305 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST); 2306 return; 2307 } 2308 } 2309 mlpp = &mlhead; 2310 while (fgets(str, STRSIZ, mlfile) != NULL) { 2311 cp = str; 2312 host = strsep(&cp, " \t\n"); 2313 dirp = strsep(&cp, " \t\n"); 2314 if (host == NULL || dirp == NULL) 2315 continue; 2316 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2317 if (mlp == (struct mountlist *)NULL) 2318 out_of_mem(); 2319 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN); 2320 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2321 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2322 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2323 mlp->ml_next = (struct mountlist *)NULL; 2324 *mlpp = mlp; 2325 mlpp = &mlp->ml_next; 2326 } 2327 fclose(mlfile); 2328} 2329 2330void 2331del_mlist(char *hostp, char *dirp) 2332{ 2333 struct mountlist *mlp, **mlpp; 2334 struct mountlist *mlp2; 2335 FILE *mlfile; 2336 int fnd = 0; 2337 2338 mlpp = &mlhead; 2339 mlp = mlhead; 2340 while (mlp) { 2341 if (!strcmp(mlp->ml_host, hostp) && 2342 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 2343 fnd = 1; 2344 mlp2 = mlp; 2345 *mlpp = mlp = mlp->ml_next; 2346 free((caddr_t)mlp2); 2347 } else { 2348 mlpp = &mlp->ml_next; 2349 mlp = mlp->ml_next; 2350 } 2351 } 2352 if (fnd) { 2353 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 2354 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST); 2355 return; 2356 } 2357 mlp = mlhead; 2358 while (mlp) { 2359 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2360 mlp = mlp->ml_next; 2361 } 2362 fclose(mlfile); 2363 } 2364} 2365 2366void 2367add_mlist(hostp, dirp) 2368 char *hostp, *dirp; 2369{ 2370 struct mountlist *mlp, **mlpp; 2371 FILE *mlfile; 2372 2373 mlpp = &mlhead; 2374 mlp = mlhead; 2375 while (mlp) { 2376 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 2377 return; 2378 mlpp = &mlp->ml_next; 2379 mlp = mlp->ml_next; 2380 } 2381 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 2382 if (mlp == (struct mountlist *)NULL) 2383 out_of_mem(); 2384 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN); 2385 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2386 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2387 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2388 mlp->ml_next = (struct mountlist *)NULL; 2389 *mlpp = mlp; 2390 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 2391 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST); 2392 return; 2393 } 2394 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2395 fclose(mlfile); 2396} 2397 2398/* 2399 * Free up a group list. 2400 */ 2401void 2402free_grp(grp) 2403 struct grouplist *grp; 2404{ 2405 if (grp->gr_type == GT_HOST) { 2406 if (grp->gr_ptr.gt_addrinfo != NULL) 2407 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 2408 } else if (grp->gr_type == GT_NET) { 2409 if (grp->gr_ptr.gt_net.nt_name) 2410 free(grp->gr_ptr.gt_net.nt_name); 2411 } 2412 free((caddr_t)grp); 2413} 2414 2415#ifdef DEBUG 2416void 2417SYSLOG(int pri, const char *fmt, ...) 2418{ 2419 va_list ap; 2420 2421 va_start(ap, fmt); 2422 vfprintf(stderr, fmt, ap); 2423 va_end(ap); 2424} 2425#endif /* DEBUG */ 2426 2427/* 2428 * Check options for consistency. 2429 */ 2430int 2431check_options(dp) 2432 struct dirlist *dp; 2433{ 2434 2435 if (dp == (struct dirlist *)NULL) 2436 return (1); 2437 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 2438 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 2439 return (1); 2440 } 2441 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 2442 syslog(LOG_ERR, "-mask requires -network"); 2443 return (1); 2444 } 2445 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) { 2446 syslog(LOG_ERR, "-network requires mask specification"); 2447 return (1); 2448 } 2449 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) { 2450 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive"); 2451 return (1); 2452 } 2453 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 2454 syslog(LOG_ERR, "-alldirs has multiple directories"); 2455 return (1); 2456 } 2457 return (0); 2458} 2459 2460/* 2461 * Check an absolute directory path for any symbolic links. Return true 2462 */ 2463int 2464check_dirpath(dirp) 2465 char *dirp; 2466{ 2467 char *cp; 2468 int ret = 1; 2469 struct stat sb; 2470 2471 cp = dirp + 1; 2472 while (*cp && ret) { 2473 if (*cp == '/') { 2474 *cp = '\0'; 2475 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2476 ret = 0; 2477 *cp = '/'; 2478 } 2479 cp++; 2480 } 2481 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2482 ret = 0; 2483 return (ret); 2484} 2485 2486/* 2487 * Make a netmask according to the specified prefix length. The ss_family 2488 * and other non-address fields must be initialised before calling this. 2489 */ 2490int 2491makemask(struct sockaddr_storage *ssp, int bitlen) 2492{ 2493 u_char *p; 2494 int bits, i, len; 2495 2496 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL) 2497 return (-1); 2498 if (bitlen > len * CHAR_BIT) 2499 return (-1); 2500 2501 for (i = 0; i < len; i++) { 2502 bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen; 2503 *p++ = (1 << bits) - 1; 2504 bitlen -= bits; 2505 } 2506 return 0; 2507} 2508 2509/* 2510 * Check that the sockaddr is a valid netmask. Returns 0 if the mask 2511 * is acceptable (i.e. of the form 1...10....0). 2512 */ 2513int 2514checkmask(struct sockaddr *sa) 2515{ 2516 u_char *mask; 2517 int i, len; 2518 2519 if ((mask = sa_rawaddr(sa, &len)) == NULL) 2520 return (-1); 2521 2522 for (i = 0; i < len; i++) 2523 if (mask[i] != 0xff) 2524 break; 2525 if (i < len) { 2526 if (~mask[i] & (u_char)(~mask[i] + 1)) 2527 return (-1); 2528 i++; 2529 } 2530 for (; i < len; i++) 2531 if (mask[i] != 0) 2532 return (-1); 2533 return (0); 2534} 2535 2536/* 2537 * Compare two sockaddrs according to a specified mask. Return zero if 2538 * `sa1' matches `sa2' when filtered by the netmask in `samask'. 2539 * If samask is NULL, perform a full comparision. 2540 */ 2541int 2542sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask) 2543{ 2544 unsigned char *p1, *p2, *mask; 2545 int len, i; 2546 2547 if (sa1->sa_family != sa2->sa_family || 2548 (p1 = sa_rawaddr(sa1, &len)) == NULL || 2549 (p2 = sa_rawaddr(sa2, NULL)) == NULL) 2550 return (1); 2551 2552 switch (sa1->sa_family) { 2553 case AF_INET6: 2554 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 2555 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 2556 return (1); 2557 break; 2558 } 2559 2560 /* Simple binary comparison if no mask specified. */ 2561 if (samask == NULL) 2562 return (memcmp(p1, p2, len)); 2563 2564 /* Set up the mask, and do a mask-based comparison. */ 2565 if (sa1->sa_family != samask->sa_family || 2566 (mask = sa_rawaddr(samask, NULL)) == NULL) 2567 return (1); 2568 2569 for (i = 0; i < len; i++) 2570 if ((p1[i] & mask[i]) != (p2[i] & mask[i])) 2571 return (1); 2572 return (0); 2573} 2574 2575/* 2576 * Return a pointer to the part of the sockaddr that contains the 2577 * raw address, and set *nbytes to its length in bytes. Returns 2578 * NULL if the address family is unknown. 2579 */ 2580void * 2581sa_rawaddr(struct sockaddr *sa, int *nbytes) { 2582 void *p; 2583 int len; 2584 2585 switch (sa->sa_family) { 2586 case AF_INET: 2587 len = sizeof(((struct sockaddr_in *)sa)->sin_addr); 2588 p = &((struct sockaddr_in *)sa)->sin_addr; 2589 break; 2590 case AF_INET6: 2591 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); 2592 p = &((struct sockaddr_in6 *)sa)->sin6_addr; 2593 break; 2594 default: 2595 p = NULL; 2596 len = 0; 2597 } 2598 2599 if (nbytes != NULL) 2600 *nbytes = len; 2601 return (p); 2602} 2603 2604void 2605huphandler(int sig) 2606{ 2607 got_sighup = 1; 2608} 2609 2610void terminate(sig) 2611int sig; 2612{ 2613 pidfile_remove(pfh); 2614 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); 2615 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); 2616 exit (0); 2617} 2618