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