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