1 /* $OpenBSD: mountd.c,v 1.71 2010/03/22 16:35:27 otto Exp $ */ 2 /* $NetBSD: mountd.c,v 1.31 1996/02/18 11:57:53 fvdl Exp $ */ 3 4 /* 5 * Copyright (c) 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Herb Hasler and Rick Macklem at The University of Guelph. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/file.h> 38 #include <sys/ioctl.h> 39 #include <sys/mount.h> 40 #include <sys/socket.h> 41 #include <sys/stat.h> 42 #include <syslog.h> 43 #include <sys/ucred.h> 44 45 #include <rpc/rpc.h> 46 #include <rpc/pmap_clnt.h> 47 #include <rpc/pmap_prot.h> 48 #include <nfs/rpcv2.h> 49 #include <nfs/nfsproto.h> 50 51 #include <arpa/inet.h> 52 53 #include <ctype.h> 54 #include <errno.h> 55 #include <grp.h> 56 #include <netdb.h> 57 #include <netgroup.h> 58 #include <pwd.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #include "pathnames.h" 65 66 #include <stdarg.h> 67 68 /* 69 * Structures for keeping the mount list and export list 70 */ 71 struct mountlist { 72 struct mountlist *ml_next; 73 char ml_host[RPCMNT_NAMELEN+1]; 74 char ml_dirp[RPCMNT_PATHLEN+1]; 75 }; 76 77 struct dirlist { 78 struct dirlist *dp_left; 79 struct dirlist *dp_right; 80 int dp_flag; 81 struct hostlist *dp_hosts; /* List of hosts this dir exported to */ 82 char dp_dirp[1]; /* Actually malloc'd to size of dir */ 83 }; 84 /* dp_flag bits */ 85 #define DP_DEFSET 0x1 86 #define DP_HOSTSET 0x2 87 88 struct exportlist { 89 struct exportlist *ex_next; 90 struct dirlist *ex_dirl; 91 struct dirlist *ex_defdir; 92 int ex_flag; 93 fsid_t ex_fs; 94 char *ex_fsdir; 95 }; 96 /* ex_flag bits */ 97 #define EX_LINKED 0x1 98 99 struct netmsk { 100 in_addr_t nt_net; 101 in_addr_t nt_mask; 102 char *nt_name; 103 }; 104 105 union grouptypes { 106 struct hostent *gt_hostent; 107 struct netmsk gt_net; 108 }; 109 110 struct grouplist { 111 int gr_type; 112 union grouptypes gr_ptr; 113 struct grouplist *gr_next; 114 }; 115 /* Group types */ 116 #define GT_NULL 0x0 117 #define GT_HOST 0x1 118 #define GT_NET 0x2 119 #define GT_IGNORE 0x5 120 121 struct hostlist { 122 int ht_flag; /* Uses DP_xx bits */ 123 struct grouplist *ht_grp; 124 struct hostlist *ht_next; 125 }; 126 127 struct fhreturn { 128 int fhr_flag; 129 int fhr_vers; 130 nfsfh_t fhr_fh; 131 }; 132 133 /* Global defs */ 134 char *add_expdir(struct dirlist **, char *, int); 135 void add_dlist(struct dirlist **, struct dirlist *, struct grouplist *, int); 136 void add_mlist(char *, char *); 137 int check_dirpath(char *); 138 int check_options(struct dirlist *); 139 int chk_host(struct dirlist *, in_addr_t, int *, int *); 140 void del_mlist(char *, char *); 141 struct dirlist *dirp_search(struct dirlist *, char *); 142 int do_mount(struct exportlist *, struct grouplist *, int, struct ucred *, 143 char *, int, struct statfs *); 144 int do_opt(char **, char **, struct exportlist *, struct grouplist *, 145 int *, int *, struct ucred *); 146 struct exportlist *ex_search(fsid_t *); 147 struct exportlist *get_exp(void); 148 void free_dir(struct dirlist *); 149 void free_exp(struct exportlist *); 150 void free_grp(struct grouplist *); 151 void free_host(struct hostlist *); 152 void new_exportlist(int signo); 153 void get_exportlist(void); 154 int get_host(char *, struct grouplist *, struct grouplist *); 155 int get_num(char *); 156 struct hostlist *get_ht(void); 157 int get_line(void); 158 void get_mountlist(void); 159 int get_net(char *, struct netmsk *, int); 160 void getexp_err(struct exportlist *, struct grouplist *); 161 struct grouplist *get_grp(void); 162 void hang_dirp(struct dirlist *, struct grouplist *, struct exportlist *, 163 int); 164 void mntsrv(struct svc_req *, SVCXPRT *); 165 void nextfield(char **, char **); 166 void out_of_mem(void); 167 void parsecred(char *, struct ucred *); 168 int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *); 169 int scan_tree(struct dirlist *, in_addr_t); 170 void send_umntall(int signo); 171 int umntall_each(caddr_t, struct sockaddr_in *); 172 int xdr_dir(XDR *, char *); 173 int xdr_explist(XDR *, caddr_t); 174 int xdr_fhs(XDR *, caddr_t); 175 int xdr_mlist(XDR *, caddr_t); 176 void mountd_svc_run(void); 177 178 struct exportlist *exphead; 179 struct mountlist *mlhead; 180 struct grouplist *grphead; 181 char exname[MAXPATHLEN]; 182 struct ucred def_anon = { 183 1, 184 (uid_t) -2, 185 (gid_t) -2, 186 0, 187 { 0, } 188 }; 189 int resvport_only = 1; 190 int opt_flags; 191 /* Bits for above */ 192 #define OP_MAPROOT 0x01 193 #define OP_MAPALL 0x02 194 #define OP_MASK 0x08 195 #define OP_NET 0x10 196 #define OP_ALLDIRS 0x40 197 198 int debug = 0; 199 200 volatile sig_atomic_t gothup; 201 volatile sig_atomic_t gotterm; 202 203 /* 204 * Mountd server for NFS mount protocol as described in: 205 * NFS: Network File System Protocol Specification, RFC1094, Appendix A 206 * The optional arguments are the exports file name 207 * default: _PATH_EXPORTS 208 * "-d" to enable debugging 209 * and "-n" to allow nonroot mount. 210 */ 211 int 212 main(int argc, char *argv[]) 213 { 214 SVCXPRT *udptransp, *tcptransp; 215 FILE *pidfile; 216 int c; 217 218 while ((c = getopt(argc, argv, "dnr")) != -1) 219 switch (c) { 220 case 'd': 221 debug = 1; 222 break; 223 case 'n': 224 resvport_only = 0; 225 break; 226 case 'r': 227 /* Compatibility */ 228 break; 229 default: 230 fprintf(stderr, "usage: mountd [-dn] [exportsfile]\n"); 231 exit(1); 232 } 233 argc -= optind; 234 argv += optind; 235 grphead = NULL; 236 exphead = NULL; 237 mlhead = NULL; 238 239 strlcpy(exname, argc == 1? *argv : _PATH_EXPORTS, sizeof(exname)); 240 241 openlog("mountd", LOG_PID, LOG_DAEMON); 242 if (debug) 243 fprintf(stderr, "Getting export list.\n"); 244 get_exportlist(); 245 if (debug) 246 fprintf(stderr, "Getting mount list.\n"); 247 get_mountlist(); 248 if (debug) 249 fprintf(stderr, "Here we go.\n"); 250 if (debug == 0) { 251 daemon(0, 0); 252 signal(SIGINT, SIG_IGN); 253 signal(SIGQUIT, SIG_IGN); 254 } 255 /* Store pid in file unless mountd is already running */ 256 pidfile = fopen(_PATH_MOUNTDPID, "r"); 257 if (pidfile != NULL) { 258 if (fscanf(pidfile, "%d\n", &c) > 0 && c > 0) { 259 if (kill(c, 0) == 0) { 260 syslog(LOG_ERR, "Already running (pid %d)", c); 261 exit(1); 262 } 263 } 264 pidfile = freopen(_PATH_MOUNTDPID, "w", pidfile); 265 } else { 266 pidfile = fopen(_PATH_MOUNTDPID, "w"); 267 } 268 if (pidfile) { 269 fprintf(pidfile, "%ld\n", (long)getpid()); 270 fclose(pidfile); 271 } 272 273 signal(SIGHUP, (void (*)(int)) new_exportlist); 274 signal(SIGTERM, (void (*)(int)) send_umntall); 275 signal(SIGSYS, SIG_IGN); 276 if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL || 277 (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) { 278 syslog(LOG_ERR, "Can't create socket"); 279 exit(1); 280 } 281 pmap_unset(RPCPROG_MNT, RPCMNT_VER1); 282 pmap_unset(RPCPROG_MNT, RPCMNT_VER3); 283 if (!svc_register(udptransp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP) || 284 !svc_register(udptransp, RPCPROG_MNT, RPCMNT_VER3, mntsrv, IPPROTO_UDP) || 285 !svc_register(tcptransp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_TCP) || 286 !svc_register(tcptransp, RPCPROG_MNT, RPCMNT_VER3, mntsrv, IPPROTO_TCP)) { 287 syslog(LOG_ERR, "Can't register mount"); 288 exit(1); 289 } 290 mountd_svc_run(); 291 syslog(LOG_ERR, "Mountd died"); 292 exit(1); 293 } 294 295 void 296 mountd_svc_run(void) 297 { 298 fd_set *fds = NULL; 299 int fds_size = 0; 300 extern fd_set *__svc_fdset; 301 extern int __svc_fdsetsize; 302 303 for (;;) { 304 if (gothup) { 305 get_exportlist(); 306 gothup = 0; 307 } 308 if (gotterm) { 309 (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, 310 RPCMNT_UMNTALL, xdr_void, (caddr_t)0, xdr_void, 311 (caddr_t)0, umntall_each); 312 exit(0); 313 } 314 if (__svc_fdset) { 315 int bytes = howmany(__svc_fdsetsize, NFDBITS) * 316 sizeof(fd_mask); 317 if (fds_size != __svc_fdsetsize) { 318 if (fds) 319 free(fds); 320 fds = (fd_set *)malloc(bytes); /* XXX */ 321 fds_size = __svc_fdsetsize; 322 } 323 memcpy(fds, __svc_fdset, bytes); 324 } else { 325 if (fds) 326 free(fds); 327 fds = NULL; 328 } 329 switch (select(svc_maxfd+1, fds, 0, 0, (struct timeval *)0)) { 330 case -1: 331 if (errno == EINTR) 332 break; 333 perror("mountd_svc_run: - select failed"); 334 if (fds) 335 free(fds); 336 return; 337 case 0: 338 break; 339 default: 340 svc_getreqset2(fds, svc_maxfd+1); 341 break; 342 } 343 } 344 } 345 346 /* 347 * The mount rpc service 348 */ 349 void 350 mntsrv(struct svc_req *rqstp, SVCXPRT *transp) 351 { 352 char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN]; 353 struct hostent *hp = NULL; 354 struct exportlist *ep; 355 sigset_t sighup_mask; 356 int defset, hostset; 357 struct fhreturn fhr; 358 struct dirlist *dp; 359 struct statfs fsb; 360 struct stat stb; 361 in_addr_t saddr; 362 u_short sport; 363 long bad = 0; 364 365 sigemptyset(&sighup_mask); 366 sigaddset(&sighup_mask, SIGHUP); 367 saddr = transp->xp_raddr.sin_addr.s_addr; 368 sport = ntohs(transp->xp_raddr.sin_port); 369 switch (rqstp->rq_proc) { 370 case NULLPROC: 371 if (!svc_sendreply(transp, xdr_void, NULL)) 372 syslog(LOG_ERR, "Can't send reply"); 373 return; 374 case RPCMNT_MOUNT: 375 if (debug) 376 fprintf(stderr, "Got mount request from %s\n", 377 inet_ntoa(transp->xp_raddr.sin_addr)); 378 if (sport >= IPPORT_RESERVED && resvport_only) { 379 syslog(LOG_NOTICE, 380 "Refused mount RPC from host %s port %d", 381 inet_ntoa(transp->xp_raddr.sin_addr), sport); 382 svcerr_weakauth(transp); 383 return; 384 } 385 if (!svc_getargs(transp, xdr_dir, rpcpath)) { 386 svcerr_decode(transp); 387 return; 388 } 389 if (debug) 390 fprintf(stderr, "rpcpath: %s\n", rpcpath); 391 392 /* 393 * Get the real pathname and make sure it is a file or 394 * directory that exists. 395 */ 396 if (realpath(rpcpath, dirpath) == NULL) { 397 bad = errno; 398 if (debug) 399 fprintf(stderr, "realpath failed on %s\n", 400 rpcpath); 401 strlcpy(dirpath, rpcpath, sizeof(dirpath)); 402 } else if (stat(dirpath, &stb) < 0 || 403 (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) || 404 statfs(dirpath, &fsb) < 0) { 405 if (debug) 406 fprintf(stderr, "stat failed on %s\n", dirpath); 407 bad = ENOENT; /* We will send error reply later */ 408 } 409 410 /* Check in the exports list */ 411 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 412 ep = ex_search(&fsb.f_fsid); 413 hostset = defset = 0; 414 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || 415 ((dp = dirp_search(ep->ex_dirl, dirpath)) && 416 chk_host(dp, saddr, &defset, &hostset)) || 417 (defset && scan_tree(ep->ex_defdir, saddr) == 0 && 418 scan_tree(ep->ex_dirl, saddr) == 0))) { 419 if (bad) { 420 if (!svc_sendreply(transp, xdr_long, 421 (caddr_t)&bad)) 422 syslog(LOG_ERR, "Can't send reply"); 423 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 424 return; 425 } 426 if (hostset & DP_HOSTSET) 427 fhr.fhr_flag = hostset; 428 else 429 fhr.fhr_flag = defset; 430 fhr.fhr_vers = rqstp->rq_vers; 431 /* Get the file handle */ 432 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); 433 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { 434 if (errno == ENOSYS) { 435 syslog(LOG_ERR, 436 "Kernel does not support NFS exporting, " 437 "mountd aborting.."); 438 _exit(1); 439 } 440 bad = errno; 441 syslog(LOG_ERR, "Can't get fh for %s", dirpath); 442 if (!svc_sendreply(transp, xdr_long, 443 (caddr_t)&bad)) 444 syslog(LOG_ERR, "Can't send reply"); 445 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 446 return; 447 } 448 if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) 449 syslog(LOG_ERR, "Can't send reply"); 450 if (hp == NULL) 451 hp = gethostbyaddr((caddr_t)&saddr, 452 sizeof(saddr), AF_INET); 453 if (hp) 454 add_mlist(hp->h_name, dirpath); 455 else 456 add_mlist(inet_ntoa(transp->xp_raddr.sin_addr), 457 dirpath); 458 if (debug) { 459 fprintf(stderr, 460 "Mount successful for %s by %s.\n", 461 dirpath, 462 inet_ntoa(transp->xp_raddr.sin_addr)); 463 } 464 } else 465 bad = EACCES; 466 467 if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad)) 468 syslog(LOG_ERR, "Can't send reply"); 469 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 470 return; 471 case RPCMNT_DUMP: 472 if (!svc_sendreply(transp, xdr_mlist, NULL)) 473 syslog(LOG_ERR, "Can't send reply"); 474 return; 475 case RPCMNT_UMOUNT: 476 if (sport >= IPPORT_RESERVED && resvport_only) { 477 svcerr_weakauth(transp); 478 return; 479 } 480 if (!svc_getargs(transp, xdr_dir, dirpath)) { 481 svcerr_decode(transp); 482 return; 483 } 484 if (!svc_sendreply(transp, xdr_void, NULL)) 485 syslog(LOG_ERR, "Can't send reply"); 486 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); 487 if (hp) 488 del_mlist(hp->h_name, dirpath); 489 del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath); 490 return; 491 case RPCMNT_UMNTALL: 492 if (sport >= IPPORT_RESERVED && resvport_only) { 493 svcerr_weakauth(transp); 494 return; 495 } 496 if (!svc_sendreply(transp, xdr_void, NULL)) 497 syslog(LOG_ERR, "Can't send reply"); 498 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); 499 if (hp) 500 del_mlist(hp->h_name, NULL); 501 del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), NULL); 502 return; 503 case RPCMNT_EXPORT: 504 if (!svc_sendreply(transp, xdr_explist, NULL)) 505 syslog(LOG_ERR, "Can't send reply"); 506 return; 507 default: 508 svcerr_noproc(transp); 509 return; 510 } 511 } 512 513 /* 514 * Xdr conversion for a dirpath string 515 */ 516 int 517 xdr_dir(XDR *xdrsp, char *dirp) 518 { 519 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 520 } 521 522 /* 523 * Xdr routine to generate file handle reply 524 */ 525 int 526 xdr_fhs(XDR *xdrsp, caddr_t cp) 527 { 528 struct fhreturn *fhrp = (struct fhreturn *)cp; 529 long ok = 0, len, auth; 530 531 if (!xdr_long(xdrsp, &ok)) 532 return (0); 533 switch (fhrp->fhr_vers) { 534 case 1: 535 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); 536 case 3: 537 len = NFSX_V3FH; 538 if (!xdr_long(xdrsp, &len)) 539 return (0); 540 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) 541 return (0); 542 auth = RPCAUTH_UNIX; 543 len = 1; 544 if (!xdr_long(xdrsp, &len)) 545 return (0); 546 return (xdr_long(xdrsp, &auth)); 547 } 548 return (0); 549 } 550 551 int 552 xdr_mlist(XDR *xdrsp, caddr_t cp) 553 { 554 int true = 1, false = 0; 555 struct mountlist *mlp; 556 char *strp; 557 558 mlp = mlhead; 559 while (mlp) { 560 if (!xdr_bool(xdrsp, &true)) 561 return (0); 562 strp = &mlp->ml_host[0]; 563 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) 564 return (0); 565 strp = &mlp->ml_dirp[0]; 566 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 567 return (0); 568 mlp = mlp->ml_next; 569 } 570 if (!xdr_bool(xdrsp, &false)) 571 return (0); 572 return (1); 573 } 574 575 /* 576 * Xdr conversion for export list 577 */ 578 int 579 xdr_explist(XDR *xdrsp, caddr_t cp) 580 { 581 struct exportlist *ep; 582 int false = 0, putdef; 583 sigset_t sighup_mask; 584 585 sigemptyset(&sighup_mask); 586 sigaddset(&sighup_mask, SIGHUP); 587 sigprocmask(SIG_BLOCK, &sighup_mask, NULL); 588 ep = exphead; 589 while (ep) { 590 putdef = 0; 591 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef)) 592 goto errout; 593 if (ep->ex_defdir && putdef == 0 && put_exlist(ep->ex_defdir, 594 xdrsp, NULL, &putdef)) 595 goto errout; 596 ep = ep->ex_next; 597 } 598 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 599 if (!xdr_bool(xdrsp, &false)) 600 return (0); 601 return (1); 602 errout: 603 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); 604 return (0); 605 } 606 607 /* 608 * Called from xdr_explist() to traverse the tree and export the 609 * directory paths. 610 */ 611 int 612 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, 613 int *putdefp) 614 { 615 int true = 1, false = 0, gotalldir = 0; 616 struct grouplist *grp; 617 struct hostlist *hp; 618 char *strp; 619 620 if (dp) { 621 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp)) 622 return (1); 623 if (!xdr_bool(xdrsp, &true)) 624 return (1); 625 strp = dp->dp_dirp; 626 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) 627 return (1); 628 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { 629 gotalldir = 1; 630 *putdefp = 1; 631 } 632 if ((dp->dp_flag & DP_DEFSET) == 0 && 633 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { 634 hp = dp->dp_hosts; 635 while (hp) { 636 grp = hp->ht_grp; 637 if (grp->gr_type == GT_HOST) { 638 if (!xdr_bool(xdrsp, &true)) 639 return (1); 640 strp = grp->gr_ptr.gt_hostent->h_name; 641 if (!xdr_string(xdrsp, &strp, 642 RPCMNT_NAMELEN)) 643 return (1); 644 } else if (grp->gr_type == GT_NET) { 645 if (!xdr_bool(xdrsp, &true)) 646 return (1); 647 strp = grp->gr_ptr.gt_net.nt_name; 648 if (!xdr_string(xdrsp, &strp, 649 RPCMNT_NAMELEN)) 650 return (1); 651 } 652 hp = hp->ht_next; 653 if (gotalldir && hp == NULL) { 654 hp = adp->dp_hosts; 655 gotalldir = 0; 656 } 657 } 658 } 659 if (!xdr_bool(xdrsp, &false)) 660 return (1); 661 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp)) 662 return (1); 663 } 664 return (0); 665 } 666 667 #define LINESIZ 10240 668 char line[LINESIZ]; 669 FILE *exp_file; 670 671 void 672 new_exportlist(int signo) 673 { 674 gothup = 1; 675 676 } 677 678 /* 679 * Get the export list 680 */ 681 void 682 get_exportlist(void) 683 { 684 int len, has_host, exflags, got_nondir, dirplen = 0, num; 685 int lookup_failed, num_hosts, i, netgrp; 686 char *cp, *endcp, *dirp = NULL, *hst, *usr, *dom, savedc; 687 struct exportlist *ep, *ep2; 688 struct grouplist *grp, *tgrp; 689 struct exportlist **epp; 690 struct dirlist *dirhead; 691 struct statfs fsb, *ofsp, *fsp; 692 struct hostent *hpe; 693 struct ucred anon; 694 union { 695 struct ufs_args ua; 696 struct iso_args ia; 697 struct mfs_args ma; 698 struct msdosfs_args da; 699 } targs; 700 struct fsarray { 701 int exflags; 702 char *mntonname; 703 } *fstbl; 704 705 /* 706 * First, get rid of the old list 707 */ 708 ep = exphead; 709 while (ep) { 710 ep2 = ep; 711 ep = ep->ex_next; 712 free_exp(ep2); 713 } 714 exphead = NULL; 715 716 grp = grphead; 717 while (grp) { 718 tgrp = grp; 719 grp = grp->gr_next; 720 free_grp(tgrp); 721 } 722 grphead = NULL; 723 724 /* 725 * And delete exports that are in the kernel for all local 726 * file systems. 727 * XXX: Should know how to handle all local exportable file systems 728 * instead of just MOUNT_FFS. 729 */ 730 num = getmntinfo(&ofsp, MNT_NOWAIT); 731 if (num == 0 && errno) 732 syslog(LOG_ERR, "getmntinfo: %s", strerror(errno)); 733 734 fsp = ofsp; 735 736 fstbl = calloc(num, sizeof (fstbl[0])); 737 if (fstbl == NULL) 738 out_of_mem(); 739 740 for (i = 0; i < num; i++) { 741 742 if (!strncmp(fsp->f_fstypename, MOUNT_MFS, MFSNAMELEN) || 743 !strncmp(fsp->f_fstypename, MOUNT_FFS, MFSNAMELEN) || 744 !strncmp(fsp->f_fstypename, MOUNT_EXT2FS, MFSNAMELEN) || 745 !strncmp(fsp->f_fstypename, MOUNT_MSDOS, MFSNAMELEN) || 746 !strncmp(fsp->f_fstypename, MOUNT_CD9660, MFSNAMELEN)) { 747 fstbl[i].exflags = MNT_DELEXPORT; 748 fstbl[i].mntonname = fsp->f_mntonname; 749 } 750 fsp++; 751 } 752 753 /* 754 * Read in the exports file and build the list, calling 755 * mount() as we go along to push the export rules into the kernel. 756 */ 757 if ((exp_file = fopen(exname, "r")) == NULL) { 758 syslog(LOG_ERR, "Can't open %s", exname); 759 exit(2); 760 } 761 dirhead = NULL; 762 while (get_line()) { 763 if (debug) 764 fprintf(stderr, "Got line %s\n",line); 765 cp = line; 766 nextfield(&cp, &endcp); 767 if (*cp == '#') 768 goto nextline; 769 770 /* 771 * Set defaults. 772 */ 773 has_host = FALSE; 774 num_hosts = 0; 775 lookup_failed = FALSE; 776 anon = def_anon; 777 exflags = MNT_EXPORTED; 778 got_nondir = 0; 779 opt_flags = 0; 780 ep = NULL; 781 782 /* 783 * Create new exports list entry 784 */ 785 len = endcp-cp; 786 tgrp = grp = get_grp(); 787 while (len > 0) { 788 if (len > RPCMNT_NAMELEN) { 789 getexp_err(ep, tgrp); 790 goto nextline; 791 } 792 if (*cp == '-') { 793 if (ep == NULL) { 794 getexp_err(ep, tgrp); 795 goto nextline; 796 } 797 if (debug) 798 fprintf(stderr, "doing opt %s\n", cp); 799 got_nondir = 1; 800 if (do_opt(&cp, &endcp, ep, grp, &has_host, 801 &exflags, &anon)) { 802 getexp_err(ep, tgrp); 803 goto nextline; 804 } 805 } else if (*cp == '/') { 806 savedc = *endcp; 807 *endcp = '\0'; 808 if (check_dirpath(cp) && 809 statfs(cp, &fsb) >= 0) { 810 if (got_nondir) { 811 syslog(LOG_ERR, "Dirs must be first"); 812 getexp_err(ep, tgrp); 813 goto nextline; 814 } 815 if (ep) { 816 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] || 817 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) { 818 getexp_err(ep, tgrp); 819 goto nextline; 820 } 821 } else { 822 /* 823 * See if this directory is already 824 * in the list. 825 */ 826 ep = ex_search(&fsb.f_fsid); 827 if (ep == NULL) { 828 int len; 829 830 ep = get_exp(); 831 ep->ex_fs = fsb.f_fsid; 832 len = strlen(fsb.f_mntonname) + 1; 833 ep->ex_fsdir = (char *)malloc(len); 834 if (ep->ex_fsdir) 835 strlcpy(ep->ex_fsdir, 836 fsb.f_mntonname, len); 837 else 838 out_of_mem(); 839 if (debug) 840 fprintf(stderr, 841 "Making new ep fs=0x%x,0x%x\n", 842 fsb.f_fsid.val[0], 843 fsb.f_fsid.val[1]); 844 } else if (debug) 845 fprintf(stderr, 846 "Found ep fs=0x%x,0x%x\n", 847 fsb.f_fsid.val[0], 848 fsb.f_fsid.val[1]); 849 } 850 851 /* 852 * Add dirpath to export mount point. 853 */ 854 dirp = add_expdir(&dirhead, cp, len); 855 dirplen = len; 856 } else { 857 getexp_err(ep, tgrp); 858 goto nextline; 859 } 860 *endcp = savedc; 861 } else { 862 savedc = *endcp; 863 *endcp = '\0'; 864 got_nondir = 1; 865 if (ep == NULL) { 866 getexp_err(ep, tgrp); 867 goto nextline; 868 } 869 870 /* 871 * Get the host or netgroup. 872 */ 873 setnetgrent(cp); 874 netgrp = getnetgrent((const char **)&hst, 875 (const char **)&usr, (const char **)&dom); 876 do { 877 if (has_host) { 878 grp->gr_next = get_grp(); 879 grp = grp->gr_next; 880 } 881 if (netgrp) { 882 if (hst == NULL) { 883 syslog(LOG_ERR, 884 "NULL hostname in netgroup %s, skipping", 885 cp); 886 grp->gr_type = GT_IGNORE; 887 lookup_failed = TRUE; 888 continue; 889 } else if (get_host(hst, grp, tgrp)) { 890 syslog(LOG_ERR, 891 "Unknown host (%s) in netgroup %s", 892 hst, cp); 893 grp->gr_type = GT_IGNORE; 894 lookup_failed = TRUE; 895 continue; 896 } 897 } else if (get_host(cp, grp, tgrp)) { 898 syslog(LOG_ERR, 899 "Unknown host (%s) in line %s", 900 cp, line); 901 grp->gr_type = GT_IGNORE; 902 lookup_failed = TRUE; 903 continue; 904 } 905 has_host = TRUE; 906 num_hosts++; 907 } while (netgrp && getnetgrent((const char **)&hst, 908 (const char **)&usr, (const char **)&dom)); 909 endnetgrent(); 910 *endcp = savedc; 911 } 912 cp = endcp; 913 nextfield(&cp, &endcp); 914 len = endcp - cp; 915 } 916 /* 917 * If the exports list is empty due to unresolvable hostnames 918 * we throw away the line. 919 */ 920 if (lookup_failed == TRUE && num_hosts == 0 && 921 tgrp->gr_type == GT_IGNORE) { 922 getexp_err(ep, tgrp); 923 goto nextline; 924 } 925 if (check_options(dirhead)) { 926 getexp_err(ep, tgrp); 927 goto nextline; 928 } 929 if (!has_host) { 930 grp->gr_type = GT_HOST; 931 if (debug) 932 fprintf(stderr, "Adding a default entry\n"); 933 /* add a default group and make the grp list NULL */ 934 hpe = (struct hostent *)malloc(sizeof(struct hostent)); 935 if (hpe == NULL) 936 out_of_mem(); 937 hpe->h_name = strdup("Default"); 938 if (hpe->h_name == NULL) 939 out_of_mem(); 940 hpe->h_addrtype = AF_INET; 941 hpe->h_length = sizeof (u_int32_t); 942 hpe->h_addr_list = NULL; 943 grp->gr_ptr.gt_hostent = hpe; 944 945 /* 946 * Don't allow a network export coincide with a list of 947 * host(s) on the same line. 948 */ 949 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 950 getexp_err(ep, tgrp); 951 goto nextline; 952 } 953 954 /* 955 * Loop through hosts, pushing the exports into the kernel. 956 * After loop, tgrp points to the start of the list and 957 * grp points to the last entry in the list. 958 */ 959 grp = tgrp; 960 do { 961 962 /* 963 * remove filesystem from unexport list 964 * add MNT_DELEXPORT to exflags to clean up 965 * any old addrlist in the kernel 966 */ 967 968 for (i = 0; i < num; i++) { 969 if ((fstbl[i].mntonname != NULL) && 970 (strcmp (dirp, fstbl[i].mntonname) == 0) && 971 (fstbl[i].exflags & MNT_DELEXPORT)) { 972 exflags |= MNT_DELEXPORT; 973 fstbl[i].exflags = 0; 974 if (debug) 975 fprintf(stderr, "removing %s %s from unexport list\n", dirp, fstbl[i].mntonname); 976 } 977 } 978 979 if (debug) 980 fprintf(stderr, "exporting %s\n", dirp); 981 /* 982 * Non-zero return indicates an error. Return 983 * val of 1 means line is invalid (not just entry). 984 */ 985 i = do_mount(ep, grp, exflags, &anon, dirp, dirplen, &fsb); 986 exflags &= ~MNT_DELEXPORT; 987 if (i == 1) { 988 getexp_err(ep, tgrp); 989 goto nextline; 990 } else if (i == 2) { 991 syslog(LOG_ERR, 992 "Bad exports list entry (%s) in line %s", 993 (grp->gr_type == GT_HOST) 994 ? grp->gr_ptr.gt_hostent->h_name 995 : (grp->gr_type == GT_NET) 996 ? grp->gr_ptr.gt_net.nt_name 997 : "Unknown", line); 998 } 999 } while (grp->gr_next && (grp = grp->gr_next)); 1000 1001 /* 1002 * Success. Update the data structures. 1003 */ 1004 if (has_host) { 1005 hang_dirp(dirhead, tgrp, ep, opt_flags); 1006 grp->gr_next = grphead; 1007 grphead = tgrp; 1008 } else { 1009 hang_dirp(dirhead, NULL, ep, 1010 opt_flags); 1011 free_grp(grp); 1012 } 1013 dirhead = NULL; 1014 if ((ep->ex_flag & EX_LINKED) == 0) { 1015 ep2 = exphead; 1016 epp = &exphead; 1017 1018 /* 1019 * Insert in the list in alphabetical order. 1020 */ 1021 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { 1022 epp = &ep2->ex_next; 1023 ep2 = ep2->ex_next; 1024 } 1025 if (ep2) 1026 ep->ex_next = ep2; 1027 *epp = ep; 1028 ep->ex_flag |= EX_LINKED; 1029 } 1030 nextline: 1031 if (dirhead) { 1032 free_dir(dirhead); 1033 dirhead = NULL; 1034 } 1035 } 1036 1037 fsp = ofsp; 1038 for (i = 0; i < num; i++, fsp++) { 1039 if ((fstbl[i].exflags & MNT_DELEXPORT) == 0) 1040 continue; 1041 if (debug) 1042 fprintf(stderr, "unexporting %s %s\n", 1043 fsp->f_mntonname, fstbl[i].mntonname); 1044 bzero(&targs, sizeof(targs)); 1045 if (mount(fsp->f_fstypename, fsp->f_mntonname, 1046 fsp->f_flags | MNT_UPDATE, &targs) < 0) 1047 syslog(LOG_ERR, "Can't delete exports for %s: %m", 1048 fsp->f_mntonname); 1049 } 1050 free(fstbl); 1051 fclose(exp_file); 1052 } 1053 1054 /* 1055 * Allocate an export list element 1056 */ 1057 struct exportlist * 1058 get_exp(void) 1059 { 1060 struct exportlist *ep; 1061 1062 ep = (struct exportlist *)malloc(sizeof (struct exportlist)); 1063 if (ep == NULL) 1064 out_of_mem(); 1065 memset(ep, 0, sizeof(struct exportlist)); 1066 return (ep); 1067 } 1068 1069 /* 1070 * Allocate a group list element 1071 */ 1072 struct grouplist * 1073 get_grp(void) 1074 { 1075 struct grouplist *gp; 1076 1077 gp = (struct grouplist *)malloc(sizeof (struct grouplist)); 1078 if (gp == NULL) 1079 out_of_mem(); 1080 memset(gp, 0, sizeof(struct grouplist)); 1081 return (gp); 1082 } 1083 1084 /* 1085 * Clean up upon an error in get_exportlist(). 1086 */ 1087 void 1088 getexp_err(struct exportlist *ep, struct grouplist *grp) 1089 { 1090 struct grouplist *tgrp; 1091 1092 syslog(LOG_ERR, "Bad exports list line %s", line); 1093 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1094 free_exp(ep); 1095 while (grp) { 1096 tgrp = grp; 1097 grp = grp->gr_next; 1098 free_grp(tgrp); 1099 } 1100 } 1101 1102 /* 1103 * Search the export list for a matching fs. 1104 */ 1105 struct exportlist * 1106 ex_search(fsid_t *fsid) 1107 { 1108 struct exportlist *ep; 1109 1110 ep = exphead; 1111 while (ep) { 1112 if (ep->ex_fs.val[0] == fsid->val[0] && 1113 ep->ex_fs.val[1] == fsid->val[1]) 1114 return (ep); 1115 ep = ep->ex_next; 1116 } 1117 return (ep); 1118 } 1119 1120 /* 1121 * Add a directory path to the list. 1122 */ 1123 char * 1124 add_expdir(struct dirlist **dpp, char *cp, int len) 1125 { 1126 struct dirlist *dp; 1127 1128 /* do not need +1 because of dp_dirp[1] */ 1129 dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len); 1130 if (dp == NULL) 1131 out_of_mem(); 1132 dp->dp_left = *dpp; 1133 dp->dp_right = NULL; 1134 dp->dp_flag = 0; 1135 dp->dp_hosts = NULL; 1136 strlcpy(dp->dp_dirp, cp, len + 1); 1137 *dpp = dp; 1138 return (dp->dp_dirp); 1139 } 1140 1141 /* 1142 * Hang the dir list element off the dirpath binary tree as required 1143 * and update the entry for host. 1144 */ 1145 void 1146 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 1147 int flags) 1148 { 1149 struct hostlist *hp; 1150 struct dirlist *dp2; 1151 1152 if (flags & OP_ALLDIRS) { 1153 if (ep->ex_defdir) 1154 free((caddr_t)dp); 1155 else 1156 ep->ex_defdir = dp; 1157 if (grp == NULL) { 1158 ep->ex_defdir->dp_flag |= DP_DEFSET; 1159 } else while (grp) { 1160 hp = get_ht(); 1161 hp->ht_grp = grp; 1162 hp->ht_next = ep->ex_defdir->dp_hosts; 1163 ep->ex_defdir->dp_hosts = hp; 1164 grp = grp->gr_next; 1165 } 1166 } else { 1167 1168 /* 1169 * Loop through the directories adding them to the tree. 1170 */ 1171 while (dp) { 1172 dp2 = dp->dp_left; 1173 add_dlist(&ep->ex_dirl, dp, grp, flags); 1174 dp = dp2; 1175 } 1176 } 1177 } 1178 1179 /* 1180 * Traverse the binary tree either updating a node that is already there 1181 * for the new directory or adding the new node. 1182 */ 1183 void 1184 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 1185 int flags) 1186 { 1187 struct dirlist *dp; 1188 struct hostlist *hp; 1189 int cmp; 1190 1191 dp = *dpp; 1192 if (dp) { 1193 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 1194 if (cmp > 0) { 1195 add_dlist(&dp->dp_left, newdp, grp, flags); 1196 return; 1197 } else if (cmp < 0) { 1198 add_dlist(&dp->dp_right, newdp, grp, flags); 1199 return; 1200 } else 1201 free((caddr_t)newdp); 1202 } else { 1203 dp = newdp; 1204 dp->dp_left = NULL; 1205 *dpp = dp; 1206 } 1207 if (grp) { 1208 1209 /* 1210 * Hang all of the host(s) off of the directory point. 1211 */ 1212 do { 1213 hp = get_ht(); 1214 hp->ht_grp = grp; 1215 hp->ht_next = dp->dp_hosts; 1216 dp->dp_hosts = hp; 1217 grp = grp->gr_next; 1218 } while (grp); 1219 } else { 1220 dp->dp_flag |= DP_DEFSET; 1221 } 1222 } 1223 1224 /* 1225 * Search for a dirpath on the export point. 1226 */ 1227 struct dirlist * 1228 dirp_search(struct dirlist *dp, char *dirpath) 1229 { 1230 int cmp; 1231 1232 if (dp) { 1233 cmp = strcmp(dp->dp_dirp, dirpath); 1234 if (cmp > 0) 1235 return (dirp_search(dp->dp_left, dirpath)); 1236 else if (cmp < 0) 1237 return (dirp_search(dp->dp_right, dirpath)); 1238 else 1239 return (dp); 1240 } 1241 return (dp); 1242 } 1243 1244 /* 1245 * Scan for a host match in a directory tree. 1246 */ 1247 int 1248 chk_host(struct dirlist *dp, in_addr_t saddr, int *defsetp, int *hostsetp) 1249 { 1250 struct hostlist *hp; 1251 struct grouplist *grp; 1252 u_int32_t **addrp; 1253 1254 if (dp) { 1255 if (dp->dp_flag & DP_DEFSET) 1256 *defsetp = dp->dp_flag; 1257 hp = dp->dp_hosts; 1258 while (hp) { 1259 grp = hp->ht_grp; 1260 switch (grp->gr_type) { 1261 case GT_HOST: 1262 addrp = (u_int32_t **) 1263 grp->gr_ptr.gt_hostent->h_addr_list; 1264 while (*addrp) { 1265 if (**addrp == saddr) { 1266 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1267 return (1); 1268 } 1269 addrp++; 1270 } 1271 break; 1272 case GT_NET: 1273 if ((saddr & grp->gr_ptr.gt_net.nt_mask) == 1274 grp->gr_ptr.gt_net.nt_net) { 1275 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1276 return (1); 1277 } 1278 break; 1279 } 1280 hp = hp->ht_next; 1281 } 1282 } 1283 return (0); 1284 } 1285 1286 /* 1287 * Scan tree for a host that matches the address. 1288 */ 1289 int 1290 scan_tree(struct dirlist *dp, in_addr_t saddr) 1291 { 1292 int defset, hostset; 1293 1294 if (dp) { 1295 if (scan_tree(dp->dp_left, saddr)) 1296 return (1); 1297 if (chk_host(dp, saddr, &defset, &hostset)) 1298 return (1); 1299 if (scan_tree(dp->dp_right, saddr)) 1300 return (1); 1301 } 1302 return (0); 1303 } 1304 1305 /* 1306 * Traverse the dirlist tree and free it up. 1307 */ 1308 void 1309 free_dir(struct dirlist *dp) 1310 { 1311 1312 if (dp) { 1313 free_dir(dp->dp_left); 1314 free_dir(dp->dp_right); 1315 free_host(dp->dp_hosts); 1316 free((caddr_t)dp); 1317 } 1318 } 1319 1320 /* 1321 * Parse the option string and update fields. 1322 * Option arguments may either be -<option>=<value> or 1323 * -<option> <value> 1324 */ 1325 int 1326 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp, 1327 int *has_hostp, int *exflagsp, struct ucred *cr) 1328 { 1329 char *cp, *endcp, *cpopt, savedc, savedc2 = 0; 1330 char *cpoptarg, *cpoptend; 1331 int allflag, usedarg; 1332 1333 cpopt = *cpp; 1334 cpopt++; 1335 cp = *endcpp; 1336 savedc = *cp; 1337 *cp = '\0'; 1338 while (cpopt && *cpopt) { 1339 allflag = 1; 1340 usedarg = -2; 1341 if ((cpoptend = strchr(cpopt, ','))) { 1342 *cpoptend++ = '\0'; 1343 if ((cpoptarg = strchr(cpopt, '='))) 1344 *cpoptarg++ = '\0'; 1345 } else { 1346 if ((cpoptarg = strchr(cpopt, '='))) 1347 *cpoptarg++ = '\0'; 1348 else { 1349 *cp = savedc; 1350 nextfield(&cp, &endcp); 1351 **endcpp = '\0'; 1352 if (endcp > cp && *cp != '-') { 1353 cpoptarg = cp; 1354 savedc2 = *endcp; 1355 *endcp = '\0'; 1356 usedarg = 0; 1357 } 1358 } 1359 } 1360 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 1361 *exflagsp |= MNT_EXRDONLY; 1362 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 1363 !(allflag = strcmp(cpopt, "mapall")) || 1364 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 1365 usedarg++; 1366 parsecred(cpoptarg, cr); 1367 if (allflag == 0) { 1368 *exflagsp |= MNT_EXPORTANON; 1369 opt_flags |= OP_MAPALL; 1370 } else 1371 opt_flags |= OP_MAPROOT; 1372 } else 1373 if (cpoptarg && (!strcmp(cpopt, "mask") || 1374 !strcmp(cpopt, "m"))) { 1375 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 1376 syslog(LOG_ERR, "Bad mask: %s", cpoptarg); 1377 return (1); 1378 } 1379 usedarg++; 1380 opt_flags |= OP_MASK; 1381 } else if (cpoptarg && (!strcmp(cpopt, "network") || 1382 !strcmp(cpopt, "n"))) { 1383 if (grp->gr_type != GT_NULL) { 1384 syslog(LOG_ERR, "Network/host conflict"); 1385 return (1); 1386 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 1387 syslog(LOG_ERR, "Bad net: %s", cpoptarg); 1388 return (1); 1389 } 1390 grp->gr_type = GT_NET; 1391 *has_hostp = 1; 1392 usedarg++; 1393 opt_flags |= OP_NET; 1394 } else if (!strcmp(cpopt, "alldirs")) { 1395 opt_flags |= OP_ALLDIRS; 1396 } else { 1397 syslog(LOG_ERR, "Bad opt %s", cpopt); 1398 return (1); 1399 } 1400 if (usedarg >= 0) { 1401 *endcp = savedc2; 1402 **endcpp = savedc; 1403 if (usedarg > 0) { 1404 *cpp = cp; 1405 *endcpp = endcp; 1406 } 1407 return (0); 1408 } 1409 cpopt = cpoptend; 1410 } 1411 **endcpp = savedc; 1412 return (0); 1413 } 1414 1415 /* 1416 * Translate a character string to the corresponding list of network 1417 * addresses for a hostname. 1418 */ 1419 int 1420 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp) 1421 { 1422 struct hostent *hp, *nhp, t_host; 1423 struct grouplist *checkgrp; 1424 char **addrp, **naddrp; 1425 struct in_addr saddr; 1426 char *aptr[2]; 1427 int i; 1428 1429 if (grp->gr_type != GT_NULL) 1430 return (1); 1431 if ((hp = gethostbyname(cp)) == NULL) { 1432 if (isdigit(*cp)) { 1433 if (inet_aton(cp, &saddr) == 0) { 1434 syslog(LOG_ERR, "inet_aton failed for %s", cp); 1435 return (1); 1436 } 1437 if ((hp = gethostbyaddr((caddr_t)&saddr.s_addr, 1438 sizeof (saddr.s_addr), AF_INET)) == NULL) { 1439 hp = &t_host; 1440 hp->h_name = cp; 1441 hp->h_addrtype = AF_INET; 1442 hp->h_length = sizeof (u_int32_t); 1443 hp->h_addr_list = aptr; 1444 aptr[0] = (char *)&saddr; 1445 aptr[1] = NULL; 1446 } 1447 } else { 1448 syslog(LOG_ERR, "gethostbyname; failed for %s: %s", cp, 1449 hstrerror(h_errno)); 1450 return (1); 1451 } 1452 } 1453 1454 /* only insert each host onto the list once */ 1455 for (checkgrp = tgrp; checkgrp; checkgrp = checkgrp->gr_next) { 1456 if (checkgrp->gr_type == GT_HOST && 1457 checkgrp->gr_ptr.gt_hostent != NULL && 1458 !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) { 1459 grp->gr_type = GT_IGNORE; 1460 return (0); 1461 } 1462 } 1463 1464 grp->gr_type = GT_HOST; 1465 nhp = grp->gr_ptr.gt_hostent = (struct hostent *) 1466 malloc(sizeof(struct hostent)); 1467 if (nhp == NULL) 1468 out_of_mem(); 1469 memcpy(nhp, hp, sizeof(struct hostent)); 1470 i = strlen(hp->h_name)+1; 1471 nhp->h_name = (char *)malloc(i); 1472 if (nhp->h_name == NULL) 1473 out_of_mem(); 1474 memcpy(nhp->h_name, hp->h_name, i); 1475 addrp = hp->h_addr_list; 1476 i = 1; 1477 while (*addrp++) 1478 i++; 1479 naddrp = nhp->h_addr_list = (char **) 1480 malloc(i*sizeof(char *)); 1481 if (naddrp == NULL) 1482 out_of_mem(); 1483 addrp = hp->h_addr_list; 1484 while (*addrp) { 1485 *naddrp = (char *) 1486 malloc(hp->h_length); 1487 if (*naddrp == NULL) 1488 out_of_mem(); 1489 memcpy(*naddrp, *addrp, hp->h_length); 1490 addrp++; 1491 naddrp++; 1492 } 1493 *naddrp = NULL; 1494 if (debug) 1495 fprintf(stderr, "got host %s\n", hp->h_name); 1496 return (0); 1497 } 1498 1499 /* 1500 * Free up an exports list component 1501 */ 1502 void 1503 free_exp(struct exportlist *ep) 1504 { 1505 1506 if (ep->ex_defdir) { 1507 free_host(ep->ex_defdir->dp_hosts); 1508 free((caddr_t)ep->ex_defdir); 1509 } 1510 if (ep->ex_fsdir) 1511 free(ep->ex_fsdir); 1512 free_dir(ep->ex_dirl); 1513 free((caddr_t)ep); 1514 } 1515 1516 /* 1517 * Free hosts. 1518 */ 1519 void 1520 free_host(struct hostlist *hp) 1521 { 1522 struct hostlist *hp2; 1523 1524 while (hp) { 1525 hp2 = hp; 1526 hp = hp->ht_next; 1527 free((caddr_t)hp2); 1528 } 1529 } 1530 1531 struct hostlist * 1532 get_ht(void) 1533 { 1534 struct hostlist *hp; 1535 1536 hp = (struct hostlist *)malloc(sizeof (struct hostlist)); 1537 if (hp == NULL) 1538 out_of_mem(); 1539 hp->ht_next = NULL; 1540 hp->ht_flag = 0; 1541 return (hp); 1542 } 1543 1544 /* 1545 * Out of memory, fatal 1546 */ 1547 void 1548 out_of_mem(void) 1549 { 1550 1551 syslog(LOG_ERR, "Out of memory"); 1552 exit(2); 1553 } 1554 1555 /* 1556 * Do the mount syscall with the update flag to push the export info into 1557 * the kernel. Returns 0 on success, 1 for fatal error, and 2 for error 1558 * that only invalidates the specific entry/host. 1559 */ 1560 int 1561 do_mount(struct exportlist *ep, struct grouplist *grp, int exflags, 1562 struct ucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb) 1563 { 1564 struct sockaddr_in sin, imask; 1565 union { 1566 struct ufs_args ua; 1567 struct iso_args ia; 1568 struct mfs_args ma; 1569 struct msdosfs_args da; 1570 } args; 1571 char savedc = '\0'; 1572 u_int32_t **addrp; 1573 char *cp = NULL; 1574 in_addr_t net; 1575 int done; 1576 1577 args.ua.fspec = 0; 1578 args.ua.export_info.ex_flags = exflags; 1579 args.ua.export_info.ex_anon = *anoncrp; 1580 memset(&sin, 0, sizeof(sin)); 1581 memset(&imask, 0, sizeof(imask)); 1582 sin.sin_family = AF_INET; 1583 sin.sin_len = sizeof(sin); 1584 imask.sin_family = AF_INET; 1585 imask.sin_len = sizeof(sin); 1586 if (grp->gr_type == GT_HOST) 1587 addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list; 1588 else 1589 addrp = NULL; 1590 1591 done = FALSE; 1592 while (!done) { 1593 switch (grp->gr_type) { 1594 case GT_HOST: 1595 args.ua.export_info.ex_addr = (struct sockaddr *)&sin; 1596 args.ua.export_info.ex_masklen = 0; 1597 if (!addrp) { 1598 args.ua.export_info.ex_addrlen = 0; 1599 break; 1600 } 1601 sin.sin_addr.s_addr = **addrp; 1602 args.ua.export_info.ex_addrlen = sizeof(sin); 1603 break; 1604 case GT_NET: 1605 sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net; 1606 args.ua.export_info.ex_addr = (struct sockaddr *)&sin; 1607 args.ua.export_info.ex_addrlen = sizeof (sin); 1608 args.ua.export_info.ex_mask = (struct sockaddr *)&imask; 1609 args.ua.export_info.ex_masklen = sizeof (imask); 1610 if (grp->gr_ptr.gt_net.nt_mask) { 1611 imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask; 1612 break; 1613 } 1614 net = ntohl(grp->gr_ptr.gt_net.nt_net); 1615 if (IN_CLASSA(net)) 1616 imask.sin_addr.s_addr = inet_addr("255.0.0.0"); 1617 else if (IN_CLASSB(net)) 1618 imask.sin_addr.s_addr = inet_addr("255.255.0.0"); 1619 else 1620 imask.sin_addr.s_addr = inet_addr("255.255.255.0"); 1621 grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr; 1622 break; 1623 case GT_IGNORE: 1624 return (0); 1625 default: 1626 syslog(LOG_ERR, "Bad grouptype"); 1627 if (cp) 1628 *cp = savedc; 1629 return (1); 1630 } 1631 1632 /* 1633 * XXX: 1634 * Maybe I should just use the fsb->f_mntonname path instead 1635 * of looping back up the dirp to the mount point?? 1636 * Also, needs to know how to export all types of local 1637 * exportable file systems and not just MOUNT_FFS. 1638 */ 1639 while (mount(fsb->f_fstypename, dirp, 1640 fsb->f_flags | MNT_UPDATE, &args) < 0) { 1641 if (cp) 1642 *cp-- = savedc; 1643 else 1644 cp = dirp + dirplen - 1; 1645 if (errno == EPERM) { 1646 syslog(LOG_ERR, 1647 "Can't change attributes for %s (%s).\n", 1648 dirp, 1649 (grp->gr_type == GT_HOST) 1650 ?grp->gr_ptr.gt_hostent->h_name 1651 :(grp->gr_type == GT_NET) 1652 ?grp->gr_ptr.gt_net.nt_name 1653 :"Unknown"); 1654 return (2); 1655 } 1656 if (opt_flags & OP_ALLDIRS) { 1657 #if 0 1658 syslog(LOG_ERR, "Could not remount %s: %m", 1659 dirp); 1660 return (2); 1661 #endif 1662 } 1663 /* back up over the last component */ 1664 while (*cp == '/' && cp > dirp) 1665 cp--; 1666 while (*(cp - 1) != '/' && cp > dirp) 1667 cp--; 1668 if (cp == dirp) { 1669 if (debug) 1670 fprintf(stderr, "mnt unsucc\n"); 1671 syslog(LOG_ERR, "Can't export %s: %m", dirp); 1672 return (2); 1673 } 1674 savedc = *cp; 1675 *cp = '\0'; 1676 } 1677 if (addrp) { 1678 ++addrp; 1679 if (*addrp == NULL) 1680 done = TRUE; 1681 } else 1682 done = TRUE; 1683 } 1684 if (cp) 1685 *cp = savedc; 1686 return (0); 1687 } 1688 1689 /* 1690 * Translate a net address. 1691 */ 1692 int 1693 get_net(char *cp, struct netmsk *net, int maskflg) 1694 { 1695 struct in_addr inetaddr, inetaddr2; 1696 in_addr_t netaddr; 1697 struct netent *np; 1698 char *name; 1699 1700 if ((netaddr = inet_network(cp)) != INADDR_NONE) { 1701 inetaddr = inet_makeaddr(netaddr, 0); 1702 /* 1703 * Due to arbitrary subnet masks, you don't know how many 1704 * bits to shift the address to make it into a network, 1705 * however you do know how to make a network address into 1706 * a host with host == 0 and then compare them. 1707 * (What a pest) 1708 */ 1709 if (!maskflg) { 1710 setnetent(0); 1711 while ((np = getnetent())) { 1712 inetaddr2 = inet_makeaddr(np->n_net, 0); 1713 if (inetaddr2.s_addr == inetaddr.s_addr) 1714 break; 1715 } 1716 endnetent(); 1717 } 1718 } else { 1719 if ((np = getnetbyname(cp))) 1720 inetaddr = inet_makeaddr(np->n_net, 0); 1721 else 1722 return (1); 1723 } 1724 if (maskflg) 1725 net->nt_mask = inetaddr.s_addr; 1726 else { 1727 int len; 1728 1729 if (np) 1730 name = np->n_name; 1731 else 1732 name = inet_ntoa(inetaddr); 1733 len = strlen(name) + 1; 1734 net->nt_name = (char *)malloc(len); 1735 if (net->nt_name == NULL) 1736 out_of_mem(); 1737 strlcpy(net->nt_name, name, len); 1738 net->nt_net = inetaddr.s_addr; 1739 } 1740 return (0); 1741 } 1742 1743 /* 1744 * Parse out the next white space separated field 1745 */ 1746 void 1747 nextfield(char **cp, char **endcp) 1748 { 1749 char *p; 1750 1751 p = *cp; 1752 while (*p == ' ' || *p == '\t') 1753 p++; 1754 if (*p == '\n' || *p == '\0') 1755 *cp = *endcp = p; 1756 else { 1757 *cp = p++; 1758 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 1759 p++; 1760 *endcp = p; 1761 } 1762 } 1763 1764 /* 1765 * Get an exports file line. Skip over blank lines and handle line 1766 * continuations. 1767 */ 1768 int 1769 get_line(void) 1770 { 1771 int totlen, cont_line, len; 1772 char *p, *cp; 1773 1774 /* 1775 * Loop around ignoring blank lines and getting all continuation lines. 1776 */ 1777 p = line; 1778 totlen = 0; 1779 do { 1780 if (fgets(p, LINESIZ - totlen, exp_file) == NULL) 1781 return (0); 1782 len = strlen(p); 1783 cp = p + len - 1; 1784 cont_line = 0; 1785 while (cp >= p && (*cp == ' ' || *cp == '\t' || *cp == '\n' || 1786 *cp == '\\')) { 1787 if (*cp == '\\') 1788 cont_line = 1; 1789 cp--; 1790 len--; 1791 } 1792 *++cp = '\0'; 1793 if (len > 0) { 1794 totlen += len; 1795 if (totlen >= LINESIZ) { 1796 syslog(LOG_ERR, "Exports line too long"); 1797 exit(2); 1798 } 1799 p = cp; 1800 } 1801 } while (totlen == 0 || cont_line); 1802 return (1); 1803 } 1804 1805 /* 1806 * Parse a description of a credential. 1807 */ 1808 void 1809 parsecred(char *namelist, struct ucred *cr) 1810 { 1811 gid_t groups[NGROUPS + 1]; 1812 char *name, *names; 1813 struct passwd *pw; 1814 struct group *gr; 1815 int ngroups, cnt; 1816 1817 /* 1818 * Set up the unprivileged user. 1819 */ 1820 cr->cr_ref = 1; 1821 cr->cr_uid = (uid_t)-2; 1822 cr->cr_gid = (gid_t)-2; 1823 cr->cr_ngroups = 0; 1824 /* 1825 * Get the user's password table entry. 1826 */ 1827 names = strsep(&namelist, " \t\n"); 1828 name = strsep(&names, ":"); 1829 if (isdigit(*name) || *name == '-') 1830 pw = getpwuid(atoi(name)); 1831 else 1832 pw = getpwnam(name); 1833 /* 1834 * Credentials specified as those of a user. 1835 */ 1836 if (names == NULL) { 1837 if (pw == NULL) { 1838 syslog(LOG_ERR, "Unknown user: %s", name); 1839 return; 1840 } 1841 cr->cr_uid = pw->pw_uid; 1842 ngroups = NGROUPS + 1; 1843 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) 1844 syslog(LOG_ERR, "Too many groups for %s: %m", pw->pw_name); 1845 /* 1846 * compress out duplicate 1847 */ 1848 cr->cr_ngroups = ngroups - 1; 1849 cr->cr_gid = groups[0]; 1850 for (cnt = 1; cnt < ngroups; cnt++) 1851 cr->cr_groups[cnt - 1] = groups[cnt]; 1852 return; 1853 } 1854 /* 1855 * Explicit credential specified as a colon separated list: 1856 * uid:gid:gid:... 1857 */ 1858 if (pw != NULL) 1859 cr->cr_uid = pw->pw_uid; 1860 else if (isdigit(*name) || *name == '-') 1861 cr->cr_uid = atoi(name); 1862 else { 1863 syslog(LOG_ERR, "Unknown user: %s", name); 1864 return; 1865 } 1866 cr->cr_ngroups = 0; 1867 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 1868 name = strsep(&names, ":"); 1869 if (isdigit(*name) || *name == '-') { 1870 cr->cr_groups[cr->cr_ngroups++] = atoi(name); 1871 } else { 1872 if ((gr = getgrnam(name)) == NULL) { 1873 syslog(LOG_ERR, "Unknown group: %s", name); 1874 continue; 1875 } 1876 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 1877 } 1878 } 1879 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 1880 syslog(LOG_ERR, "Too many groups"); 1881 } 1882 1883 #define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 1884 /* 1885 * Routines that maintain the remote mounttab 1886 */ 1887 void 1888 get_mountlist(void) 1889 { 1890 struct mountlist *mlp, **mlpp; 1891 char *host, *dirp, *cp; 1892 char str[STRSIZ]; 1893 FILE *mlfile; 1894 1895 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 1896 syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST); 1897 return; 1898 } 1899 mlpp = &mlhead; 1900 while (fgets(str, STRSIZ, mlfile) != NULL) { 1901 cp = str; 1902 host = strsep(&cp, " \t\n"); 1903 dirp = strsep(&cp, " \t\n"); 1904 if (host == NULL || dirp == NULL) 1905 continue; 1906 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 1907 if (mlp == NULL) 1908 out_of_mem(); 1909 strlcpy(mlp->ml_host, host, sizeof(mlp->ml_host)); 1910 strlcpy(mlp->ml_dirp, dirp, sizeof(mlp->ml_dirp)); 1911 mlp->ml_next = NULL; 1912 *mlpp = mlp; 1913 mlpp = &mlp->ml_next; 1914 } 1915 fclose(mlfile); 1916 } 1917 1918 void 1919 del_mlist(char *hostp, char *dirp) 1920 { 1921 struct mountlist *mlp, **mlpp; 1922 struct mountlist *mlp2; 1923 FILE *mlfile; 1924 int fnd = 0; 1925 1926 mlpp = &mlhead; 1927 mlp = mlhead; 1928 while (mlp) { 1929 if (!strcmp(mlp->ml_host, hostp) && 1930 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 1931 fnd = 1; 1932 mlp2 = mlp; 1933 *mlpp = mlp = mlp->ml_next; 1934 free((caddr_t)mlp2); 1935 } else { 1936 mlpp = &mlp->ml_next; 1937 mlp = mlp->ml_next; 1938 } 1939 } 1940 if (fnd) { 1941 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 1942 syslog(LOG_ERR, "Can't update %s: %m", 1943 _PATH_RMOUNTLIST); 1944 return; 1945 } 1946 mlp = mlhead; 1947 while (mlp) { 1948 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 1949 mlp = mlp->ml_next; 1950 } 1951 fclose(mlfile); 1952 } 1953 } 1954 1955 void 1956 add_mlist(char *hostp, char *dirp) 1957 { 1958 struct mountlist *mlp, **mlpp; 1959 FILE *mlfile; 1960 1961 mlpp = &mlhead; 1962 mlp = mlhead; 1963 while (mlp) { 1964 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 1965 return; 1966 mlpp = &mlp->ml_next; 1967 mlp = mlp->ml_next; 1968 } 1969 mlp = (struct mountlist *)malloc(sizeof (*mlp)); 1970 if (mlp == NULL) 1971 out_of_mem(); 1972 strlcpy(mlp->ml_host, hostp, sizeof(mlp->ml_host)); 1973 strlcpy(mlp->ml_dirp, dirp, sizeof(mlp->ml_dirp)); 1974 mlp->ml_next = NULL; 1975 *mlpp = mlp; 1976 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 1977 syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST); 1978 return; 1979 } 1980 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 1981 fclose(mlfile); 1982 } 1983 1984 /* 1985 * This function is called via SIGTERM when the system is going down. 1986 * It sends a broadcast RPCMNT_UMNTALL. 1987 */ 1988 void 1989 send_umntall(int signo) 1990 { 1991 gotterm = 1; 1992 } 1993 1994 int 1995 umntall_each(caddr_t resultsp, struct sockaddr_in *raddr) 1996 { 1997 return (1); 1998 } 1999 2000 /* 2001 * Free up a group list. 2002 */ 2003 void 2004 free_grp(struct grouplist *grp) 2005 { 2006 char **addrp; 2007 2008 if (grp->gr_type == GT_HOST) { 2009 if (grp->gr_ptr.gt_hostent->h_name) { 2010 addrp = grp->gr_ptr.gt_hostent->h_addr_list; 2011 while (addrp && *addrp) 2012 free(*addrp++); 2013 free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list); 2014 free(grp->gr_ptr.gt_hostent->h_name); 2015 } 2016 free((caddr_t)grp->gr_ptr.gt_hostent); 2017 } else if (grp->gr_type == GT_NET) { 2018 if (grp->gr_ptr.gt_net.nt_name) 2019 free(grp->gr_ptr.gt_net.nt_name); 2020 } 2021 free((caddr_t)grp); 2022 } 2023 2024 /* 2025 * Check options for consistency. 2026 */ 2027 int 2028 check_options(struct dirlist *dp) 2029 { 2030 2031 if (dp == NULL) 2032 return (1); 2033 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) { 2034 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive"); 2035 return (1); 2036 } 2037 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 2038 syslog(LOG_ERR, "-mask requires -network"); 2039 return (1); 2040 } 2041 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 2042 syslog(LOG_ERR, "-alldirs has multiple directories"); 2043 return (1); 2044 } 2045 return (0); 2046 } 2047 2048 /* 2049 * Check an absolute directory path for any symbolic links. Return true 2050 * if no symbolic links are found. 2051 */ 2052 int 2053 check_dirpath(char *dirp) 2054 { 2055 struct stat sb; 2056 int ret = 1; 2057 char *cp; 2058 2059 /* Remove trailing '/' */ 2060 cp = dirp + strlen(dirp) - 1; 2061 while (cp > dirp && *cp == '/') 2062 *cp-- = '\0'; 2063 2064 cp = dirp + 1; 2065 while (*cp && ret) { 2066 if (*cp == '/') { 2067 *cp = '\0'; 2068 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode)) 2069 ret = 0; 2070 *cp = '/'; 2071 } 2072 cp++; 2073 } 2074 if (lstat(dirp, &sb) < 0 || 2075 (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))) 2076 ret = 0; 2077 return (ret); 2078 } 2079