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