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