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