1 /* $NetBSD: rsh.c,v 1.20 2003/06/14 22:43:32 joff Exp $ */ 2 3 /*- 4 * Copyright (c) 1983, 1990, 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 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)rsh.c 8.4 (Berkeley) 4/29/95"; 45 #else 46 __RCSID("$NetBSD: rsh.c,v 1.20 2003/06/14 22:43:32 joff Exp $"); 47 #endif 48 #endif /* not lint */ 49 50 #include <sys/types.h> 51 #include <sys/socket.h> 52 #include <sys/ioctl.h> 53 #include <sys/file.h> 54 #include <poll.h> 55 56 #include <netinet/in.h> 57 #include <netinet/tcp.h> 58 #include <netdb.h> 59 60 #include <err.h> 61 #include <errno.h> 62 #include <pwd.h> 63 #include <signal.h> 64 #include <stdarg.h> 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <unistd.h> 69 70 #include "pathnames.h" 71 72 #ifdef KERBEROS 73 #include <kerberosIV/des.h> 74 #include <kerberosIV/krb.h> 75 76 CREDENTIALS cred; 77 Key_schedule schedule; 78 int use_kerberos = 1, doencrypt; 79 char dst_realm_buf[REALM_SZ], *dest_realm; 80 81 void warning(const char *, ...); 82 #endif 83 84 /* 85 * rsh - remote shell 86 */ 87 int remerr; 88 89 static int sigs[] = { SIGINT, SIGTERM, SIGQUIT }; 90 91 char *copyargs(char **); 92 void sendsig(int); 93 int checkfd(struct pollfd *, int); 94 void talk(int, sigset_t *, pid_t, int); 95 void usage(void); 96 int main(int, char **); 97 #ifdef IN_RCMD 98 int orcmd(char **, int, const char *, 99 const char *, const char *, int *); 100 int orcmd_af(char **, int, const char *, 101 const char *, const char *, int *, int); 102 #endif 103 104 int 105 main(int argc, char **argv) 106 { 107 struct passwd *pw; 108 struct servent *sp; 109 sigset_t oset, nset; 110 struct protoent *proto; 111 112 #ifdef IN_RCMD 113 char *locuser = 0, *loop; 114 #endif /* IN_RCMD */ 115 int argoff, asrsh, ch, dflag, nflag, one, rem, i; 116 pid_t pid; 117 uid_t uid; 118 char *args, *host, *p, *user, *name; 119 char *service=NULL; 120 121 argoff = asrsh = dflag = nflag = 0; 122 one = 1; 123 host = user = NULL; 124 sp = NULL; 125 126 #ifndef IN_RCMD 127 /* 128 * If called as something other than "rsh" use it as the host name, 129 * only for rsh. 130 */ 131 if (strcmp(getprogname(), "rsh") == 0) 132 asrsh = 1; 133 else { 134 host = strdup(getprogname()); 135 if (host == NULL) 136 err(1, NULL); 137 } 138 #endif /* IN_RCMD */ 139 140 /* handle "rsh host flags" */ 141 if (!host && argc > 2 && argv[1][0] != '-') { 142 host = argv[1]; 143 argoff = 1; 144 } 145 146 #ifdef IN_RCMD 147 if ((loop = getenv("RCMD_LOOP")) && strcmp(loop, "YES") == 0) 148 warnx("rcmd appears to be looping!"); 149 150 putenv("RCMD_LOOP=YES"); 151 152 # ifdef KERBEROS 153 # ifdef CRYPT 154 # define OPTIONS "8KLdek:l:np:u:wx" 155 # else 156 # define OPTIONS "8KLdek:l:np:u:w" 157 # endif 158 # else 159 # define OPTIONS "8KLdel:np:u:w" 160 # endif 161 162 #else /* IN_RCMD */ 163 164 # ifdef KERBEROS 165 # ifdef CRYPT 166 # define OPTIONS "8KLdek:l:np:wx" 167 # else 168 # define OPTIONS "8KLdek:l:np:w" 169 # endif 170 # else 171 # define OPTIONS "8KLdel:np:w" 172 # endif 173 174 #endif /* IN_RCMD */ 175 176 if (!(pw = getpwuid(uid = getuid()))) 177 errx(1, "unknown user id"); 178 179 if ((name = strdup(pw->pw_name)) == NULL) 180 err(1, "malloc"); 181 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 182 switch(ch) { 183 case 'K': 184 #ifdef KERBEROS 185 use_kerberos = 0; 186 #endif 187 break; 188 case 'L': /* -8Lew are ignored to allow rlogin aliases */ 189 case 'e': 190 case 'w': 191 case '8': 192 break; 193 case 'd': 194 dflag = 1; 195 break; 196 case 'l': 197 user = optarg; 198 break; 199 #ifdef KERBEROS 200 case 'k': 201 strlcpy(dest_realm_buf, optarg, sizeof(dest_realm_buf)); 202 dest_realm = dst_realm_buf; 203 break; 204 #endif 205 case 'n': 206 nflag = 1; 207 break; 208 case 'p': 209 service = optarg; 210 sp = getservbyname(service, "tcp"); 211 if (sp == NULL) { /* number given, no name */ 212 sp = malloc(sizeof(*sp)); 213 memset(sp, 0, sizeof(*sp)); 214 sp->s_name = service; 215 sp->s_proto = "tcp"; 216 sp->s_port = atoi(service); 217 if (sp->s_port <= 0 || sp->s_port > IPPORT_ANONMAX) 218 errx(1,"port must be between 1 and %d", IPPORT_ANONMAX); 219 } 220 break; 221 #ifdef IN_RCMD 222 case 'u': 223 if (getuid() != 0 && optarg && name && 224 strcmp(name, optarg) != 0) 225 errx(1,"only super user can use the -u option"); 226 locuser = optarg; 227 break; 228 #endif /* IN_RCMD */ 229 #ifdef KERBEROS 230 #ifdef CRYPT 231 case 'x': 232 doencrypt = 1; 233 des_set_key((des_cblock *) cred.session, schedule); 234 break; 235 #endif 236 #endif 237 case '?': 238 default: 239 usage(); 240 } 241 optind += argoff; 242 243 /* if haven't gotten a host yet, do so */ 244 if (!host && !(host = argv[optind++])) 245 usage(); 246 247 /* if no further arguments, must have been called as rlogin. */ 248 if (!argv[optind]) { 249 #ifdef IN_RCMD 250 usage(); 251 #else 252 if (asrsh) 253 *argv = "rlogin"; 254 execv(_PATH_RLOGIN, argv); 255 err(1, "can't exec %s", _PATH_RLOGIN); 256 #endif 257 } 258 259 argc -= optind; 260 argv += optind; 261 262 /* Accept user1@host format, though "-l user2" overrides user1 */ 263 p = strchr(host, '@'); 264 if (p) { 265 *p = '\0'; 266 if (!user && p > host) 267 user = host; 268 host = p + 1; 269 if (*host == '\0') 270 usage(); 271 } 272 if (!user) 273 user = name; 274 275 #ifdef KERBEROS 276 #ifdef CRYPT 277 /* -x turns off -n */ 278 if (doencrypt) 279 nflag = 0; 280 #endif 281 #endif 282 283 args = copyargs(argv); 284 285 #ifdef KERBEROS 286 if (use_kerberos) { 287 if (sp == NULL) { 288 sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp"); 289 } 290 if (sp == NULL) { 291 use_kerberos = 0; 292 warning("can't get entry for %s/tcp service", 293 doencrypt ? "ekshell" : "kshell"); 294 } 295 } 296 #endif 297 if (sp == NULL) 298 sp = getservbyname("shell", "tcp"); 299 if (sp == NULL) 300 errx(1, "shell/tcp: unknown service"); 301 302 #ifdef KERBEROS 303 try_connect: 304 if (use_kerberos) { 305 #if 1 306 struct hostent *hp; 307 308 /* fully qualify hostname (needed for krb_realmofhost) */ 309 hp = gethostbyname(host); 310 if (hp != NULL && !(host = strdup(hp->h_name))) 311 err(1, "strdup"); 312 #endif 313 314 rem = KSUCCESS; 315 errno = 0; 316 if (dest_realm == NULL) 317 dest_realm = krb_realmofhost(host); 318 319 #ifdef CRYPT 320 if (doencrypt) 321 rem = krcmd_mutual(&host, sp->s_port, user, args, 322 &remerr, dest_realm, &cred, schedule); 323 else 324 #endif 325 rem = krcmd(&host, sp->s_port, user, args, &remerr, 326 dest_realm); 327 if (rem < 0) { 328 use_kerberos = 0; 329 sp = getservbyname("shell", "tcp"); 330 if (sp == NULL) 331 errx(1, "shell/tcp: unknown service"); 332 if (errno == ECONNREFUSED) 333 warning("remote host doesn't support Kerberos"); 334 if (errno == ENOENT) 335 warning("can't provide Kerberos auth data"); 336 goto try_connect; 337 } 338 } else { 339 if (doencrypt) 340 errx(1, "the -x flag requires Kerberos authentication."); 341 #ifdef IN_RCMD 342 rem = orcmd_af(&host, sp->s_port, locuser ? locuser : 343 #else 344 rem = rcmd_af(&host, sp->s_port, 345 #endif 346 name, 347 user, args, &remerr, PF_UNSPEC); 348 } 349 #else /* KERBEROS */ 350 351 #ifdef IN_RCMD 352 rem = orcmd_af(&host, sp->s_port, locuser ? locuser : 353 #else 354 rem = rcmd_af(&host, sp->s_port, 355 #endif 356 name, user, args, &remerr, PF_UNSPEC); 357 #endif /* KERBEROS */ 358 (void)free(name); 359 360 if (rem < 0) 361 exit(1); 362 363 if (remerr < 0) 364 errx(1, "can't establish stderr"); 365 if (dflag) { 366 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, 367 sizeof(one)) < 0) 368 warn("setsockopt remote"); 369 if (setsockopt(remerr, SOL_SOCKET, SO_DEBUG, &one, 370 sizeof(one)) < 0) 371 warn("setsockopt stderr"); 372 } 373 proto = getprotobyname("tcp"); 374 setsockopt(rem, proto->p_proto, TCP_NODELAY, &one, sizeof(one)); 375 setsockopt(remerr, proto->p_proto, TCP_NODELAY, &one, sizeof(one)); 376 377 378 (void) setuid(uid); 379 380 (void) sigemptyset(&nset); 381 for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) 382 (void) sigaddset(&nset, sigs[i]); 383 384 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 385 386 for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) { 387 struct sigaction sa; 388 389 if (sa.sa_handler != SIG_IGN) { 390 sa.sa_handler = sendsig; 391 (void) sigaction(sigs[i], &sa, NULL); 392 } 393 } 394 395 if (!nflag) { 396 pid = fork(); 397 if (pid < 0) 398 err(1, "fork"); 399 } 400 else 401 pid = -1; 402 403 #if defined(KERBEROS) && defined(CRYPT) 404 if (!doencrypt) 405 #endif 406 { 407 (void)ioctl(remerr, FIONBIO, &one); 408 (void)ioctl(rem, FIONBIO, &one); 409 } 410 411 talk(nflag, &oset, pid, rem); 412 413 if (!nflag) 414 (void)kill(pid, SIGKILL); 415 exit(0); 416 } 417 418 int 419 checkfd(struct pollfd *fdp, int outfd) 420 { 421 int nr, nw; 422 char buf[BUFSIZ]; 423 424 if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP)) 425 return -1; 426 427 if ((fdp->revents & POLLIN) == 0) 428 return 0; 429 430 errno = 0; 431 #if defined(KERBEROS) && defined(CRYPT) 432 if (doencrypt) 433 nr = des_read(fdp->fd, buf, sizeof buf); 434 else 435 #endif 436 nr = read(fdp->fd, buf, sizeof buf); 437 438 if (nr <= 0) { 439 if (errno != EAGAIN) 440 return -1; 441 else 442 return 0; 443 } 444 else { 445 char *bc = buf; 446 while (nr) { 447 if ((nw = write(outfd, bc, nr)) <= 0) 448 return -1; 449 nr -= nw; 450 bc += nw; 451 } 452 return 0; 453 } 454 } 455 456 void 457 talk(int nflag, sigset_t *oset, __pid_t pid, int rem) 458 { 459 int nr, nw, nfds; 460 struct pollfd fds[2], *fdp = &fds[0]; 461 char *bp, buf[BUFSIZ]; 462 463 464 if (!nflag && pid == 0) { 465 (void)close(remerr); 466 467 fdp->events = POLLOUT|POLLNVAL|POLLERR|POLLHUP; 468 fdp->fd = rem; 469 nr = 0; 470 bp = buf; 471 472 for (;;) { 473 errno = 0; 474 475 if (nr == 0) { 476 if ((nr = read(0, buf, sizeof buf)) == 0) 477 goto done; 478 if (nr == -1) { 479 if (errno == EIO) 480 goto done; 481 if (errno == EINTR) 482 continue; 483 err(1, "read"); 484 } 485 bp = buf; 486 } 487 488 rewrite: if (poll(fdp, 1, INFTIM) == -1) { 489 if (errno != EINTR) 490 err(1, "poll"); 491 goto rewrite; 492 } 493 494 if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP)) 495 err(1, "poll"); 496 497 if ((fdp->revents & POLLOUT) == 0) 498 goto rewrite; 499 500 #if defined(KERBEROS) && defined(CRYPT) 501 if (doencrypt) 502 nw = des_write(rem, bp, nr); 503 else 504 #endif 505 nw = write(rem, bp, nr); 506 507 if (nw < 0) { 508 if (errno == EAGAIN) 509 continue; 510 err(1, "write"); 511 } 512 bp += nw; 513 nr -= nw; 514 } 515 done: 516 (void)shutdown(rem, 1); 517 exit(0); 518 } 519 520 (void) sigprocmask(SIG_SETMASK, oset, NULL); 521 fds[0].events = fds[1].events = POLLIN|POLLNVAL|POLLERR|POLLHUP; 522 fds[0].fd = remerr; 523 fds[1].fd = rem; 524 fdp = &fds[0]; 525 nfds = 2; 526 do { 527 if (poll(fdp, nfds, INFTIM) == -1) { 528 if (errno != EINTR) 529 err(1, "poll"); 530 continue; 531 } 532 if (fds[0].events != 0 && checkfd(&fds[0], 2) == -1) { 533 nfds--; 534 fds[0].events = 0; 535 fdp = &fds[1]; 536 } 537 if (fds[1].events != 0 && checkfd(&fds[1], 1) == -1) { 538 nfds--; 539 fds[1].events = 0; 540 } 541 } 542 while (nfds); 543 } 544 545 void 546 sendsig(int sig) 547 { 548 char signo; 549 550 signo = sig; 551 #ifdef KERBEROS 552 #ifdef CRYPT 553 if (doencrypt) 554 (void)des_write(remerr, &signo, 1); 555 else 556 #endif 557 #endif 558 (void)write(remerr, &signo, 1); 559 } 560 561 #ifdef KERBEROS 562 /* VARARGS */ 563 void 564 warning(const char *fmt, ...) 565 { 566 va_list ap; 567 568 va_start(ap, fmt); 569 (void) fprintf(stderr, "%s: warning, using standard rsh: ", 570 getprogname()); 571 (void) vfprintf(stderr, fmt, ap); 572 va_end(ap); 573 (void) fprintf(stderr, ".\n"); 574 } 575 #endif 576 577 char * 578 copyargs(char **argv) 579 { 580 int cc; 581 char **ap, *args, *p, *ep; 582 583 cc = 0; 584 for (ap = argv; *ap; ++ap) 585 cc += strlen(*ap) + 1; 586 if (!(args = malloc((u_int)cc))) 587 err(1, "malloc"); 588 ep = args + cc; 589 for (p = args, *p = '\0', ap = argv; *ap; ++ap) { 590 (void)strlcpy(p, *ap, ep - p); 591 p += strlen(p); 592 if (ap[1]) 593 *p++ = ' '; 594 } 595 *p = '\0'; 596 return (args); 597 } 598 599 void 600 usage(void) 601 { 602 603 (void)fprintf(stderr, 604 "usage: %s [-nd%s]%s[-l login] [-p port]%s [login@]host %s\n", getprogname(), 605 #ifdef KERBEROS 606 #ifdef CRYPT 607 "x", " [-k realm] ", 608 #else 609 "", " [-k realm] ", 610 #endif 611 #else 612 "", " ", 613 #endif 614 #ifdef IN_RCMD 615 " [-u locuser]", "command" 616 #else 617 "", "[command]" 618 #endif 619 ); 620 exit(1); 621 } 622