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