1 /* $NetBSD: mountd.c,v 1.130 2018/01/09 03:31:13 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.130 2018/01/09 03:31:13 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 ((fsp->f_flag & MNT_AUTOMOUNTED) != 0) 968 syslog(LOG_ERR, "\"%s\", line %ld: Warning: exporting of " 969 "automounted fs %s not supported", 970 line, (unsigned long)lineno, cp); 971 if (got_nondir) { 972 syslog(LOG_ERR, 973 "\"%s\", line %ld: Directories must precede files", 974 line, (unsigned long)lineno); 975 return 0; 976 } 977 if (*ep) { 978 if ((*ep)->ex_fs.__fsid_val[0] != fsp->f_fsidx.__fsid_val[0] || 979 (*ep)->ex_fs.__fsid_val[1] != fsp->f_fsidx.__fsid_val[1]) { 980 syslog(LOG_ERR, 981 "\"%s\", line %ld: filesystem ids disagree", 982 line, (unsigned long)lineno); 983 return 0; 984 } 985 } else { 986 /* 987 * See if this directory is already 988 * in the list. 989 */ 990 *ep = ex_search(&fsp->f_fsidx); 991 if (*ep == NULL) { 992 *ep = get_exp(); 993 (*ep)->ex_fs = fsp->f_fsidx; 994 (*ep)->ex_fsdir = estrdup(fsp->f_mntonname); 995 if (debug) 996 (void)fprintf(stderr, 997 "Making new ep fs=0x%x,0x%x\n", 998 fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]); 999 } else { 1000 if (debug) 1001 (void)fprintf(stderr, 1002 "Found ep fs=0x%x,0x%x\n", 1003 fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]); 1004 } 1005 } 1006 1007 return 1; 1008 } 1009 1010 1011 /* 1012 * Get the export list 1013 */ 1014 /* ARGSUSED */ 1015 void 1016 get_exportlist(int n) 1017 { 1018 struct exportlist *ep, *ep2; 1019 struct grouplist *grp, *tgrp; 1020 struct exportlist **epp; 1021 struct dirlist *dirhead; 1022 struct statvfs fsb, *fsp; 1023 struct addrinfo *ai; 1024 struct uucred anon; 1025 char *cp, *endcp, *dirp, savedc; 1026 int has_host, exflags, got_nondir, dirplen, num, i; 1027 FILE *exp_file; 1028 char *line; 1029 size_t lineno = 0, len; 1030 1031 1032 /* 1033 * First, get rid of the old list 1034 */ 1035 ep = exphead; 1036 while (ep) { 1037 ep2 = ep; 1038 ep = ep->ex_next; 1039 free_exp(ep2); 1040 } 1041 exphead = NULL; 1042 1043 dirp = NULL; 1044 dirplen = 0; 1045 grp = grphead; 1046 while (grp) { 1047 tgrp = grp; 1048 grp = grp->gr_next; 1049 free_grp(tgrp); 1050 } 1051 grphead = NULL; 1052 1053 /* 1054 * And delete exports that are in the kernel for all local 1055 * file systems. 1056 */ 1057 num = getmntinfo(&fsp, MNT_NOWAIT); 1058 for (i = 0; i < num; i++) { 1059 struct mountd_exports_list mel; 1060 1061 /* Delete all entries from the export list. */ 1062 mel.mel_path = fsp->f_mntonname; 1063 mel.mel_nexports = 0; 1064 if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) == -1 && 1065 errno != EOPNOTSUPP) 1066 syslog(LOG_ERR, "Can't delete exports for %s (%m)", 1067 fsp->f_mntonname); 1068 1069 fsp++; 1070 } 1071 1072 /* 1073 * Read in the exports file and build the list, calling 1074 * mount() as we go along to push the export rules into the kernel. 1075 */ 1076 if ((exp_file = fopen(exname, "r")) == NULL) { 1077 /* 1078 * Don't exit here; we can still reload the config 1079 * after a SIGHUP. 1080 */ 1081 if (debug) 1082 (void)fprintf(stderr, "Can't open %s: %s\n", exname, 1083 strerror(errno)); 1084 return; 1085 } 1086 dirhead = NULL; 1087 while ((line = fparseln(exp_file, &len, &lineno, NULL, 0)) != NULL) { 1088 if (debug) 1089 (void)fprintf(stderr, "Got line %s\n", line); 1090 cp = line; 1091 nextfield(&cp, &endcp); 1092 if (cp == endcp) 1093 goto nextline; /* skip empty line */ 1094 /* 1095 * Set defaults. 1096 */ 1097 has_host = FALSE; 1098 anon = def_anon; 1099 exflags = MNT_EXPORTED; 1100 got_nondir = 0; 1101 opt_flags = 0; 1102 ep = NULL; 1103 1104 if (noprivports) { 1105 opt_flags |= OP_NORESMNT | OP_NORESPORT; 1106 exflags |= MNT_EXNORESPORT; 1107 } 1108 1109 /* 1110 * Create new exports list entry 1111 */ 1112 len = endcp - cp; 1113 tgrp = grp = get_grp(); 1114 while (len > 0) { 1115 if (len > RPCMNT_NAMELEN) { 1116 *endcp = '\0'; 1117 syslog(LOG_ERR, 1118 "\"%s\", line %ld: name `%s' is too long", 1119 line, (unsigned long)lineno, cp); 1120 goto badline; 1121 } 1122 switch (*cp) { 1123 case '-': 1124 /* 1125 * Option 1126 */ 1127 if (ep == NULL) { 1128 syslog(LOG_ERR, 1129 "\"%s\", line %ld: No current export list", 1130 line, (unsigned long)lineno); 1131 goto badline; 1132 } 1133 if (debug) 1134 (void)fprintf(stderr, "doing opt %s\n", 1135 cp); 1136 got_nondir = 1; 1137 if (do_opt(line, lineno, &cp, &endcp, ep, grp, 1138 &has_host, &exflags, &anon)) 1139 goto badline; 1140 break; 1141 1142 case '/': 1143 /* 1144 * Directory 1145 */ 1146 savedc = *endcp; 1147 *endcp = '\0'; 1148 1149 if (!parse_directory(line, lineno, tgrp, 1150 got_nondir, cp, &ep, &fsb)) 1151 goto badline; 1152 /* 1153 * Add dirpath to export mount point. 1154 */ 1155 dirp = add_expdir(&dirhead, cp, len); 1156 dirplen = len; 1157 1158 *endcp = savedc; 1159 break; 1160 1161 default: 1162 /* 1163 * Host or netgroup. 1164 */ 1165 savedc = *endcp; 1166 *endcp = '\0'; 1167 1168 if (!parse_host_netgroup(line, lineno, ep, 1169 tgrp, cp, &has_host, &grp)) 1170 goto badline; 1171 1172 got_nondir = 1; 1173 1174 *endcp = savedc; 1175 break; 1176 } 1177 1178 cp = endcp; 1179 nextfield(&cp, &endcp); 1180 len = endcp - cp; 1181 } 1182 if (check_options(line, lineno, dirhead)) 1183 goto badline; 1184 1185 if (!has_host) { 1186 grp->gr_type = GT_HOST; 1187 if (debug) 1188 (void)fprintf(stderr, 1189 "Adding a default entry\n"); 1190 /* add a default group and make the grp list NULL */ 1191 ai = emalloc(sizeof(struct addrinfo)); 1192 ai->ai_flags = 0; 1193 ai->ai_family = AF_INET; /* XXXX */ 1194 ai->ai_socktype = SOCK_DGRAM; 1195 /* setting the length to 0 will match anything */ 1196 ai->ai_addrlen = 0; 1197 ai->ai_flags = AI_CANONNAME; 1198 ai->ai_canonname = estrdup("Default"); 1199 ai->ai_addr = NULL; 1200 ai->ai_next = NULL; 1201 grp->gr_ptr.gt_addrinfo = ai; 1202 1203 } else if ((opt_flags & OP_NET) && tgrp->gr_next) { 1204 /* 1205 * Don't allow a network export coincide with a list of 1206 * host(s) on the same line. 1207 */ 1208 syslog(LOG_ERR, 1209 "\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed", 1210 line, (unsigned long)lineno); 1211 goto badline; 1212 } 1213 /* 1214 * Loop through hosts, pushing the exports into the kernel. 1215 * After loop, tgrp points to the start of the list and 1216 * grp points to the last entry in the list. 1217 */ 1218 grp = tgrp; 1219 do { 1220 if (do_nfssvc(line, lineno, ep, grp, exflags, &anon, 1221 dirp, dirplen, &fsb)) 1222 goto badline; 1223 } while (grp->gr_next && (grp = grp->gr_next)); 1224 1225 /* 1226 * Success. Update the data structures. 1227 */ 1228 if (has_host) { 1229 hang_dirp(dirhead, tgrp, ep, opt_flags); 1230 grp->gr_next = grphead; 1231 grphead = tgrp; 1232 } else { 1233 hang_dirp(dirhead, NULL, ep, opt_flags); 1234 free_grp(tgrp); 1235 } 1236 tgrp = NULL; 1237 dirhead = NULL; 1238 if ((ep->ex_flag & EX_LINKED) == 0) { 1239 ep2 = exphead; 1240 epp = &exphead; 1241 1242 /* 1243 * Insert in the list in alphabetical order. 1244 */ 1245 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) { 1246 epp = &ep2->ex_next; 1247 ep2 = ep2->ex_next; 1248 } 1249 if (ep2) 1250 ep->ex_next = ep2; 1251 *epp = ep; 1252 ep->ex_flag |= EX_LINKED; 1253 } 1254 goto nextline; 1255 badline: 1256 free_exp_grp(ep, grp); 1257 nextline: 1258 if (dirhead) { 1259 free_dir(dirhead); 1260 dirhead = NULL; 1261 } 1262 free(line); 1263 } 1264 (void)fclose(exp_file); 1265 } 1266 1267 /* 1268 * Allocate an export list element 1269 */ 1270 static struct exportlist * 1271 get_exp(void) 1272 { 1273 struct exportlist *ep; 1274 1275 ep = emalloc(sizeof(struct exportlist)); 1276 (void)memset(ep, 0, sizeof(struct exportlist)); 1277 return (ep); 1278 } 1279 1280 /* 1281 * Allocate a group list element 1282 */ 1283 static struct grouplist * 1284 get_grp(void) 1285 { 1286 struct grouplist *gp; 1287 1288 gp = emalloc(sizeof(struct grouplist)); 1289 (void)memset(gp, 0, sizeof(struct grouplist)); 1290 return (gp); 1291 } 1292 1293 /* 1294 * Clean up upon an error in get_exportlist(). 1295 */ 1296 static void 1297 free_exp_grp(struct exportlist *ep, struct grouplist *grp) 1298 { 1299 struct grouplist *tgrp; 1300 1301 if (ep && (ep->ex_flag & EX_LINKED) == 0) 1302 free_exp(ep); 1303 while (grp) { 1304 tgrp = grp; 1305 grp = grp->gr_next; 1306 free_grp(tgrp); 1307 } 1308 } 1309 1310 /* 1311 * Search the export list for a matching fs. 1312 */ 1313 static struct exportlist * 1314 ex_search(fsid_t *fsid) 1315 { 1316 #ifdef MOUNTD_RUMP 1317 return exphead; 1318 #else 1319 struct exportlist *ep; 1320 1321 ep = exphead; 1322 while (ep) { 1323 if (ep->ex_fs.__fsid_val[0] == fsid->__fsid_val[0] && 1324 ep->ex_fs.__fsid_val[1] == fsid->__fsid_val[1]) 1325 return (ep); 1326 ep = ep->ex_next; 1327 } 1328 return (ep); 1329 #endif 1330 } 1331 1332 /* 1333 * Add a directory path to the list. 1334 */ 1335 static char * 1336 add_expdir(struct dirlist **dpp, char *cp, int len) 1337 { 1338 struct dirlist *dp; 1339 1340 dp = emalloc(sizeof(struct dirlist) + len); 1341 dp->dp_left = *dpp; 1342 dp->dp_right = NULL; 1343 dp->dp_flag = 0; 1344 dp->dp_hosts = NULL; 1345 (void)strcpy(dp->dp_dirp, cp); 1346 *dpp = dp; 1347 return (dp->dp_dirp); 1348 } 1349 1350 /* 1351 * Hang the dir list element off the dirpath binary tree as required 1352 * and update the entry for host. 1353 */ 1354 void 1355 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, 1356 int flags) 1357 { 1358 struct hostlist *hp; 1359 struct dirlist *dp2; 1360 1361 if (flags & OP_ALLDIRS) { 1362 if (ep->ex_defdir) 1363 free(dp); 1364 else 1365 ep->ex_defdir = dp; 1366 if (grp == NULL) { 1367 ep->ex_defdir->dp_flag |= DP_DEFSET; 1368 if (flags & OP_KERB) 1369 ep->ex_defdir->dp_flag |= DP_KERB; 1370 if (flags & OP_NORESMNT) 1371 ep->ex_defdir->dp_flag |= DP_NORESMNT; 1372 } else 1373 while (grp) { 1374 hp = get_ht(); 1375 if (flags & OP_KERB) 1376 hp->ht_flag |= DP_KERB; 1377 if (flags & OP_NORESMNT) 1378 hp->ht_flag |= DP_NORESMNT; 1379 hp->ht_grp = grp; 1380 hp->ht_next = ep->ex_defdir->dp_hosts; 1381 ep->ex_defdir->dp_hosts = hp; 1382 grp = grp->gr_next; 1383 } 1384 } else { 1385 1386 /* 1387 * Loop through the directories adding them to the tree. 1388 */ 1389 while (dp) { 1390 dp2 = dp->dp_left; 1391 add_dlist(&ep->ex_dirl, dp, grp, flags); 1392 dp = dp2; 1393 } 1394 } 1395 } 1396 1397 /* 1398 * Traverse the binary tree either updating a node that is already there 1399 * for the new directory or adding the new node. 1400 */ 1401 static void 1402 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, 1403 int flags) 1404 { 1405 struct dirlist *dp; 1406 struct hostlist *hp; 1407 int cmp; 1408 1409 dp = *dpp; 1410 if (dp) { 1411 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); 1412 if (cmp > 0) { 1413 add_dlist(&dp->dp_left, newdp, grp, flags); 1414 return; 1415 } else if (cmp < 0) { 1416 add_dlist(&dp->dp_right, newdp, grp, flags); 1417 return; 1418 } else 1419 free(newdp); 1420 } else { 1421 dp = newdp; 1422 dp->dp_left = NULL; 1423 *dpp = dp; 1424 } 1425 if (grp) { 1426 1427 /* 1428 * Hang all of the host(s) off of the directory point. 1429 */ 1430 do { 1431 hp = get_ht(); 1432 if (flags & OP_KERB) 1433 hp->ht_flag |= DP_KERB; 1434 if (flags & OP_NORESMNT) 1435 hp->ht_flag |= DP_NORESMNT; 1436 hp->ht_grp = grp; 1437 hp->ht_next = dp->dp_hosts; 1438 dp->dp_hosts = hp; 1439 grp = grp->gr_next; 1440 } while (grp); 1441 } else { 1442 dp->dp_flag |= DP_DEFSET; 1443 if (flags & OP_KERB) 1444 dp->dp_flag |= DP_KERB; 1445 if (flags & OP_NORESMNT) 1446 dp->dp_flag |= DP_NORESMNT; 1447 } 1448 } 1449 1450 /* 1451 * Search for a dirpath on the export point. 1452 */ 1453 static struct dirlist * 1454 dirp_search(struct dirlist *dp, char *dirp) 1455 { 1456 int cmp; 1457 1458 if (dp) { 1459 cmp = strcmp(dp->dp_dirp, dirp); 1460 if (cmp > 0) 1461 return (dirp_search(dp->dp_left, dirp)); 1462 else if (cmp < 0) 1463 return (dirp_search(dp->dp_right, dirp)); 1464 else 1465 return (dp); 1466 } 1467 return (dp); 1468 } 1469 1470 /* 1471 * Some helper functions for netmasks. They all assume masks in network 1472 * order (big endian). 1473 */ 1474 static int 1475 bitcmp(void *dst, void *src, int bitlen) 1476 { 1477 int i; 1478 u_int8_t *p1 = dst, *p2 = src; 1479 u_int8_t bitmask; 1480 int bytelen, bitsleft; 1481 1482 bytelen = bitlen / 8; 1483 bitsleft = bitlen % 8; 1484 1485 if (debug) { 1486 printf("comparing:\n"); 1487 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++) 1488 printf("%02x", p1[i]); 1489 printf("\n"); 1490 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++) 1491 printf("%02x", p2[i]); 1492 printf("\n"); 1493 } 1494 1495 for (i = 0; i < bytelen; i++) { 1496 if (*p1 != *p2) 1497 return 1; 1498 p1++; 1499 p2++; 1500 } 1501 1502 for (i = 0; i < bitsleft; i++) { 1503 bitmask = 1 << (7 - i); 1504 if ((*p1 & bitmask) != (*p2 & bitmask)) 1505 return 1; 1506 } 1507 1508 return 0; 1509 } 1510 1511 static int 1512 netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen) 1513 { 1514 void *src, *dst; 1515 1516 if (s1->sa_family != s2->sa_family) 1517 return 1; 1518 1519 switch (s1->sa_family) { 1520 case AF_INET: 1521 src = &((struct sockaddr_in *)s1)->sin_addr; 1522 dst = &((struct sockaddr_in *)s2)->sin_addr; 1523 if (bitlen > (int)sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8) 1524 return 1; 1525 break; 1526 case AF_INET6: 1527 src = &((struct sockaddr_in6 *)s1)->sin6_addr; 1528 dst = &((struct sockaddr_in6 *)s2)->sin6_addr; 1529 if (((struct sockaddr_in6 *)s1)->sin6_scope_id != 1530 ((struct sockaddr_in6 *)s2)->sin6_scope_id) 1531 return 1; 1532 if (bitlen > (int)sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8) 1533 return 1; 1534 break; 1535 default: 1536 return 1; 1537 } 1538 1539 return bitcmp(src, dst, bitlen); 1540 } 1541 1542 static int 1543 allones(struct sockaddr_storage *ssp, int bitlen) 1544 { 1545 u_int8_t *p; 1546 int bytelen, bitsleft, i; 1547 int zerolen; 1548 1549 switch (ssp->ss_family) { 1550 case AF_INET: 1551 p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr; 1552 zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr); 1553 break; 1554 case AF_INET6: 1555 p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr; 1556 zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr); 1557 break; 1558 default: 1559 return -1; 1560 } 1561 1562 memset(p, 0, zerolen); 1563 1564 bytelen = bitlen / 8; 1565 bitsleft = bitlen % 8; 1566 1567 if (bytelen > zerolen) 1568 return -1; 1569 1570 for (i = 0; i < bytelen; i++) 1571 *p++ = 0xff; 1572 1573 for (i = 0; i < bitsleft; i++) 1574 *p |= 1 << (7 - i); 1575 1576 return 0; 1577 } 1578 1579 static int 1580 sacmp(struct sockaddr *sa1, struct sockaddr *sa2) 1581 { 1582 void *p1, *p2; 1583 int len; 1584 1585 if (sa1->sa_family != sa2->sa_family) 1586 return 1; 1587 1588 switch (sa1->sa_family) { 1589 case AF_INET: 1590 p1 = &((struct sockaddr_in *)sa1)->sin_addr; 1591 p2 = &((struct sockaddr_in *)sa2)->sin_addr; 1592 len = 4; 1593 break; 1594 case AF_INET6: 1595 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; 1596 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; 1597 len = 16; 1598 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 1599 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 1600 return 1; 1601 break; 1602 default: 1603 return 1; 1604 } 1605 1606 return memcmp(p1, p2, len); 1607 } 1608 1609 /* 1610 * Scan for a host match in a directory tree. 1611 */ 1612 static int 1613 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, 1614 int *hostsetp) 1615 { 1616 struct hostlist *hp; 1617 struct grouplist *grp; 1618 struct addrinfo *ai; 1619 1620 if (dp) { 1621 if (dp->dp_flag & DP_DEFSET) 1622 *defsetp = dp->dp_flag; 1623 hp = dp->dp_hosts; 1624 while (hp) { 1625 grp = hp->ht_grp; 1626 switch (grp->gr_type) { 1627 case GT_HOST: 1628 ai = grp->gr_ptr.gt_addrinfo; 1629 for (; ai; ai = ai->ai_next) { 1630 if (!sacmp(ai->ai_addr, saddr)) { 1631 *hostsetp = 1632 (hp->ht_flag | DP_HOSTSET); 1633 return (1); 1634 } 1635 } 1636 break; 1637 case GT_NET: 1638 if (!netpartcmp(saddr, 1639 (struct sockaddr *) 1640 &grp->gr_ptr.gt_net.nt_net, 1641 grp->gr_ptr.gt_net.nt_len)) { 1642 *hostsetp = (hp->ht_flag | DP_HOSTSET); 1643 return (1); 1644 } 1645 break; 1646 }; 1647 hp = hp->ht_next; 1648 } 1649 } 1650 return (0); 1651 } 1652 1653 /* 1654 * Scan tree for a host that matches the address. 1655 */ 1656 static int 1657 scan_tree(struct dirlist *dp, struct sockaddr *saddr) 1658 { 1659 int defset, hostset; 1660 1661 if (dp) { 1662 if (scan_tree(dp->dp_left, saddr)) 1663 return (1); 1664 if (chk_host(dp, saddr, &defset, &hostset)) 1665 return (1); 1666 if (scan_tree(dp->dp_right, saddr)) 1667 return (1); 1668 } 1669 return (0); 1670 } 1671 1672 /* 1673 * Traverse the dirlist tree and free it up. 1674 */ 1675 static void 1676 free_dir(struct dirlist *dp) 1677 { 1678 1679 if (dp) { 1680 free_dir(dp->dp_left); 1681 free_dir(dp->dp_right); 1682 free_host(dp->dp_hosts); 1683 free(dp); 1684 } 1685 } 1686 1687 /* 1688 * Parse the option string and update fields. 1689 * Option arguments may either be -<option>=<value> or 1690 * -<option> <value> 1691 */ 1692 static int 1693 do_opt(const char *line, size_t lineno, char **cpp, char **endcpp, 1694 struct exportlist *ep, struct grouplist *grp, int *has_hostp, 1695 int *exflagsp, struct uucred *cr) 1696 { 1697 char *cpoptarg, *cpoptend; 1698 char *cp, *cpopt, savedc, savedc2; 1699 char *endcp = NULL; /* XXX: GCC */ 1700 int allflag, usedarg; 1701 1702 cpopt = *cpp; 1703 cpopt++; 1704 cp = *endcpp; 1705 savedc = *cp; 1706 *cp = '\0'; 1707 while (cpopt && *cpopt) { 1708 allflag = 1; 1709 usedarg = -2; 1710 savedc2 = '\0'; 1711 if ((cpoptend = strchr(cpopt, ',')) != NULL) { 1712 *cpoptend++ = '\0'; 1713 if ((cpoptarg = strchr(cpopt, '=')) != NULL) 1714 *cpoptarg++ = '\0'; 1715 } else { 1716 if ((cpoptarg = strchr(cpopt, '=')) != NULL) 1717 *cpoptarg++ = '\0'; 1718 else { 1719 *cp = savedc; 1720 nextfield(&cp, &endcp); 1721 **endcpp = '\0'; 1722 if (endcp > cp && *cp != '-') { 1723 cpoptarg = cp; 1724 savedc2 = *endcp; 1725 *endcp = '\0'; 1726 usedarg = 0; 1727 } 1728 } 1729 } 1730 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) { 1731 *exflagsp |= MNT_EXRDONLY; 1732 } else if (cpoptarg && (!strcmp(cpopt, "maproot") || 1733 !(allflag = strcmp(cpopt, "mapall")) || 1734 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) { 1735 usedarg++; 1736 parsecred(cpoptarg, cr); 1737 if (allflag == 0) { 1738 *exflagsp |= MNT_EXPORTANON; 1739 opt_flags |= OP_MAPALL; 1740 } else 1741 opt_flags |= OP_MAPROOT; 1742 } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) { 1743 *exflagsp |= MNT_EXKERB; 1744 opt_flags |= OP_KERB; 1745 } else if (cpoptarg && (!strcmp(cpopt, "mask") || 1746 !strcmp(cpopt, "m"))) { 1747 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) { 1748 syslog(LOG_ERR, 1749 "\"%s\", line %ld: Bad mask: %s", 1750 line, (unsigned long)lineno, cpoptarg); 1751 return (1); 1752 } 1753 usedarg++; 1754 opt_flags |= OP_MASK; 1755 } else if (cpoptarg && (!strcmp(cpopt, "network") || 1756 !strcmp(cpopt, "n"))) { 1757 if (strchr(cpoptarg, '/') != NULL) { 1758 if (debug) 1759 fprintf(stderr, "setting OP_MASKLEN\n"); 1760 opt_flags |= OP_MASKLEN; 1761 } 1762 if (grp->gr_type != GT_NULL) { 1763 syslog(LOG_ERR, 1764 "\"%s\", line %ld: Network/host conflict", 1765 line, (unsigned long)lineno); 1766 return (1); 1767 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) { 1768 syslog(LOG_ERR, 1769 "\"%s\", line %ld: Bad net: %s", 1770 line, (unsigned long)lineno, cpoptarg); 1771 return (1); 1772 } 1773 grp->gr_type = GT_NET; 1774 *has_hostp = 1; 1775 usedarg++; 1776 opt_flags |= OP_NET; 1777 } else if (!strcmp(cpopt, "alldirs")) { 1778 opt_flags |= OP_ALLDIRS; 1779 } else if (!strcmp(cpopt, "noresvmnt")) { 1780 opt_flags |= OP_NORESMNT; 1781 } else if (!strcmp(cpopt, "noresvport")) { 1782 opt_flags |= OP_NORESPORT; 1783 *exflagsp |= MNT_EXNORESPORT; 1784 } else if (!strcmp(cpopt, "public")) { 1785 *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC); 1786 opt_flags |= OP_NORESPORT; 1787 } else if (!strcmp(cpopt, "webnfs")) { 1788 *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC | 1789 MNT_EXRDONLY | MNT_EXPORTANON); 1790 opt_flags |= (OP_MAPALL | OP_NORESPORT); 1791 } else if (cpoptarg && !strcmp(cpopt, "index")) { 1792 ep->ex_indexfile = strdup(cpoptarg); 1793 } else { 1794 syslog(LOG_ERR, 1795 "\"%s\", line %ld: Bad opt %s", 1796 line, (unsigned long)lineno, cpopt); 1797 return (1); 1798 } 1799 if (usedarg >= 0) { 1800 *endcp = savedc2; 1801 **endcpp = savedc; 1802 if (usedarg > 0) { 1803 *cpp = cp; 1804 *endcpp = endcp; 1805 } 1806 return (0); 1807 } 1808 cpopt = cpoptend; 1809 } 1810 **endcpp = savedc; 1811 return (0); 1812 } 1813 1814 /* 1815 * Translate a character string to the corresponding list of network 1816 * addresses for a hostname. 1817 */ 1818 static int 1819 get_host(const char *line, size_t lineno, const char *cp, 1820 struct grouplist *grp) 1821 { 1822 struct addrinfo *ai, hints; 1823 int ecode; 1824 char host[NI_MAXHOST]; 1825 1826 if (grp->gr_type != GT_NULL) { 1827 syslog(LOG_ERR, 1828 "\"%s\", line %ld: Bad netgroup type for ip host %s", 1829 line, (unsigned long)lineno, cp); 1830 return (1); 1831 } 1832 memset(&hints, 0, sizeof hints); 1833 hints.ai_flags = AI_CANONNAME; 1834 hints.ai_protocol = IPPROTO_UDP; 1835 ecode = getaddrinfo(cp, NULL, &hints, &ai); 1836 if (ecode != 0) { 1837 syslog(LOG_ERR, "\"%s\", line %ld: can't get address info for " 1838 "host %s", 1839 line, (long)lineno, cp); 1840 return 1; 1841 } 1842 grp->gr_type = GT_HOST; 1843 grp->gr_ptr.gt_addrinfo = ai; 1844 while (ai != NULL) { 1845 if (ai->ai_canonname == NULL) { 1846 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, 1847 sizeof host, NULL, 0, ninumeric) != 0) 1848 strlcpy(host, "?", sizeof(host)); 1849 ai->ai_canonname = estrdup(host); 1850 ai->ai_flags |= AI_CANONNAME; 1851 } else 1852 ai->ai_flags &= ~AI_CANONNAME; 1853 if (debug) 1854 (void)fprintf(stderr, "got host %s\n", ai->ai_canonname); 1855 ai = ai->ai_next; 1856 } 1857 return (0); 1858 } 1859 1860 /* 1861 * Free up an exports list component 1862 */ 1863 static void 1864 free_exp(struct exportlist *ep) 1865 { 1866 1867 if (ep->ex_defdir) { 1868 free_host(ep->ex_defdir->dp_hosts); 1869 free(ep->ex_defdir); 1870 } 1871 if (ep->ex_fsdir) 1872 free(ep->ex_fsdir); 1873 if (ep->ex_indexfile) 1874 free(ep->ex_indexfile); 1875 free_dir(ep->ex_dirl); 1876 free(ep); 1877 } 1878 1879 /* 1880 * Free hosts. 1881 */ 1882 static void 1883 free_host(struct hostlist *hp) 1884 { 1885 struct hostlist *hp2; 1886 1887 while (hp) { 1888 hp2 = hp; 1889 hp = hp->ht_next; 1890 free(hp2); 1891 } 1892 } 1893 1894 static struct hostlist * 1895 get_ht(void) 1896 { 1897 struct hostlist *hp; 1898 1899 hp = emalloc(sizeof(struct hostlist)); 1900 hp->ht_next = NULL; 1901 hp->ht_flag = 0; 1902 return (hp); 1903 } 1904 1905 /* 1906 * Do the nfssvc syscall to push the export info into the kernel. 1907 */ 1908 static int 1909 do_nfssvc(const char *line, size_t lineno, struct exportlist *ep, 1910 struct grouplist *grp, int exflags, struct uucred *anoncrp, 1911 char *dirp, int dirplen, struct statvfs *fsb) 1912 { 1913 struct sockaddr *addrp; 1914 struct sockaddr_storage ss; 1915 struct addrinfo *ai; 1916 int addrlen; 1917 int done; 1918 struct export_args export; 1919 1920 export.ex_flags = exflags; 1921 export.ex_anon = *anoncrp; 1922 export.ex_indexfile = ep->ex_indexfile; 1923 if (grp->gr_type == GT_HOST) { 1924 ai = grp->gr_ptr.gt_addrinfo; 1925 addrp = ai->ai_addr; 1926 addrlen = ai->ai_addrlen; 1927 } else { 1928 addrp = NULL; 1929 ai = NULL; /* XXXGCC -Wuninitialized */ 1930 addrlen = 0; /* XXXGCC -Wuninitialized */ 1931 } 1932 done = FALSE; 1933 while (!done) { 1934 struct mountd_exports_list mel; 1935 1936 switch (grp->gr_type) { 1937 case GT_HOST: 1938 if (addrp != NULL && addrp->sa_family == AF_INET6 && 1939 have_v6 == 0) 1940 goto skip; 1941 export.ex_addr = addrp; 1942 export.ex_addrlen = addrlen; 1943 export.ex_masklen = 0; 1944 break; 1945 case GT_NET: 1946 export.ex_addr = (struct sockaddr *) 1947 &grp->gr_ptr.gt_net.nt_net; 1948 if (export.ex_addr->sa_family == AF_INET6 && 1949 have_v6 == 0) 1950 goto skip; 1951 export.ex_addrlen = export.ex_addr->sa_len; 1952 memset(&ss, 0, sizeof ss); 1953 ss.ss_family = export.ex_addr->sa_family; 1954 ss.ss_len = export.ex_addr->sa_len; 1955 if (allones(&ss, grp->gr_ptr.gt_net.nt_len) != 0) { 1956 syslog(LOG_ERR, 1957 "\"%s\", line %ld: Bad network flag", 1958 line, (unsigned long)lineno); 1959 return (1); 1960 } 1961 export.ex_mask = (struct sockaddr *)&ss; 1962 export.ex_masklen = ss.ss_len; 1963 break; 1964 default: 1965 syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type", 1966 line, (unsigned long)lineno); 1967 return (1); 1968 }; 1969 1970 /* 1971 * XXX: 1972 * Maybe I should just use the fsb->f_mntonname path? 1973 */ 1974 1975 mel.mel_path = dirp; 1976 mel.mel_nexports = 1; 1977 mel.mel_exports = &export; 1978 1979 if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) != 0) { 1980 syslog(LOG_ERR, 1981 "\"%s\", line %ld: Can't change attributes for %s to %s: %m", 1982 line, (unsigned long)lineno, 1983 dirp, (grp->gr_type == GT_HOST) ? 1984 grp->gr_ptr.gt_addrinfo->ai_canonname : 1985 (grp->gr_type == GT_NET) ? 1986 grp->gr_ptr.gt_net.nt_name : 1987 "Unknown"); 1988 return (1); 1989 } 1990 skip: 1991 if (addrp) { 1992 ai = ai->ai_next; 1993 if (ai == NULL) 1994 done = TRUE; 1995 else { 1996 addrp = ai->ai_addr; 1997 addrlen = ai->ai_addrlen; 1998 } 1999 } else 2000 done = TRUE; 2001 } 2002 return (0); 2003 } 2004 2005 /* 2006 * Parse out the next white space separated field 2007 */ 2008 static void 2009 nextfield(char **cp, char **endcp) 2010 { 2011 char *p; 2012 2013 p = *cp; 2014 while (*p == ' ' || *p == '\t') 2015 p++; 2016 if (*p == '\n' || *p == '\0') 2017 *cp = *endcp = p; 2018 else { 2019 *cp = p++; 2020 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 2021 p++; 2022 *endcp = p; 2023 } 2024 } 2025 2026 /* 2027 * Parse a description of a credential. 2028 */ 2029 static void 2030 parsecred(char *namelist, struct uucred *cr) 2031 { 2032 char *username; 2033 int cnt; 2034 char *names; 2035 struct passwd *pw; 2036 struct group *gr; 2037 int ngroups; 2038 gid_t usergroups[NGROUPS + 1]; 2039 2040 /* 2041 * Set up the unprivileged user. 2042 */ 2043 *cr = def_anon; 2044 /* 2045 * Get the user's password table entry. 2046 */ 2047 names = strsep(&namelist, " \t\n"); 2048 username = strsep(&names, ":"); 2049 if (isdigit((unsigned char)*username) || *username == '-') 2050 pw = getpwuid(atoi(username)); 2051 else 2052 pw = getpwnam(username); 2053 /* 2054 * Credentials specified as those of a user. 2055 */ 2056 if (names == NULL) { 2057 if (pw == NULL) { 2058 syslog(LOG_ERR, "Unknown user: %s", username); 2059 return; 2060 } 2061 cr->cr_uid = pw->pw_uid; 2062 ngroups = NGROUPS + 1; 2063 if (getgrouplist(pw->pw_name, pw->pw_gid, usergroups, &ngroups)) 2064 syslog(LOG_ERR, "Too many groups for user %s", username); 2065 /* 2066 * Convert from int's to gid_t's and compress out duplicate 2067 */ 2068 cr->cr_ngroups = ngroups - 1; 2069 cr->cr_gid = usergroups[0]; 2070 for (cnt = 1; cnt < ngroups; cnt++) 2071 cr->cr_groups[cnt - 1] = usergroups[cnt]; 2072 return; 2073 } 2074 /* 2075 * Explicit credential specified as a colon separated list: 2076 * uid:gid:gid:... 2077 */ 2078 if (pw != NULL) 2079 cr->cr_uid = pw->pw_uid; 2080 else if (isdigit((unsigned char)*username) || *username == '-') 2081 cr->cr_uid = atoi(username); 2082 else { 2083 syslog(LOG_ERR, "Unknown user: %s", username); 2084 return; 2085 } 2086 cr->cr_ngroups = 0; 2087 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) { 2088 username = strsep(&names, ":"); 2089 if (isdigit((unsigned char)*username) || *username == '-') { 2090 cr->cr_groups[cr->cr_ngroups++] = atoi(username); 2091 } else { 2092 if ((gr = getgrnam(username)) == NULL) { 2093 syslog(LOG_ERR, "Unknown group: %s", username); 2094 continue; 2095 } 2096 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid; 2097 } 2098 } 2099 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS) 2100 syslog(LOG_ERR, "Too many groups"); 2101 } 2102 2103 #define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50) 2104 /* 2105 * Routines that maintain the remote mounttab 2106 */ 2107 static void 2108 get_mountlist(void) 2109 { 2110 struct mountlist *mlp, **mlpp; 2111 char *host, *dirp, *cp; 2112 char str[STRSIZ]; 2113 FILE *mlfile; 2114 2115 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) { 2116 syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST); 2117 return; 2118 } 2119 mlpp = &mlhead; 2120 while (fgets(str, STRSIZ, mlfile) != NULL) { 2121 cp = str; 2122 host = strsep(&cp, " \t\n"); 2123 dirp = strsep(&cp, " \t\n"); 2124 if (host == NULL || dirp == NULL) 2125 continue; 2126 mlp = emalloc(sizeof(*mlp)); 2127 (void)strncpy(mlp->ml_host, host, RPCMNT_NAMELEN); 2128 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2129 (void)strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2130 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2131 mlp->ml_next = NULL; 2132 *mlpp = mlp; 2133 mlpp = &mlp->ml_next; 2134 } 2135 (void)fclose(mlfile); 2136 } 2137 2138 static int 2139 del_mlist(char *hostp, char *dirp, struct sockaddr *saddr) 2140 { 2141 struct mountlist *mlp, **mlpp; 2142 struct mountlist *mlp2; 2143 u_short sport; 2144 FILE *mlfile; 2145 int fnd = 0, ret = 0; 2146 char host[NI_MAXHOST]; 2147 2148 switch (saddr->sa_family) { 2149 case AF_INET6: 2150 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); 2151 break; 2152 case AF_INET: 2153 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); 2154 break; 2155 default: 2156 return -1; 2157 } 2158 mlpp = &mlhead; 2159 mlp = mlhead; 2160 while (mlp) { 2161 if (!strcmp(mlp->ml_host, hostp) && 2162 (!dirp || !strcmp(mlp->ml_dirp, dirp))) { 2163 if (!(mlp->ml_flag & DP_NORESMNT) && 2164 sport >= IPPORT_RESERVED) { 2165 if (getnameinfo(saddr, saddr->sa_len, host, 2166 sizeof host, NULL, 0, ninumeric) != 0) 2167 strlcpy(host, "?", sizeof(host)); 2168 syslog(LOG_NOTICE, 2169 "Umount request for %s:%s from %s refused\n", 2170 mlp->ml_host, mlp->ml_dirp, host); 2171 ret = -1; 2172 goto cont; 2173 } 2174 fnd = 1; 2175 mlp2 = mlp; 2176 *mlpp = mlp = mlp->ml_next; 2177 free(mlp2); 2178 } else { 2179 cont: 2180 mlpp = &mlp->ml_next; 2181 mlp = mlp->ml_next; 2182 } 2183 } 2184 if (fnd) { 2185 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) { 2186 syslog(LOG_ERR, "Can't update %s: %m", 2187 _PATH_RMOUNTLIST); 2188 return ret; 2189 } 2190 mlp = mlhead; 2191 while (mlp) { 2192 (void)fprintf(mlfile, "%s %s\n", mlp->ml_host, 2193 mlp->ml_dirp); 2194 mlp = mlp->ml_next; 2195 } 2196 (void)fclose(mlfile); 2197 } 2198 return ret; 2199 } 2200 2201 static void 2202 add_mlist(char *hostp, char *dirp, int flags) 2203 { 2204 struct mountlist *mlp, **mlpp; 2205 FILE *mlfile; 2206 2207 mlpp = &mlhead; 2208 mlp = mlhead; 2209 while (mlp) { 2210 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp)) 2211 return; 2212 mlpp = &mlp->ml_next; 2213 mlp = mlp->ml_next; 2214 } 2215 mlp = emalloc(sizeof(*mlp)); 2216 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN); 2217 mlp->ml_host[RPCMNT_NAMELEN] = '\0'; 2218 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN); 2219 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0'; 2220 mlp->ml_flag = flags; 2221 mlp->ml_next = NULL; 2222 *mlpp = mlp; 2223 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) { 2224 syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST); 2225 return; 2226 } 2227 (void)fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp); 2228 (void)fclose(mlfile); 2229 } 2230 2231 #ifndef MOUNTD_RUMP 2232 static int 2233 umntall_each(caddr_t resultsp, struct sockaddr_in *raddr) 2234 { 2235 return (1); 2236 } 2237 #endif 2238 2239 /* 2240 * This function is called via. SIGTERM when the system is going down. 2241 * It sends a broadcast RPCMNT_UMNTALL. 2242 */ 2243 /* ARGSUSED */ 2244 static void 2245 send_umntall(int n) 2246 { 2247 #ifndef MOUNTD_RUMP 2248 (void)clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL, 2249 (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL, 2250 (resultproc_t)umntall_each); 2251 #endif 2252 exit(0); 2253 } 2254 2255 2256 /* 2257 * Free up a group list. 2258 */ 2259 static void 2260 free_grp(struct grouplist *grp) 2261 { 2262 2263 if (grp->gr_type == GT_HOST) { 2264 if (grp->gr_ptr.gt_addrinfo != NULL) 2265 freeaddrinfo(grp->gr_ptr.gt_addrinfo); 2266 } else if (grp->gr_type == GT_NET) { 2267 if (grp->gr_ptr.gt_net.nt_name) 2268 free(grp->gr_ptr.gt_net.nt_name); 2269 } 2270 free(grp); 2271 } 2272 2273 #if 0 2274 static void 2275 SYSLOG(int pri, const char *fmt,...) 2276 { 2277 va_list ap; 2278 2279 va_start(ap, fmt); 2280 2281 if (debug) 2282 vfprintf(stderr, fmt, ap); 2283 else 2284 vsyslog(pri, fmt, ap); 2285 2286 va_end(ap); 2287 } 2288 #endif 2289 2290 /* 2291 * Check options for consistency. 2292 */ 2293 static int 2294 check_options(const char *line, size_t lineno, struct dirlist *dp) 2295 { 2296 2297 if (dp == NULL) { 2298 syslog(LOG_ERR, 2299 "\"%s\", line %ld: missing directory list", 2300 line, (unsigned long)lineno); 2301 return (1); 2302 } 2303 if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) || 2304 (opt_flags & (OP_MAPROOT|OP_KERB)) == (OP_MAPROOT|OP_KERB) || 2305 (opt_flags & (OP_MAPALL|OP_KERB)) == (OP_MAPALL|OP_KERB)) { 2306 syslog(LOG_ERR, 2307 "\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive", 2308 line, (unsigned long)lineno); 2309 return (1); 2310 } 2311 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) { 2312 syslog(LOG_ERR, "\"%s\", line %ld: -mask requires -net", 2313 line, (unsigned long)lineno); 2314 return (1); 2315 } 2316 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN) != 0) { 2317 syslog(LOG_ERR, "\"%s\", line %ld: /pref and -mask mutually" 2318 " exclusive", 2319 line, (unsigned long)lineno); 2320 return (1); 2321 } 2322 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) { 2323 syslog(LOG_ERR, 2324 "\"%s\", line %ld: -alldirs has multiple directories", 2325 line, (unsigned long)lineno); 2326 return (1); 2327 } 2328 return (0); 2329 } 2330 2331 /* 2332 * Check an absolute directory path for any symbolic links. Return true 2333 * if no symbolic links are found. 2334 */ 2335 static int 2336 check_dirpath(const char *line, size_t lineno, char *dirp) 2337 { 2338 char *cp; 2339 struct stat sb; 2340 const char *file = ""; 2341 2342 for (cp = dirp + 1; *cp; cp++) { 2343 if (*cp == '/') { 2344 *cp = '\0'; 2345 if (lstat(dirp, &sb) == -1) 2346 goto bad; 2347 if (!S_ISDIR(sb.st_mode)) 2348 goto bad1; 2349 *cp = '/'; 2350 } 2351 } 2352 2353 cp = NULL; 2354 if (lstat(dirp, &sb) == -1) 2355 goto bad; 2356 2357 if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) { 2358 file = " file or a"; 2359 goto bad1; 2360 } 2361 2362 return 1; 2363 2364 bad: 2365 syslog(LOG_ERR, 2366 "\"%s\", line %ld: lstat for `%s' failed: %m", 2367 line, (unsigned long)lineno, dirp); 2368 if (cp) 2369 *cp = '/'; 2370 return 0; 2371 2372 bad1: 2373 syslog(LOG_ERR, 2374 "\"%s\", line %ld: `%s' is not a%s directory", 2375 line, (unsigned long)lineno, dirp, file); 2376 if (cp) 2377 *cp = '/'; 2378 return 0; 2379 } 2380 2381 static void 2382 bind_resv_port(int sock, sa_family_t family, in_port_t port) 2383 { 2384 struct sockaddr *sa; 2385 struct sockaddr_in sasin; 2386 struct sockaddr_in6 sasin6; 2387 2388 switch (family) { 2389 case AF_INET: 2390 (void)memset(&sasin, 0, sizeof(sasin)); 2391 sasin.sin_len = sizeof(sasin); 2392 sasin.sin_family = family; 2393 sasin.sin_port = htons(port); 2394 sa = (struct sockaddr *)(void *)&sasin; 2395 break; 2396 case AF_INET6: 2397 (void)memset(&sasin6, 0, sizeof(sasin6)); 2398 sasin6.sin6_len = sizeof(sasin6); 2399 sasin6.sin6_family = family; 2400 sasin6.sin6_port = htons(port); 2401 sa = (struct sockaddr *)(void *)&sasin6; 2402 break; 2403 default: 2404 syslog(LOG_ERR, "Unsupported address family %d", family); 2405 return; 2406 } 2407 if (bindresvport_sa(sock, sa) == -1) 2408 syslog(LOG_ERR, "Cannot bind to reserved port %d (%m)", port); 2409 } 2410 2411 /* ARGSUSED */ 2412 static void 2413 no_nfs(int sig) 2414 { 2415 syslog(LOG_ERR, "kernel NFS support not present; exiting"); 2416 exit(1); 2417 } 2418