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