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