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