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