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