1 /* $NetBSD: rcmd.c,v 1.18 1997/01/23 14:02:05 mrg 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #if defined(LIBC_SCCS) && !defined(lint) 37 #if 0 38 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; 39 #else 40 static char *rcsid = "$NetBSD: rcmd.c,v 1.18 1997/01/23 14:02:05 mrg Exp $"; 41 #endif 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include <sys/param.h> 45 #include <sys/socket.h> 46 #include <sys/stat.h> 47 #include <sys/poll.h> 48 49 #include <netinet/in.h> 50 #include <arpa/inet.h> 51 #include <netgroup.h> 52 53 #include <signal.h> 54 #include <fcntl.h> 55 #include <netdb.h> 56 #include <unistd.h> 57 #include <pwd.h> 58 #include <errno.h> 59 #include <stdio.h> 60 #include <ctype.h> 61 #include <string.h> 62 #include <syslog.h> 63 64 int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *)); 65 static int __icheckhost __P((u_int32_t, const char *)); 66 static char *__gethostloop __P((u_int32_t)); 67 68 int 69 rcmd(ahost, rport, locuser, remuser, cmd, fd2p) 70 char **ahost; 71 u_short rport; 72 const char *locuser, *remuser, *cmd; 73 int *fd2p; 74 { 75 struct hostent *hp; 76 struct sockaddr_in sin, from; 77 struct pollfd reads[2]; 78 int oldmask; 79 pid_t pid; 80 int s, lport, timo; 81 char c; 82 83 pid = getpid(); 84 hp = gethostbyname(*ahost); 85 if (hp == NULL) { 86 herror(*ahost); 87 return (-1); 88 } 89 *ahost = hp->h_name; 90 oldmask = sigblock(sigmask(SIGURG)); 91 for (timo = 1, lport = IPPORT_RESERVED - 1;;) { 92 s = rresvport(&lport); 93 if (s < 0) { 94 if (errno == EAGAIN) 95 (void)fprintf(stderr, 96 "rcmd: socket: All ports in use\n"); 97 else 98 (void)fprintf(stderr, "rcmd: socket: %s\n", 99 strerror(errno)); 100 sigsetmask(oldmask); 101 return (-1); 102 } 103 fcntl(s, F_SETOWN, pid); 104 sin.sin_len = sizeof(struct sockaddr_in); 105 sin.sin_family = hp->h_addrtype; 106 sin.sin_port = rport; 107 bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length); 108 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 109 break; 110 (void)close(s); 111 if (errno == EADDRINUSE) { 112 lport--; 113 continue; 114 } 115 if (errno == ECONNREFUSED && timo <= 16) { 116 (void)sleep(timo); 117 timo *= 2; 118 continue; 119 } 120 if (hp->h_addr_list[1] != NULL) { 121 int oerrno = errno; 122 123 (void)fprintf(stderr, "connect to address %s: ", 124 inet_ntoa(sin.sin_addr)); 125 errno = oerrno; 126 perror(0); 127 hp->h_addr_list++; 128 bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length); 129 (void)fprintf(stderr, "Trying %s...\n", 130 inet_ntoa(sin.sin_addr)); 131 continue; 132 } 133 (void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno)); 134 sigsetmask(oldmask); 135 return (-1); 136 } 137 lport--; 138 if (fd2p == 0) { 139 write(s, "", 1); 140 lport = 0; 141 } else { 142 char num[8]; 143 int s2 = rresvport(&lport), s3; 144 int len = sizeof(from); 145 146 if (s2 < 0) 147 goto bad; 148 listen(s2, 1); 149 (void)snprintf(num, sizeof(num), "%d", lport); 150 if (write(s, num, strlen(num)+1) != strlen(num)+1) { 151 (void)fprintf(stderr, 152 "rcmd: write (setting up stderr): %s\n", 153 strerror(errno)); 154 (void)close(s2); 155 goto bad; 156 } 157 reads[0].fd = s; 158 reads[1].fd = s2; 159 reads[0].events = reads[1].events = reads[0].revents = 160 reads[1].revents = POLLIN; 161 errno = 0; 162 if (poll(reads, 2, INFTIM) < 1 || 163 (reads[1].revents & POLLIN) == 0) { 164 if (errno != 0) 165 (void)fprintf(stderr, 166 "rcmd: poll (setting up stderr): %s\n", 167 strerror(errno)); 168 else 169 (void)fprintf(stderr, 170 "poll: protocol failure in circuit setup\n"); 171 (void)close(s2); 172 goto bad; 173 } 174 s3 = accept(s2, (struct sockaddr *)&from, &len); 175 (void)close(s2); 176 if (s3 < 0) { 177 (void)fprintf(stderr, 178 "rcmd: accept: %s\n", strerror(errno)); 179 lport = 0; 180 goto bad; 181 } 182 *fd2p = s3; 183 from.sin_port = ntohs(from.sin_port); 184 if (from.sin_family != AF_INET || 185 from.sin_port >= IPPORT_RESERVED || 186 from.sin_port < IPPORT_RESERVED / 2) { 187 (void)fprintf(stderr, 188 "socket: protocol failure in circuit setup.\n"); 189 goto bad2; 190 } 191 } 192 (void)write(s, locuser, strlen(locuser)+1); 193 (void)write(s, remuser, strlen(remuser)+1); 194 (void)write(s, cmd, strlen(cmd)+1); 195 if (read(s, &c, 1) != 1) { 196 (void)fprintf(stderr, 197 "rcmd: %s: %s\n", *ahost, strerror(errno)); 198 goto bad2; 199 } 200 if (c != 0) { 201 while (read(s, &c, 1) == 1) { 202 (void)write(STDERR_FILENO, &c, 1); 203 if (c == '\n') 204 break; 205 } 206 goto bad2; 207 } 208 sigsetmask(oldmask); 209 return (s); 210 bad2: 211 if (lport) 212 (void)close(*fd2p); 213 bad: 214 (void)close(s); 215 sigsetmask(oldmask); 216 return (-1); 217 } 218 219 int 220 rresvport(alport) 221 int *alport; 222 { 223 struct sockaddr_in sin; 224 int s; 225 226 sin.sin_len = sizeof(struct sockaddr_in); 227 sin.sin_family = AF_INET; 228 sin.sin_addr.s_addr = INADDR_ANY; 229 s = socket(AF_INET, SOCK_STREAM, 0); 230 if (s < 0) 231 return (-1); 232 for (;;) { 233 sin.sin_port = htons((u_short)*alport); 234 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 235 return (s); 236 if (errno != EADDRINUSE) { 237 (void)close(s); 238 return (-1); 239 } 240 (*alport)--; 241 if (*alport == IPPORT_RESERVED/2) { 242 (void)close(s); 243 errno = EAGAIN; /* close */ 244 return (-1); 245 } 246 } 247 } 248 249 int __check_rhosts_file = 1; 250 char *__rcmd_errstr; 251 252 int 253 ruserok(rhost, superuser, ruser, luser) 254 const char *rhost, *ruser, *luser; 255 int superuser; 256 { 257 struct hostent *hp; 258 char **ap; 259 int i; 260 #define MAXADDRS 35 261 u_int32_t addrs[MAXADDRS + 1]; 262 263 if ((hp = gethostbyname(rhost)) == NULL) 264 return (-1); 265 for (i = 0, ap = hp->h_addr_list; *ap && i < MAXADDRS; ++ap, ++i) 266 bcopy(*ap, &addrs[i], sizeof(addrs[i])); 267 addrs[i] = 0; 268 269 for (i = 0; i < MAXADDRS && addrs[i]; i++) 270 if (iruserok(addrs[i], superuser, ruser, luser) == 0) 271 return (0); 272 return (-1); 273 } 274 275 /* 276 * New .rhosts strategy: We are passed an ip address. We spin through 277 * hosts.equiv and .rhosts looking for a match. When the .rhosts only 278 * has ip addresses, we don't have to trust a nameserver. When it 279 * contains hostnames, we spin through the list of addresses the nameserver 280 * gives us and look for a match. 281 * 282 * Returns 0 if ok, -1 if not ok. 283 */ 284 int 285 iruserok(raddr, superuser, ruser, luser) 286 u_int32_t raddr; 287 int superuser; 288 const char *ruser, *luser; 289 { 290 register char *cp; 291 struct stat sbuf; 292 struct passwd *pwd; 293 FILE *hostf; 294 uid_t uid; 295 gid_t gid; 296 int first; 297 char pbuf[MAXPATHLEN]; 298 299 first = 1; 300 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); 301 again: 302 if (hostf) { 303 if (__ivaliduser(hostf, raddr, luser, ruser) == 0) { 304 (void)fclose(hostf); 305 return (0); 306 } 307 (void)fclose(hostf); 308 } 309 if (first == 1 && (__check_rhosts_file || superuser)) { 310 first = 0; 311 if ((pwd = getpwnam(luser)) == NULL) 312 return (-1); 313 (void)strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1); 314 (void)strncat(pbuf, "/.rhosts", sizeof(pbuf) - strlen(pbuf) - 1); 315 316 /* 317 * Change effective uid while opening .rhosts. If root and 318 * reading an NFS mounted file system, can't read files that 319 * are protected read/write owner only. 320 */ 321 uid = geteuid(); 322 gid = getegid(); 323 (void)setegid(pwd->pw_gid); 324 initgroups(pwd->pw_name, pwd->pw_gid); 325 (void)seteuid(pwd->pw_uid); 326 hostf = fopen(pbuf, "r"); 327 (void)seteuid(uid); 328 (void)setegid(gid); 329 330 if (hostf == NULL) 331 return (-1); 332 /* 333 * If not a regular file, or is owned by someone other than 334 * user or root or if writeable by anyone but the owner, quit. 335 */ 336 cp = NULL; 337 if (lstat(pbuf, &sbuf) < 0) 338 cp = ".rhosts lstat failed"; 339 else if (!S_ISREG(sbuf.st_mode)) 340 cp = ".rhosts not regular file"; 341 else if (fstat(fileno(hostf), &sbuf) < 0) 342 cp = ".rhosts fstat failed"; 343 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) 344 cp = "bad .rhosts owner"; 345 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) 346 cp = ".rhosts writeable by other than owner"; 347 /* If there were any problems, quit. */ 348 if (cp) { 349 __rcmd_errstr = cp; 350 (void)fclose(hostf); 351 return (-1); 352 } 353 goto again; 354 } 355 return (-1); 356 } 357 358 /* 359 * XXX 360 * Don't make static, used by lpd(8). 361 * 362 * Returns 0 if ok, -1 if not ok. 363 */ 364 int 365 __ivaliduser(hostf, raddr, luser, ruser) 366 FILE *hostf; 367 u_int32_t raddr; 368 const char *luser, *ruser; 369 { 370 register char *user, *p; 371 int ch; 372 char buf[MAXHOSTNAMELEN + 128]; /* host + login */ 373 const char *auser, *ahost; 374 int hostok, userok; 375 char *rhost = NULL; 376 int firsttime = 1; 377 char domain[MAXHOSTNAMELEN]; 378 379 getdomainname(domain, sizeof(domain)); 380 381 while (fgets(buf, sizeof(buf), hostf)) { 382 p = buf; 383 /* Skip lines that are too long. */ 384 if (strchr(p, '\n') == NULL) { 385 while ((ch = getc(hostf)) != '\n' && ch != EOF) 386 ; 387 continue; 388 } 389 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { 390 *p = isupper(*p) ? tolower(*p) : *p; 391 p++; 392 } 393 if (*p == ' ' || *p == '\t') { 394 *p++ = '\0'; 395 while (*p == ' ' || *p == '\t') 396 p++; 397 user = p; 398 while (*p != '\n' && *p != ' ' && 399 *p != '\t' && *p != '\0') 400 p++; 401 } else 402 user = p; 403 *p = '\0'; 404 405 if (p == buf) 406 continue; 407 408 auser = *user ? user : luser; 409 ahost = buf; 410 411 if (ahost[0] == '+') 412 switch (ahost[1]) { 413 case '\0': 414 hostok = 1; 415 break; 416 417 case '@': 418 if (firsttime) { 419 rhost = __gethostloop(raddr); 420 firsttime = 0; 421 } 422 if (rhost) 423 hostok = innetgr(&ahost[2], rhost, 424 NULL, domain); 425 else 426 hostok = 0; 427 break; 428 429 default: 430 hostok = __icheckhost(raddr, &ahost[1]); 431 break; 432 } 433 else if (ahost[0] == '-') 434 switch (ahost[1]) { 435 case '\0': 436 hostok = -1; 437 break; 438 439 case '@': 440 if (firsttime) { 441 rhost = __gethostloop(raddr); 442 firsttime = 0; 443 } 444 if (rhost) 445 hostok = -innetgr(&ahost[2], rhost, 446 NULL, domain); 447 else 448 hostok = 0; 449 break; 450 451 default: 452 hostok = -__icheckhost(raddr, &ahost[1]); 453 break; 454 } 455 else 456 hostok = __icheckhost(raddr, ahost); 457 458 459 if (auser[0] == '+') 460 switch (auser[1]) { 461 case '\0': 462 userok = 1; 463 break; 464 465 case '@': 466 userok = innetgr(&auser[2], NULL, ruser, 467 domain); 468 break; 469 470 default: 471 userok = strcmp(ruser, &auser[1]) == 0; 472 break; 473 } 474 else if (auser[0] == '-') 475 switch (auser[1]) { 476 case '\0': 477 userok = -1; 478 break; 479 480 case '@': 481 userok = -innetgr(&auser[2], NULL, ruser, 482 domain); 483 break; 484 485 default: 486 userok = -(strcmp(ruser, &auser[1]) == 0); 487 break; 488 } 489 else 490 userok = strcmp(ruser, auser) == 0; 491 492 /* Check if one component did not match */ 493 if (hostok == 0 || userok == 0) 494 continue; 495 496 /* Check if we got a forbidden pair */ 497 if (userok == -1 || hostok == -1) 498 return -1; 499 500 /* Check if we got a valid pair */ 501 if (hostok == 1 && userok == 1) 502 return 0; 503 } 504 return -1; 505 } 506 507 /* 508 * Returns "true" if match, 0 if no match. 509 */ 510 static int 511 __icheckhost(raddr, lhost) 512 u_int32_t raddr; 513 const char *lhost; 514 { 515 register struct hostent *hp; 516 register u_int32_t laddr; 517 register char **pp; 518 519 /* Try for raw ip address first. */ 520 if (isdigit(*lhost) && (int32_t)(laddr = inet_addr(lhost)) != -1) 521 return (raddr == laddr); 522 523 /* Better be a hostname. */ 524 if ((hp = gethostbyname(lhost)) == NULL) 525 return (0); 526 527 /* Spin through ip addresses. */ 528 for (pp = hp->h_addr_list; *pp; ++pp) 529 if (!bcmp(&raddr, *pp, sizeof(u_int32_t))) 530 return (1); 531 532 /* No match. */ 533 return (0); 534 } 535 536 /* 537 * Return the hostname associated with the supplied address. 538 * Do a reverse lookup as well for security. If a loop cannot 539 * be found, pack the result of inet_ntoa() into the string. 540 */ 541 static char * 542 __gethostloop(raddr) 543 u_int32_t raddr; 544 { 545 static char remotehost[MAXHOSTNAMELEN]; 546 struct hostent *hp; 547 struct in_addr in; 548 549 hp = gethostbyaddr((char *) &raddr, sizeof(raddr), AF_INET); 550 if (hp == NULL) 551 return (NULL); 552 553 /* 554 * Look up the name and check that the supplied 555 * address is in the list 556 */ 557 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 558 remotehost[sizeof(remotehost) - 1] = '\0'; 559 hp = gethostbyname(remotehost); 560 if (hp == NULL) 561 return (NULL); 562 563 for (; hp->h_addr_list[0] != NULL; hp->h_addr_list++) 564 if (!bcmp(hp->h_addr_list[0], (caddr_t)&raddr, sizeof(raddr))) 565 return (remotehost); 566 567 /* 568 * either the DNS adminstrator has made a configuration 569 * mistake, or someone has attempted to spoof us 570 */ 571 in.s_addr = raddr; 572 syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s", 573 inet_ntoa(in), hp->h_name); 574 return (NULL); 575 } 576