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