1/* 2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights 7 * Reserved. This file contains Original Code and/or Modifications of 8 * Original Code as defined in and that are subject to the Apple Public 9 * Source License Version 1.1 (the "License"). You may not use this file 10 * except in compliance with the License. Please obtain a copy of the 11 * License at http://www.apple.com/publicsource and read it before using 12 * this file. 13 * 14 * The Original Code and all software distributed under the License are 15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the 19 * License for the specific language governing rights and limitations 20 * under the License. 21 * 22 * @APPLE_LICENSE_HEADER_END@ 23 */ 24/* 25 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved. 26 * Copyright (c) 1983, 1993, 1994 27 * The Regents of the University of California. All rights reserved. 28 * 29 * Redistribution and use in source and binary forms, with or without 30 * modification, are permitted provided that the following conditions 31 * are met: 32 * 1. Redistributions of source code must retain the above copyright 33 * notice, this list of conditions and the following disclaimer. 34 * 2. Redistributions in binary form must reproduce the above copyright 35 * notice, this list of conditions and the following disclaimer in the 36 * documentation and/or other materials provided with the distribution. 37 * 3. All advertising materials mentioning features or use of this software 38 * must display the following acknowledgement: 39 * This product includes software developed by the University of 40 * California, Berkeley and its contributors. 41 * This product includes software developed by Theo de Raadt. 42 * 4. Neither the name of the University nor the names of its contributors 43 * may be used to endorse or promote products derived from this software 44 * without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 * 58 * $FreeBSD: src/lib/libc/net/rcmd.c,v 1.23.2.5 2001/03/05 10:47:11 obrien Exp $ 59 */ 60 61#if defined(LIBC_SCCS) && !defined(lint) 62static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; 63#endif /* LIBC_SCCS and not lint */ 64 65#include <sys/param.h> 66#include <sys/socket.h> 67#include <sys/stat.h> 68 69#include <arpa/inet.h> 70 71#include <signal.h> 72#include <fcntl.h> 73#include <netdb.h> 74#include <unistd.h> 75#include <pwd.h> 76#include <errno.h> 77#include <stdio.h> 78#include <ctype.h> 79#include <string.h> 80#include <time.h> 81#ifdef YP 82#include <rpc/rpc.h> 83#include <rpcsvc/yp_prot.h> 84#include <rpcsvc/ypclnt.h> 85#endif 86 87#include <arpa/nameser_compat.h> 88#include <nameser.h> 89 90/* wrapper for KAME-special getnameinfo() */ 91#ifndef NI_WITHSCOPEID 92#define NI_WITHSCOPEID 0 93#endif 94 95#ifndef socklen_t 96#define socklen_t int 97#endif 98 99extern int innetgr __P(( const char *, const char *, const char *, const char * )); 100extern int bindresvport_sa(int, struct sockaddr *); 101 102#define max(a, b) ((a > b) ? a : b) 103 104int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *)); 105int __ivaliduser_af __P((FILE *,const void *, const char *, const char *, 106 int, int)); 107int __ivaliduser_sa __P((FILE *, const struct sockaddr *, socklen_t, 108 const char *,const char *)); 109static int __icheckhost __P((const struct sockaddr *, socklen_t, 110 const char *)); 111 112int 113rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) 114 char **ahost; 115 u_short rport; 116 const char *locuser, *remuser, *cmd; 117 int *fd2p; 118 int af; 119{ 120 struct addrinfo hints, *res, *ai; 121 struct sockaddr_storage from; 122 fd_set reads; 123 long oldmask; 124 pid_t pid; 125 int s, aport, lport, timo, error; 126 char c; 127 int refused, nres; 128 char num[8], paddr[NI_MAXHOST]; 129 static char canonnamebuf[MAXDNAME]; /* is it proper here? */ 130 131 pid = getpid(); 132 133 memset(&hints, 0, sizeof(hints)); 134 hints.ai_flags = AI_CANONNAME; 135 hints.ai_family = af; 136 hints.ai_socktype = SOCK_STREAM; 137 hints.ai_protocol = 0; 138 (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); 139 error = getaddrinfo(*ahost, num, &hints, &res); 140 if (error) { 141 fprintf(stderr, "rcmd: getaddrinfo: %s\n", 142 gai_strerror(error)); 143 if (error == EAI_SYSTEM) 144 fprintf(stderr, "rcmd: getaddrinfo: %s\n", 145 strerror(errno)); 146 return (-1); 147 } 148 149 if (res->ai_canonname 150 && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { 151 strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); 152 *ahost = canonnamebuf; 153 } 154 nres = 0; 155 for (ai = res; ai; ai = ai->ai_next) 156 nres++; 157 ai = res; 158 refused = 0; 159 oldmask = sigblock(sigmask(SIGURG)); 160 for (timo = 1, lport = IPPORT_RESERVED - 1;;) { 161 s = rresvport_af(&lport, ai->ai_family); 162 if (s < 0) { 163 if (errno != EAGAIN && ai->ai_next) { 164 ai = ai->ai_next; 165 continue; 166 } 167 if (errno == EAGAIN) 168 (void)fprintf(stderr, 169 "rcmd: socket: All ports in use\n"); 170 else 171 (void)fprintf(stderr, "rcmd: socket: %s\n", 172 strerror(errno)); 173 freeaddrinfo(res); 174 sigsetmask(oldmask); 175 return (-1); 176 } 177 fcntl(s, F_SETOWN, pid); 178 if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) 179 break; 180 (void)close(s); 181 if (errno == EADDRINUSE) { 182 lport--; 183 continue; 184 } 185 if (errno == ECONNREFUSED) 186 refused = 1; 187 if (ai->ai_next == NULL && (!refused || timo > 16)) { 188 (void)fprintf(stderr, "%s: %s\n", 189 *ahost, strerror(errno)); 190 freeaddrinfo(res); 191 sigsetmask(oldmask); 192 return (-1); 193 } 194 if (nres > 1) { 195 int oerrno = errno; 196 197 getnameinfo(ai->ai_addr, ai->ai_addrlen, 198 paddr, sizeof(paddr), 199 NULL, 0, 200 NI_NUMERICHOST|NI_WITHSCOPEID); 201 (void)fprintf(stderr, "connect to address %s: ", 202 paddr); 203 errno = oerrno; 204 perror(0); 205 } 206 if ((ai = ai->ai_next) == NULL) { 207 /* refused && timo <= 16 */ 208 struct timespec time_to_sleep, time_remaining; 209 210 time_to_sleep.tv_sec = timo; 211 time_to_sleep.tv_nsec = 0; 212 (void)nanosleep(&time_to_sleep, &time_remaining); 213 timo *= 2; 214 ai = res; 215 refused = 0; 216 } 217 if (nres > 1) { 218 getnameinfo(ai->ai_addr, ai->ai_addrlen, 219 paddr, sizeof(paddr), 220 NULL, 0, 221 NI_NUMERICHOST|NI_WITHSCOPEID); 222 fprintf(stderr, "Trying %s...\n", paddr); 223 } 224 } 225#if 0 226 /* 227 * try to rresvport() to the same port. This will make rresvport() 228 * fail it's first bind, resulting in it choosing a random port. 229 */ 230 lport--; 231#endif 232 if (fd2p == 0) { 233 write(s, "", 1); 234 lport = 0; 235 } else { 236 char num[8]; 237 int s2 = rresvport_af(&lport, ai->ai_family), s3; 238 unsigned int len = ai->ai_addrlen; 239 int nfds; 240 241 if (s2 < 0) 242 goto bad; 243 listen(s2, 1); 244 (void)snprintf(num, sizeof(num), "%d", lport); 245 if (write(s, num, strlen(num)+1) != strlen(num)+1) { 246 (void)fprintf(stderr, 247 "rcmd: write (setting up stderr): %s\n", 248 strerror(errno)); 249 (void)close(s2); 250 goto bad; 251 } 252 nfds = max(s, s2)+1; 253 if(nfds > FD_SETSIZE) { 254 fprintf(stderr, "rcmd: too many files\n"); 255 (void)close(s2); 256 goto bad; 257 } 258again: 259 FD_ZERO(&reads); 260 FD_SET(s, &reads); 261 FD_SET(s2, &reads); 262 errno = 0; 263 if (select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ 264 if (errno != 0) 265 (void)fprintf(stderr, 266 "rcmd: select (setting up stderr): %s\n", 267 strerror(errno)); 268 else 269 (void)fprintf(stderr, 270 "select: protocol failure in circuit setup\n"); 271 (void)close(s2); 272 goto bad; 273 } 274 s3 = accept(s2, (struct sockaddr *)&from, &len); 275 switch (from.ss_family) { 276 case AF_INET: 277 aport = ntohs(((struct sockaddr_in *)&from)->sin_port); 278 break; 279#ifdef INET6 280 case AF_INET6: 281 aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); 282 break; 283#endif 284 default: 285 aport = 0; /* error */ 286 break; 287 } 288 /* 289 * XXX careful for ftp bounce attacks. If discovered, shut them 290 * down and check for the real auxiliary channel to connect. 291 */ 292 if (aport == 20) { 293 close(s3); 294 goto again; 295 } 296 (void)close(s2); 297 if (s3 < 0) { 298 (void)fprintf(stderr, 299 "rcmd: accept: %s\n", strerror(errno)); 300 lport = 0; 301 goto bad; 302 } 303 *fd2p = s3; 304 if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { 305 (void)fprintf(stderr, 306 "socket: protocol failure in circuit setup.\n"); 307 goto bad2; 308 } 309 } 310 (void)write(s, locuser, strlen(locuser)+1); 311 (void)write(s, remuser, strlen(remuser)+1); 312 (void)write(s, cmd, strlen(cmd)+1); 313 if (read(s, &c, 1) != 1) { 314 (void)fprintf(stderr, 315 "rcmd: %s: %s\n", *ahost, strerror(errno)); 316 goto bad2; 317 } 318 if (c != 0) { 319 while (read(s, &c, 1) == 1) { 320 (void)write(STDERR_FILENO, &c, 1); 321 if (c == '\n') 322 break; 323 } 324 goto bad2; 325 } 326 sigsetmask(oldmask); 327 freeaddrinfo(res); 328 return (s); 329bad2: 330 if (lport) 331 (void)close(*fd2p); 332bad: 333 (void)close(s); 334 sigsetmask(oldmask); 335 freeaddrinfo(res); 336 return (-1); 337} 338 339int 340rcmd(ahost, rport, locuser, remuser, cmd, fd2p) 341 char **ahost; 342 in_port_t rport; 343 const char *locuser, *remuser, *cmd; 344 int *fd2p; 345{ 346 return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 347} 348 349int 350rresvport(port) 351 int *port; 352{ 353 return rresvport_af(port, AF_INET); 354} 355 356int 357rresvport_af(alport, family) 358 int *alport, family; 359{ 360 int s; 361 struct sockaddr_storage ss; 362 u_short *sport; 363 364 memset(&ss, 0, sizeof(ss)); 365 ss.ss_family = family; 366 switch (family) { 367 case AF_INET: 368 ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in); 369 sport = &((struct sockaddr_in *)&ss)->sin_port; 370 ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; 371 break; 372#ifdef INET6 373 case AF_INET6: 374 ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6); 375 sport = &((struct sockaddr_in6 *)&ss)->sin6_port; 376 ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any; 377 break; 378#endif 379 default: 380 errno = EAFNOSUPPORT; 381 return -1; 382 } 383 384 s = socket(ss.ss_family, SOCK_STREAM, 0); 385 if (s < 0) 386 return (-1); 387#if 0 /* compat_exact_traditional_rresvport_semantics */ 388 sin.sin_port = htons((u_short)*alport); 389 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 390 return (s); 391 if (errno != EADDRINUSE) { 392 (void)close(s); 393 return (-1); 394 } 395#endif 396 *sport = 0; 397 if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) { 398 (void)close(s); 399 return (-1); 400 } 401 *alport = (int)ntohs(*sport); 402 return (s); 403} 404 405int __check_rhosts_file = 1; 406char *__rcmd_errstr; 407 408/* Guess at the size of a password buffer for getpwnam_r (see lookup.subproj/lu_group.c) */ 409#define MAXPWBUF (MAXLOGNAME + 1 + _PASSWORD_LEN + 1 + MAXPATHLEN + 1 + MAXPATHLEN + 1 + 4098) 410 411/* 412 * AF independent extension of iruserok. 413 * 414 * Returns 0 if ok, -1 if not ok. 415 */ 416int 417iruserok_sa(ra, rlen, superuser, ruser, luser) 418 const void *ra; 419 int rlen; 420 int superuser; 421 const char *ruser, *luser; 422{ 423 register char *cp; 424 struct stat sbuf; 425 struct passwd p, *pwd; 426 FILE *hostf; 427 uid_t uid; 428 int first, status; 429 char pbuf[MAXPATHLEN]; 430 const struct sockaddr *raddr; 431 struct sockaddr_storage ss; 432 char pwbuf[MAXPWBUF]; 433 434 /* avoid alignment issue */ 435 if (rlen > sizeof(ss)) 436 return(-1); 437 memcpy(&ss, ra, rlen); 438 raddr = (struct sockaddr *)&ss; 439 440 first = 1; 441 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 442again: 443 if (hostf) { 444 if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) { 445 (void)fclose(hostf); 446 return (0); 447 } 448 (void)fclose(hostf); 449 } 450 if (first == 1 && (__check_rhosts_file || superuser)) { 451 first = 0; 452 453 memset(&p, 0, sizeof(struct passwd)); 454 memset(pwbuf, 0, sizeof(pwbuf)); 455 pwd = NULL; 456 457 status = getpwnam_r(luser, &p, pwbuf, MAXPWBUF, &pwd); 458 if (status != 0) return -1; 459 460 (void)strcpy(pbuf, pwd->pw_dir); 461 (void)strcat(pbuf, "/.rhosts"); 462 463 /* 464 * Change effective uid while opening .rhosts. If root and 465 * reading an NFS mounted file system, can't read files that 466 * are protected read/write owner only. 467 */ 468 uid = geteuid(); 469 (void)seteuid(pwd->pw_uid); 470 hostf = fopen(pbuf, "r"); 471 (void)seteuid(uid); 472 473 if (hostf == NULL) 474 return (-1); 475 /* 476 * If not a regular file, or is owned by someone other than 477 * user or root or if writeable by anyone but the owner, quit. 478 */ 479 cp = NULL; 480 if (lstat(pbuf, &sbuf) < 0) 481 cp = ".rhosts lstat failed"; 482 else if (!S_ISREG(sbuf.st_mode)) 483 cp = ".rhosts not regular file"; 484 else if (fstat(fileno(hostf), &sbuf) < 0) 485 cp = ".rhosts fstat failed"; 486 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 487 cp = "bad .rhosts owner"; 488 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 489 cp = ".rhosts writeable by other than owner"; 490 /* If there were any problems, quit. */ 491 if (cp) { 492 __rcmd_errstr = cp; 493 (void)fclose(hostf); 494 return (-1); 495 } 496 goto again; 497 } 498 return (-1); 499} 500 501int 502ruserok(rhost, superuser, ruser, luser) 503 const char *rhost, *ruser, *luser; 504 int superuser; 505{ 506 struct addrinfo hints, *res, *r; 507 int error; 508 509 memset(&hints, 0, sizeof(hints)); 510 hints.ai_family = PF_UNSPEC; 511 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 512 error = getaddrinfo(rhost, "0", &hints, &res); 513 if (error) 514 return (-1); 515 516 for (r = res; r; r = r->ai_next) { 517 if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, 518 luser) == 0) { 519 freeaddrinfo(res); 520 return (0); 521 } 522 } 523 freeaddrinfo(res); 524 return (-1); 525} 526 527/* 528 * New .rhosts strategy: We are passed an ip address. We spin through 529 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 530 * has ip addresses, we don't have to trust a nameserver. When it 531 * contains hostnames, we spin through the list of addresses the nameserver 532 * gives us and look for a match. 533 * 534 * Returns 0 if ok, -1 if not ok. 535 */ 536int 537iruserok(raddr, superuser, ruser, luser) 538 unsigned long raddr; 539 int superuser; 540 const char *ruser, *luser; 541{ 542 struct sockaddr_in sin; 543 544 memset(&sin, 0, sizeof(sin)); 545 sin.sin_family = AF_INET; 546 sin.sin_len = sizeof(struct sockaddr_in); 547 memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 548 return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser, 549 ruser, luser); 550} 551 552/* 553 * XXX 554 * Don't make static, used by lpd(8). 555 * 556 * Returns 0 if ok, -1 if not ok. 557 */ 558int 559__ivaliduser(hostf, raddr, luser, ruser) 560 FILE *hostf; 561 u_int32_t raddr; 562 const char *luser, *ruser; 563{ 564 struct sockaddr_in sin; 565 566 memset(&sin, 0, sizeof(sin)); 567 sin.sin_family = AF_INET; 568 sin.sin_len = sizeof(struct sockaddr_in); 569 memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); 570 return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, 571 luser, ruser); 572} 573 574/* 575 * Returns 0 if ok, -1 if not ok. 576 * 577 * XXX obsolete API. 578 */ 579int 580__ivaliduser_af(hostf, raddr, luser, ruser, af, len) 581 FILE *hostf; 582 const void *raddr; 583 const char *luser, *ruser; 584 int af, len; 585{ 586 struct sockaddr *sa = NULL; 587 struct sockaddr_in *sin = NULL; 588#ifdef INET6 589 struct sockaddr_in6 *sin6 = NULL; 590#endif 591 struct sockaddr_storage ss; 592 593 memset(&ss, 0, sizeof(ss)); 594 switch (af) { 595 case AF_INET: 596 if (len != sizeof(sin->sin_addr)) 597 return -1; 598 sin = (struct sockaddr_in *)&ss; 599 sin->sin_family = AF_INET; 600 sin->sin_len = sizeof(struct sockaddr_in); 601 memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr)); 602 break; 603#ifdef INET6 604 case AF_INET6: 605 if (len != sizeof(sin6->sin6_addr)) 606 return -1; 607 /* you will lose scope info */ 608 sin6 = (struct sockaddr_in6 *)&ss; 609 sin6->sin6_family = AF_INET6; 610 sin6->sin6_len = sizeof(struct sockaddr_in6); 611 memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr)); 612 break; 613#endif 614 default: 615 return -1; 616 } 617 618 sa = (struct sockaddr *)&ss; 619 return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); 620} 621 622/* 623 * Returns 0 if ok, -1 if not ok. 624 */ 625int 626__ivaliduser_sa(hostf, raddr, salen, luser, ruser) 627 FILE *hostf; 628 const struct sockaddr *raddr; 629 socklen_t salen; 630 const char *luser, *ruser; 631{ 632 register char *user, *p; 633 int ch; 634 char buf[MAXHOSTNAMELEN + 128]; /* host + login */ 635 char hname[MAXHOSTNAMELEN]; 636 /* Presumed guilty until proven innocent. */ 637 int userok = 0, hostok = 0; 638#ifdef YP 639 char *ypdomain; 640 641 if (yp_get_default_domain(&ypdomain)) 642 ypdomain = NULL; 643#else 644#define ypdomain NULL 645#endif 646 /* 647 * We try to get the hostname back for netgroup matching. 648 * However, the .rosts file may contain IP addresses as well 649 * as names, so a name is not always required. 650 */ 651 getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0, NI_NAMEREQD); 652 653 while (fgets(buf, sizeof(buf), hostf)) { 654 p = buf; 655 /* Skip lines that are too long. */ 656 if (strchr(p, '\n') == NULL) { 657 while ((ch = getc(hostf)) != '\n' && ch != EOF); 658 continue; 659 } 660 if (*p == '\n' || *p == '#') { 661 /* comment... */ 662 continue; 663 } 664 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 665 *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p; 666 p++; 667 } 668 if (*p == ' ' || *p == '\t') { 669 *p++ = '\0'; 670 while (*p == ' ' || *p == '\t') 671 p++; 672 user = p; 673 while (*p != '\n' && *p != ' ' && 674 *p != '\t' && *p != '\0') 675 p++; 676 } else 677 user = p; 678 *p = '\0'; 679 /* 680 * Do +/- and +@/-@ checking. This looks really nasty, 681 * but it matches SunOS's behavior so far as I can tell. 682 */ 683 switch(buf[0]) { 684 case '+': 685 if (!buf[1]) { /* '+' matches all hosts */ 686 hostok = 1; 687 break; 688 } 689 if (buf[1] == '@') /* match a host by netgroup */ 690 hostok = innetgr((char *)&buf[2], 691 (char *)&hname, NULL, ypdomain); 692 else /* match a host by addr */ 693 hostok = __icheckhost(raddr, salen, 694 (char *)&buf[1]); 695 break; 696 case '-': /* reject '-' hosts and all their users */ 697 if (buf[1] == '@') { 698 if (innetgr((char *)&buf[2], 699 (char *)&hname, NULL, ypdomain)) 700 return(-1); 701 } else { 702 if (__icheckhost(raddr, salen, 703 (char *)&buf[1])) 704 return(-1); 705 } 706 break; 707 default: /* if no '+' or '-', do a simple match */ 708 hostok = __icheckhost(raddr, salen, buf); 709 break; 710 } 711 switch(*user) { 712 case '+': 713 if (!*(user+1)) { /* '+' matches all users */ 714 userok = 1; 715 break; 716 } 717 if (*(user+1) == '@') /* match a user by netgroup */ 718 userok = innetgr(user+2, NULL, ruser, ypdomain); 719 else /* match a user by direct specification */ 720 userok = !(strcmp(ruser, user+1)); 721 break; 722 case '-': /* if we matched a hostname, */ 723 if (hostok) { /* check for user field rejections */ 724 if (!*(user+1)) 725 return(-1); 726 if (*(user+1) == '@') { 727 if (innetgr(user+2, NULL, 728 ruser, ypdomain)) 729 return(-1); 730 } else { 731 if (!strcmp(ruser, user+1)) 732 return(-1); 733 } 734 } 735 break; 736 default: /* no rejections: try to match the user */ 737 if (hostok) 738 userok = !(strcmp(ruser,*user ? user : luser)); 739 break; 740 } 741 if (hostok && userok) 742 return(0); 743 } 744 return (-1); 745} 746 747/* 748 * Returns "true" if match, 0 if no match. 749 * 750 * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion 751 * if af == AF_INET6. 752 */ 753static int 754__icheckhost(raddr, salen, lhost) 755 const struct sockaddr *raddr; 756 socklen_t salen; 757 const char *lhost; 758{ 759 struct sockaddr_in sin; 760 struct sockaddr_in6 *sin6; 761 struct addrinfo hints, *res, *r; 762 int error; 763 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 764 765 if (raddr->sa_family == AF_INET6) { 766 sin6 = (struct sockaddr_in6 *)raddr; 767 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 768 memset(&sin, 0, sizeof(sin)); 769 sin.sin_family = AF_INET; 770 sin.sin_len = sizeof(struct sockaddr_in); 771 memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], 772 sizeof(sin.sin_addr)); 773 raddr = (struct sockaddr *)&sin; 774 salen = sin.sin_len; 775 } 776 } 777 778 h1[0] = '\0'; 779 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 780 NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 781 return (0); 782 783 /* Resolve laddr into sockaddr */ 784 memset(&hints, 0, sizeof(hints)); 785 hints.ai_family = raddr->sa_family; 786 hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/ 787 res = NULL; 788 error = getaddrinfo(lhost, "0", &hints, &res); 789 if (error) 790 return (0); 791 792 for (r = res; r ; r = r->ai_next) { 793 h2[0] = '\0'; 794 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 795 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0) 796 continue; 797 if (strcmp(h1, h2) == 0) { 798 freeaddrinfo(res); 799 return (1); 800 } 801 } 802 803 /* No match. */ 804 freeaddrinfo(res); 805 return (0); 806} 807