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