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