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