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