1 /* $NetBSD: rcmd.c,v 1.53 2003/08/07 16:43:12 agc Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1997 Matthew R. Green. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. All advertising materials mentioning features or use of this software 44 * must display the following acknowledgement: 45 * This product includes software developed by the University of 46 * California, Berkeley and its contributors. 47 * 4. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #if defined(LIBC_SCCS) && !defined(lint) 66 #if 0 67 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; 68 #else 69 __RCSID("$NetBSD: rcmd.c,v 1.53 2003/08/07 16:43:12 agc Exp $"); 70 #endif 71 #endif /* LIBC_SCCS and not lint */ 72 73 #ifdef _LIBC 74 #include "namespace.h" 75 #endif 76 #include <sys/param.h> 77 #include <sys/socket.h> 78 #include <sys/stat.h> 79 #include <sys/poll.h> 80 #include <sys/wait.h> 81 82 #include <netinet/in.h> 83 #include <rpc/rpc.h> 84 #include <arpa/inet.h> 85 #include <netgroup.h> 86 87 #include <assert.h> 88 #include <ctype.h> 89 #include <err.h> 90 #include <errno.h> 91 #include <fcntl.h> 92 #include <grp.h> 93 #include <netdb.h> 94 #include <paths.h> 95 #include <pwd.h> 96 #include <signal.h> 97 #include <stdio.h> 98 #include <stdlib.h> 99 #include <string.h> 100 #include <syslog.h> 101 #include <unistd.h> 102 103 #include "pathnames.h" 104 105 int orcmd __P((char **, u_int, const char *, const char *, const char *, 106 int *)); 107 int orcmd_af __P((char **, u_int, const char *, const char *, const char *, 108 int *, int)); 109 int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *)); 110 int __ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t, const char *, 111 const char *)); 112 static int rshrcmd __P((char **, u_int32_t, const char *, const char *, 113 const char *, int *, const char *)); 114 static int resrcmd __P((struct addrinfo *, char **, u_int32_t, const char *, 115 const char *, const char *, int *)); 116 static int __icheckhost __P((struct sockaddr *, socklen_t, const char *)); 117 static char *__gethostloop __P((struct sockaddr *, socklen_t)); 118 119 int 120 rcmd(ahost, rport, locuser, remuser, cmd, fd2p) 121 char **ahost; 122 u_short rport; 123 const char *locuser, *remuser, *cmd; 124 int *fd2p; 125 { 126 127 return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 128 } 129 130 int 131 rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) 132 char **ahost; 133 u_short rport; 134 const char *locuser, *remuser, *cmd; 135 int *fd2p; 136 int af; 137 { 138 static char hbuf[MAXHOSTNAMELEN]; 139 char pbuf[NI_MAXSERV]; 140 struct addrinfo hints, *res; 141 int error; 142 struct servent *sp; 143 144 _DIAGASSERT(ahost != NULL); 145 _DIAGASSERT(locuser != NULL); 146 _DIAGASSERT(remuser != NULL); 147 _DIAGASSERT(cmd != NULL); 148 /* fd2p may be NULL */ 149 150 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport)); 151 memset(&hints, 0, sizeof(hints)); 152 hints.ai_family = af; 153 hints.ai_socktype = SOCK_STREAM; 154 hints.ai_flags = AI_CANONNAME; 155 error = getaddrinfo(*ahost, pbuf, &hints, &res); 156 if (error) { 157 warnx("%s: %s", *ahost, gai_strerror(error)); /*XXX*/ 158 return (-1); 159 } 160 if (res->ai_canonname) { 161 /* 162 * Canonicalise hostname. 163 * XXX: Should we really do this? 164 */ 165 strlcpy(hbuf, res->ai_canonname, sizeof(hbuf)); 166 *ahost = hbuf; 167 } 168 169 /* 170 * Check if rport is the same as the shell port, and that the fd2p. If 171 * it is not, the program isn't expecting 'rsh' and so we can't use the 172 * RCMD_CMD environment. 173 */ 174 sp = getservbyname("shell", "tcp"); 175 if (sp != NULL && sp->s_port == rport) 176 error = rshrcmd(ahost, (u_int32_t)rport, 177 locuser, remuser, cmd, fd2p, getenv("RCMD_CMD")); 178 else 179 error = resrcmd(res, ahost, (u_int32_t)rport, 180 locuser, remuser, cmd, fd2p); 181 freeaddrinfo(res); 182 return (error); 183 } 184 185 /* this is simply a wrapper around hprcmd() that handles ahost first */ 186 int 187 orcmd(ahost, rport, locuser, remuser, cmd, fd2p) 188 char **ahost; 189 u_int rport; 190 const char *locuser, *remuser, *cmd; 191 int *fd2p; 192 { 193 return orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 194 } 195 196 int 197 orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af) 198 char **ahost; 199 u_int rport; 200 const char *locuser, *remuser, *cmd; 201 int *fd2p; 202 int af; 203 { 204 static char hbuf[MAXHOSTNAMELEN]; 205 char pbuf[NI_MAXSERV]; 206 struct addrinfo hints, *res; 207 int error; 208 209 _DIAGASSERT(ahost != NULL); 210 _DIAGASSERT(locuser != NULL); 211 _DIAGASSERT(remuser != NULL); 212 _DIAGASSERT(cmd != NULL); 213 /* fd2p may be NULL */ 214 215 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport)); 216 memset(&hints, 0, sizeof(hints)); 217 hints.ai_family = af; 218 hints.ai_socktype = SOCK_STREAM; 219 hints.ai_flags = AI_CANONNAME; 220 error = getaddrinfo(*ahost, pbuf, &hints, &res); 221 if (error) { 222 warnx("%s: %s", *ahost, gai_strerror(error)); /*XXX*/ 223 return (-1); 224 } 225 if (res->ai_canonname) { 226 strlcpy(hbuf, res->ai_canonname, sizeof(hbuf)); 227 *ahost = hbuf; 228 } 229 230 error = resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p); 231 freeaddrinfo(res); 232 return (error); 233 } 234 235 /*ARGSUSED*/ 236 static int 237 resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p) 238 struct addrinfo *res; 239 char **ahost; 240 u_int32_t rport; 241 const char *locuser, *remuser, *cmd; 242 int *fd2p; 243 { 244 struct addrinfo *r; 245 struct sockaddr_storage from; 246 struct pollfd reads[2]; 247 sigset_t nmask, omask; 248 pid_t pid; 249 int s, lport, timo; 250 int pollr; 251 char c; 252 int refused; 253 254 _DIAGASSERT(res != NULL); 255 _DIAGASSERT(ahost != NULL); 256 _DIAGASSERT(locuser != NULL); 257 _DIAGASSERT(remuser != NULL); 258 _DIAGASSERT(cmd != NULL); 259 /* fd2p may be NULL */ 260 261 r = res; 262 refused = 0; 263 pid = getpid(); 264 sigemptyset(&nmask); 265 sigaddset(&nmask, SIGURG); 266 if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1) 267 return -1; 268 for (timo = 1, lport = IPPORT_RESERVED - 1;;) { 269 s = rresvport_af(&lport, r->ai_family); 270 if (s < 0) { 271 if (errno == EAGAIN) 272 warnx("rcmd: socket: All ports in use"); 273 else 274 warn("rcmd: socket"); 275 if (r->ai_next) { 276 r = r->ai_next; 277 continue; 278 } else { 279 (void)sigprocmask(SIG_SETMASK, &omask, NULL); 280 return (-1); 281 } 282 } 283 fcntl(s, F_SETOWN, pid); 284 if (connect(s, r->ai_addr, r->ai_addrlen) >= 0) 285 break; 286 (void)close(s); 287 if (errno == EADDRINUSE) { 288 lport--; 289 continue; 290 } else if (errno == ECONNREFUSED) 291 refused++; 292 if (r->ai_next) { 293 int oerrno = errno; 294 char hbuf[NI_MAXHOST]; 295 #ifdef NI_WITHSCOPEID 296 const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; 297 #else 298 const int niflags = NI_NUMERICHOST; 299 #endif 300 301 hbuf[0] = '\0'; 302 if (getnameinfo(r->ai_addr, r->ai_addrlen, 303 hbuf, sizeof(hbuf), NULL, 0, niflags) != 0) 304 strlcpy(hbuf, "(invalid)", sizeof(hbuf)); 305 warnx("rcmd: connect to address %s", hbuf); 306 errno = oerrno; 307 perror(0); 308 r = r->ai_next; 309 hbuf[0] = '\0'; 310 if (getnameinfo(r->ai_addr, r->ai_addrlen, 311 hbuf, sizeof(hbuf), NULL, 0, niflags) != 0) 312 strlcpy(hbuf, "(invalid)", sizeof(hbuf)); 313 (void)fprintf(stderr, "Trying %s...\n", hbuf); 314 continue; 315 } 316 if (refused && timo <= 16) { 317 (void)sleep((unsigned int)timo); 318 timo *= 2; 319 r = res; 320 refused = 0; 321 continue; 322 } 323 (void)fprintf(stderr, "%s: %s\n", res->ai_canonname, 324 strerror(errno)); 325 (void)sigprocmask(SIG_SETMASK, &omask, NULL); 326 return (-1); 327 } 328 lport--; 329 if (fd2p == 0) { 330 write(s, "", 1); 331 lport = 0; 332 } else { 333 char num[8]; 334 int s2 = rresvport_af(&lport, r->ai_family), s3; 335 socklen_t len = sizeof(from); 336 337 if (s2 < 0) 338 goto bad; 339 listen(s2, 1); 340 (void)snprintf(num, sizeof(num), "%d", lport); 341 if (write(s, num, strlen(num) + 1) != 342 (ssize_t) (strlen(num) + 1)) { 343 warn("rcmd: write (setting up stderr)"); 344 (void)close(s2); 345 goto bad; 346 } 347 reads[0].fd = s; 348 reads[0].events = POLLIN; 349 reads[1].fd = s2; 350 reads[1].events = POLLIN; 351 errno = 0; 352 pollr = poll(reads, 2, INFTIM); 353 if (pollr < 1 || (reads[1].revents & POLLIN) == 0) { 354 if (errno != 0) 355 warn("poll: setting up stderr"); 356 else 357 warnx("poll: protocol failure in circuit setup"); 358 (void)close(s2); 359 goto bad; 360 } 361 s3 = accept(s2, (struct sockaddr *)(void *)&from, &len); 362 (void)close(s2); 363 if (s3 < 0) { 364 warn("rcmd: accept"); 365 lport = 0; 366 goto bad; 367 } 368 *fd2p = s3; 369 switch (((struct sockaddr *)(void *)&from)->sa_family) { 370 case AF_INET: 371 #ifdef INET6 372 case AF_INET6: 373 #endif 374 if (getnameinfo((struct sockaddr *)(void *)&from, len, 375 NULL, 0, num, sizeof(num), NI_NUMERICSERV) != 0 || 376 (atoi(num) >= IPPORT_RESERVED || 377 atoi(num) < IPPORT_RESERVED / 2)) { 378 warnx("rcmd: protocol failure in circuit setup."); 379 goto bad2; 380 } 381 break; 382 default: 383 break; 384 } 385 } 386 387 (void)write(s, locuser, strlen(locuser)+1); 388 (void)write(s, remuser, strlen(remuser)+1); 389 (void)write(s, cmd, strlen(cmd)+1); 390 if (read(s, &c, 1) != 1) { 391 warn("%s", *ahost); 392 goto bad2; 393 } 394 if (c != 0) { 395 while (read(s, &c, 1) == 1) { 396 (void)write(STDERR_FILENO, &c, 1); 397 if (c == '\n') 398 break; 399 } 400 goto bad2; 401 } 402 (void)sigprocmask(SIG_SETMASK, &omask, NULL); 403 return (s); 404 bad2: 405 if (lport) 406 (void)close(*fd2p); 407 bad: 408 (void)close(s); 409 (void)sigprocmask(SIG_SETMASK, &omask, NULL); 410 return (-1); 411 } 412 413 /* 414 * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca> 415 */ 416 /* ARGSUSED */ 417 static int 418 rshrcmd(ahost, rport, locuser, remuser, cmd, fd2p, rshcmd) 419 char **ahost; 420 u_int32_t rport; 421 const char *locuser, *remuser, *cmd; 422 int *fd2p; 423 const char *rshcmd; 424 { 425 pid_t pid; 426 int sp[2], ep[2]; 427 char *p; 428 struct passwd *pw; 429 430 _DIAGASSERT(ahost != NULL); 431 _DIAGASSERT(locuser != NULL); 432 _DIAGASSERT(remuser != NULL); 433 _DIAGASSERT(cmd != NULL); 434 /* fd2p may be NULL */ 435 436 /* What rsh/shell to use. */ 437 if (rshcmd == NULL) 438 rshcmd = _PATH_BIN_RCMD; 439 440 /* locuser must exist on this host. */ 441 if ((pw = getpwnam(locuser)) == NULL) { 442 warnx("rshrcmd: unknown user: %s", locuser); 443 return(-1); 444 } 445 446 /* get a socketpair we'll use for stdin and stdout. */ 447 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sp) < 0) { 448 warn("rshrcmd: socketpair"); 449 return (-1); 450 } 451 /* we will use this for the fd2 pointer */ 452 if (fd2p) { 453 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, ep) < 0) { 454 warn("rshrcmd: socketpair"); 455 return (-1); 456 } 457 *fd2p = ep[0]; 458 } 459 460 pid = fork(); 461 if (pid < 0) { 462 warn("rshrcmd: fork"); 463 return (-1); 464 } 465 if (pid == 0) { 466 /* 467 * child 468 * - we use sp[1] to be stdin/stdout, and close sp[0] 469 * - with fd2p, we use ep[1] for stderr, and close ep[0] 470 */ 471 (void)close(sp[0]); 472 if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) { 473 warn("rshrcmd: dup2"); 474 _exit(1); 475 } 476 if (fd2p) { 477 if (dup2(ep[1], 2) < 0) { 478 warn("rshrcmd: dup2"); 479 _exit(1); 480 } 481 (void)close(ep[0]); 482 (void)close(ep[1]); 483 } else if (dup2(0, 2) < 0) { 484 warn("rshrcmd: dup2"); 485 _exit(1); 486 } 487 /* fork again to lose parent. */ 488 pid = fork(); 489 if (pid < 0) { 490 warn("rshrcmd: second fork"); 491 _exit(1); 492 } 493 if (pid > 0) 494 _exit(0); 495 496 /* Orphan. Become local user for rshprog. */ 497 if (setuid(pw->pw_uid)) { 498 warn("rshrcmd: setuid(%lu)", (u_long)pw->pw_uid); 499 _exit(1); 500 } 501 502 /* 503 * If we are rcmd'ing to "localhost" as the same user as we are, 504 * then avoid running remote shell for efficiency. 505 */ 506 if (strcmp(*ahost, "localhost") == 0 && 507 strcmp(locuser, remuser) == 0) { 508 if (pw->pw_shell[0] == '\0') 509 rshcmd = _PATH_BSHELL; 510 else 511 rshcmd = pw->pw_shell; 512 p = strrchr(rshcmd, '/'); 513 execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL); 514 } else { 515 p = strrchr(rshcmd, '/'); 516 execlp(rshcmd, p ? p + 1 : rshcmd, *ahost, "-l", 517 remuser, cmd, NULL); 518 } 519 warn("rshrcmd: exec %s", rshcmd); 520 _exit(1); 521 } 522 /* Parent */ 523 (void)close(sp[1]); 524 if (fd2p) 525 (void)close(ep[1]); 526 527 (void)waitpid(pid, NULL, 0); 528 return (sp[0]); 529 } 530 531 int 532 rresvport(alport) 533 int *alport; 534 { 535 536 _DIAGASSERT(alport != NULL); 537 538 return rresvport_af(alport, AF_INET); 539 } 540 541 int 542 rresvport_af(alport, family) 543 int *alport; 544 int family; 545 { 546 struct sockaddr_storage ss; 547 struct sockaddr *sa; 548 int salen; 549 int s; 550 u_int16_t *portp; 551 552 _DIAGASSERT(alport != NULL); 553 554 memset(&ss, 0, sizeof(ss)); 555 sa = (struct sockaddr *)(void *)&ss; 556 switch (family) { 557 case AF_INET: 558 #ifdef BSD4_4 559 sa->sa_len = 560 #endif 561 salen = sizeof(struct sockaddr_in); 562 portp = &((struct sockaddr_in *)(void *)sa)->sin_port; 563 break; 564 #ifdef INET6 565 case AF_INET6: 566 #ifdef BSD4_4 567 sa->sa_len = 568 #endif 569 salen = sizeof(struct sockaddr_in6); 570 portp = &((struct sockaddr_in6 *)(void *)sa)->sin6_port; 571 break; 572 #endif 573 default: 574 portp = NULL; 575 return EAFNOSUPPORT; 576 } 577 sa->sa_family = family; 578 s = socket(family, SOCK_STREAM, 0); 579 if (s < 0) 580 return (-1); 581 #ifdef BSD4_4 582 switch (family) { 583 case AF_INET: 584 case AF_INET6: 585 *portp = 0; 586 if (bindresvport(s, (struct sockaddr_in *)(void *)sa) < 0) { 587 int sverr = errno; 588 589 (void)close(s); 590 errno = sverr; 591 return (-1); 592 } 593 *alport = (int)ntohs(*portp); 594 return (s); 595 default: 596 /* is it necessary to try keep code for other AFs? */ 597 break; 598 } 599 #endif 600 for (;;) { 601 *portp = htons((u_short)*alport); 602 if (bind(s, sa, (socklen_t)salen) >= 0) 603 return (s); 604 if (errno != EADDRINUSE) { 605 (void)close(s); 606 return (-1); 607 } 608 (*alport)--; 609 if (*alport == IPPORT_RESERVED/2) { 610 (void)close(s); 611 errno = EAGAIN; /* close */ 612 return (-1); 613 } 614 } 615 } 616 617 int __check_rhosts_file = 1; 618 char *__rcmd_errstr; 619 620 int 621 ruserok(rhost, superuser, ruser, luser) 622 const char *rhost, *ruser, *luser; 623 int superuser; 624 { 625 struct addrinfo hints, *res, *r; 626 int error; 627 628 _DIAGASSERT(rhost != NULL); 629 _DIAGASSERT(ruser != NULL); 630 _DIAGASSERT(luser != NULL); 631 632 memset(&hints, 0, sizeof(hints)); 633 hints.ai_family = PF_UNSPEC; 634 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 635 error = getaddrinfo(rhost, "0", &hints, &res); 636 if (error) 637 return (-1); 638 639 for (r = res; r; r = r->ai_next) { 640 if (iruserok_sa(r->ai_addr, (int)r->ai_addrlen, superuser, 641 ruser, luser) == 0) { 642 freeaddrinfo(res); 643 return (0); 644 } 645 } 646 freeaddrinfo(res); 647 return (-1); 648 } 649 650 /* 651 * New .rhosts strategy: We are passed an ip address. We spin through 652 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 653 * has ip addresses, we don't have to trust a nameserver. When it 654 * contains hostnames, we spin through the list of addresses the nameserver 655 * gives us and look for a match. 656 * 657 * Returns 0 if ok, -1 if not ok. 658 */ 659 int 660 iruserok(raddr, superuser, ruser, luser) 661 u_int32_t raddr; 662 int superuser; 663 const char *ruser, *luser; 664 { 665 struct sockaddr_in irsin; 666 667 memset(&irsin, 0, sizeof(irsin)); 668 irsin.sin_family = AF_INET; 669 #ifdef BSD4_4 670 irsin.sin_len = sizeof(struct sockaddr_in); 671 #endif 672 memcpy(&irsin.sin_addr, &raddr, sizeof(irsin.sin_addr)); 673 return iruserok_sa(&irsin, sizeof(struct sockaddr_in), superuser, ruser, 674 luser); 675 } 676 677 /* 678 * 2nd and 3rd arguments are typed like this, to avoid dependency between 679 * unistd.h and sys/socket.h. There's no better way. 680 */ 681 int 682 iruserok_sa(raddr, rlen, superuser, ruser, luser) 683 const void *raddr; 684 int rlen; 685 int superuser; 686 const char *ruser, *luser; 687 { 688 struct sockaddr *sa; 689 register char *cp; 690 struct stat sbuf; 691 struct passwd *pwd; 692 FILE *hostf; 693 uid_t uid; 694 gid_t gid; 695 int first; 696 char pbuf[MAXPATHLEN]; 697 698 _DIAGASSERT(raddr != NULL); 699 _DIAGASSERT(ruser != NULL); 700 _DIAGASSERT(luser != NULL); 701 702 /*LINTED const castaway*/ 703 sa = (struct sockaddr *)(void *)raddr; 704 705 first = 1; 706 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 707 again: 708 if (hostf) { 709 if (__ivaliduser_sa(hostf, sa, (socklen_t)rlen, luser, 710 ruser) == 0) { 711 (void)fclose(hostf); 712 return (0); 713 } 714 (void)fclose(hostf); 715 } 716 if (first == 1 && (__check_rhosts_file || superuser)) { 717 first = 0; 718 if ((pwd = getpwnam(luser)) == NULL) 719 return (-1); 720 (void)strlcpy(pbuf, pwd->pw_dir, sizeof(pbuf)); 721 (void)strlcat(pbuf, "/.rhosts", sizeof(pbuf)); 722 723 /* 724 * Change effective uid while opening .rhosts. If root and 725 * reading an NFS mounted file system, can't read files that 726 * are protected read/write owner only. 727 */ 728 uid = geteuid(); 729 gid = getegid(); 730 (void)setegid(pwd->pw_gid); 731 initgroups(pwd->pw_name, pwd->pw_gid); 732 (void)seteuid(pwd->pw_uid); 733 hostf = fopen(pbuf, "r"); 734 (void)seteuid(uid); 735 (void)setegid(gid); 736 737 if (hostf == NULL) 738 return (-1); 739 /* 740 * If not a regular file, or is owned by someone other than 741 * user or root or if writable by anyone but the owner, quit. 742 */ 743 cp = NULL; 744 if (lstat(pbuf, &sbuf) < 0) 745 cp = ".rhosts lstat failed"; 746 else if (!S_ISREG(sbuf.st_mode)) 747 cp = ".rhosts not regular file"; 748 else if (fstat(fileno(hostf), &sbuf) < 0) 749 cp = ".rhosts fstat failed"; 750 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 751 cp = "bad .rhosts owner"; 752 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 753 cp = ".rhosts writable by other than owner"; 754 /* If there were any problems, quit. */ 755 if (cp) { 756 __rcmd_errstr = cp; 757 (void)fclose(hostf); 758 return (-1); 759 } 760 goto again; 761 } 762 return (-1); 763 } 764 765 /* 766 * XXX 767 * Don't make static, used by lpd(8). We will be able to change the function 768 * into static function, when we bump libc major #. 769 * 770 * Returns 0 if ok, -1 if not ok. 771 */ 772 #ifdef notdef /*_LIBC*/ 773 static 774 #endif 775 int 776 __ivaliduser(hostf, raddr, luser, ruser) 777 FILE *hostf; 778 u_int32_t raddr; 779 const char *luser, *ruser; 780 { 781 struct sockaddr_in ivusin; 782 783 memset(&ivusin, 0, sizeof(ivusin)); 784 ivusin.sin_family = AF_INET; 785 #ifdef BSD4_4 786 ivusin.sin_len = sizeof(struct sockaddr_in); 787 #endif 788 memcpy(&ivusin.sin_addr, &raddr, sizeof(ivusin.sin_addr)); 789 return __ivaliduser_sa(hostf, (struct sockaddr *)(void *)&ivusin, 790 sizeof(struct sockaddr_in), luser, ruser); 791 } 792 793 #ifdef notdef /*_LIBC*/ 794 static 795 #endif 796 int 797 __ivaliduser_sa(hostf, raddr, salen, luser, ruser) 798 FILE *hostf; 799 struct sockaddr *raddr; 800 socklen_t salen; 801 const char *luser, *ruser; 802 { 803 register char *user, *p; 804 int ch; 805 char buf[MAXHOSTNAMELEN + 128]; /* host + login */ 806 const char *auser, *ahost; 807 int hostok, userok; 808 char *rhost = NULL; 809 int firsttime = 1; 810 char domain[MAXHOSTNAMELEN]; 811 812 getdomainname(domain, sizeof(domain)); 813 814 _DIAGASSERT(hostf != NULL); 815 _DIAGASSERT(luser != NULL); 816 _DIAGASSERT(ruser != NULL); 817 818 while (fgets(buf, sizeof(buf), hostf)) { 819 p = buf; 820 /* Skip lines that are too long. */ 821 if (strchr(p, '\n') == NULL) { 822 while ((ch = getc(hostf)) != '\n' && ch != EOF) 823 ; 824 continue; 825 } 826 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 827 *p = isupper((unsigned char)*p) ? 828 tolower((unsigned char)*p) : *p; 829 p++; 830 } 831 if (*p == ' ' || *p == '\t') { 832 *p++ = '\0'; 833 while (*p == ' ' || *p == '\t') 834 p++; 835 user = p; 836 while (*p != '\n' && *p != ' ' && 837 *p != '\t' && *p != '\0') 838 p++; 839 } else 840 user = p; 841 *p = '\0'; 842 843 if (p == buf) 844 continue; 845 846 auser = *user ? user : luser; 847 ahost = buf; 848 849 if (ahost[0] == '+') 850 switch (ahost[1]) { 851 case '\0': 852 hostok = 1; 853 break; 854 855 case '@': 856 if (firsttime) { 857 rhost = __gethostloop(raddr, salen); 858 firsttime = 0; 859 } 860 if (rhost) 861 hostok = innetgr(&ahost[2], rhost, 862 NULL, domain); 863 else 864 hostok = 0; 865 break; 866 867 default: 868 hostok = __icheckhost(raddr, salen, &ahost[1]); 869 break; 870 } 871 else if (ahost[0] == '-') 872 switch (ahost[1]) { 873 case '\0': 874 hostok = -1; 875 break; 876 877 case '@': 878 if (firsttime) { 879 rhost = __gethostloop(raddr, salen); 880 firsttime = 0; 881 } 882 if (rhost) 883 hostok = -innetgr(&ahost[2], rhost, 884 NULL, domain); 885 else 886 hostok = 0; 887 break; 888 889 default: 890 hostok = -__icheckhost(raddr, salen, &ahost[1]); 891 break; 892 } 893 else 894 hostok = __icheckhost(raddr, salen, ahost); 895 896 897 if (auser[0] == '+') 898 switch (auser[1]) { 899 case '\0': 900 userok = 1; 901 break; 902 903 case '@': 904 userok = innetgr(&auser[2], NULL, ruser, 905 domain); 906 break; 907 908 default: 909 userok = strcmp(ruser, &auser[1]) == 0; 910 break; 911 } 912 else if (auser[0] == '-') 913 switch (auser[1]) { 914 case '\0': 915 userok = -1; 916 break; 917 918 case '@': 919 userok = -innetgr(&auser[2], NULL, ruser, 920 domain); 921 break; 922 923 default: 924 userok = 925 -(strcmp(ruser, &auser[1]) == 0 ? 1 : 0); 926 break; 927 } 928 else 929 userok = strcmp(ruser, auser) == 0; 930 931 /* Check if one component did not match */ 932 if (hostok == 0 || userok == 0) 933 continue; 934 935 /* Check if we got a forbidden pair */ 936 if (userok == -1 || hostok == -1) 937 return -1; 938 939 /* Check if we got a valid pair */ 940 if (hostok == 1 && userok == 1) 941 return 0; 942 } 943 return -1; 944 } 945 946 /* 947 * Returns "true" if match, 0 if no match. 948 * 949 * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion 950 * if af == AF_INET6. 951 */ 952 static int 953 __icheckhost(raddr, salen, lhost) 954 struct sockaddr *raddr; 955 socklen_t salen; 956 const char *lhost; 957 { 958 struct addrinfo hints, *res, *r; 959 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 960 int error; 961 #ifdef NI_WITHSCOPEID 962 const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; 963 #else 964 const int niflags = NI_NUMERICHOST; 965 #endif 966 967 _DIAGASSERT(raddr != NULL); 968 _DIAGASSERT(lhost != NULL); 969 970 h1[0] = '\0'; 971 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 972 niflags) != 0) 973 return (0); 974 975 /* Resolve laddr into sockaddr */ 976 memset(&hints, 0, sizeof(hints)); 977 hints.ai_family = raddr->sa_family; 978 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 979 res = NULL; 980 error = getaddrinfo(lhost, "0", &hints, &res); 981 if (error) 982 return (0); 983 984 /* 985 * Try string comparisons between raddr and laddr. 986 */ 987 for (r = res; r; r = r->ai_next) { 988 h2[0] = '\0'; 989 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 990 NULL, 0, niflags) != 0) 991 continue; 992 if (strcmp(h1, h2) == 0) { 993 freeaddrinfo(res); 994 return (1); 995 } 996 } 997 998 /* No match. */ 999 freeaddrinfo(res); 1000 return (0); 1001 } 1002 1003 /* 1004 * Return the hostname associated with the supplied address. 1005 * Do a reverse lookup as well for security. If a loop cannot 1006 * be found, pack the numeric IP address into the string. 1007 * 1008 * NI_WITHSCOPEID is useful for comparing sin6_scope_id portion 1009 * if af == AF_INET6. 1010 */ 1011 static char * 1012 __gethostloop(raddr, salen) 1013 struct sockaddr *raddr; 1014 socklen_t salen; 1015 { 1016 static char remotehost[NI_MAXHOST]; 1017 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 1018 struct addrinfo hints, *res, *r; 1019 int error; 1020 #ifdef NI_WITHSCOPEID 1021 const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; 1022 #else 1023 const int niflags = NI_NUMERICHOST; 1024 #endif 1025 1026 _DIAGASSERT(raddr != NULL); 1027 1028 h1[0] = remotehost[0] = '\0'; 1029 if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), 1030 NULL, 0, NI_NAMEREQD) != 0) 1031 return (NULL); 1032 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, 1033 niflags) != 0) 1034 return (NULL); 1035 1036 /* 1037 * Look up the name and check that the supplied 1038 * address is in the list 1039 */ 1040 memset(&hints, 0, sizeof(hints)); 1041 hints.ai_family = raddr->sa_family; 1042 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 1043 hints.ai_flags = AI_CANONNAME; 1044 res = NULL; 1045 error = getaddrinfo(remotehost, "0", &hints, &res); 1046 if (error) 1047 return (NULL); 1048 1049 for (r = res; r; r = r->ai_next) { 1050 h2[0] = '\0'; 1051 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 1052 NULL, 0, niflags) != 0) 1053 continue; 1054 if (strcmp(h1, h2) == 0) { 1055 freeaddrinfo(res); 1056 return (remotehost); 1057 } 1058 } 1059 1060 /* 1061 * either the DNS adminstrator has made a configuration 1062 * mistake, or someone has attempted to spoof us 1063 */ 1064 syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s", 1065 h1, res->ai_canonname ? res->ai_canonname : remotehost); 1066 freeaddrinfo(res); 1067 return (NULL); 1068 } 1069