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