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