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