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