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