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