1/* 2 * Copyright (c) 1983, 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41#ifndef lint 42static const char copyright[] = 43"@(#) Copyright (c) 1983, 1990, 1993\n\ 44 The Regents of the University of California. All rights reserved.\n"; 45#endif /* not lint */ 46 47#if 0 48#ifndef lint 49static const char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93"; 50#endif /* not lint */ 51#endif 52 53#include <sys/cdefs.h> 54__FBSDID("$FreeBSD$"); 55 56/* 57 * rlogin - remote login 58 */ 59 60#include <sys/param.h> 61#include <sys/ioctl.h> 62#include <sys/socket.h> 63#include <sys/time.h> 64#include <sys/resource.h> 65#include <sys/wait.h> 66 67#include <netinet/in.h> 68#include <netinet/in_systm.h> 69#include <netinet/ip.h> 70#include <netinet/tcp.h> 71 72#include <err.h> 73#include <errno.h> 74#include <fcntl.h> 75#include <netdb.h> 76#include <paths.h> 77#include <pwd.h> 78#include <setjmp.h> 79#include <termios.h> 80#include <signal.h> 81#include <stdio.h> 82#include <stdlib.h> 83#include <string.h> 84#include <unistd.h> 85 86#ifndef TIOCPKT_WINDOW 87#define TIOCPKT_WINDOW 0x80 88#endif 89 90/* concession to Sun */ 91#ifndef SIGUSR1 92#define SIGUSR1 30 93#endif 94 95static int eight, rem; 96static struct termios deftty; 97 98static int family = PF_UNSPEC; 99 100static int noescape; 101static u_char escapechar = '~'; 102 103#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 104static struct winsize winsize; 105 106static void catch_child(int); 107static void copytochild(int); 108static _Noreturn void doit(long); 109static _Noreturn void done(int); 110static void echo(char); 111static u_int getescape(const char *); 112static void lostpeer(int); 113static void mode(int); 114static void msg(const char *); 115static void oob(int); 116static int reader(int); 117static void sendwindow(void); 118static void setsignal(int); 119static void sigwinch(int); 120static void stop(char); 121static _Noreturn void usage(void); 122static void writer(void); 123static void writeroob(int); 124 125int 126main(int argc, char *argv[]) 127{ 128 struct passwd *pw; 129 struct servent *sp; 130 struct termios tty; 131 long omask; 132 int argoff, ch, dflag, Dflag, one; 133 uid_t uid; 134 char *host, *localname, *p, *user, term[1024]; 135 speed_t ospeed; 136 struct sockaddr_storage ss; 137 socklen_t sslen; 138 size_t len, len2; 139 int i; 140 141 argoff = dflag = Dflag = 0; 142 one = 1; 143 host = localname = user = NULL; 144 145 if ((p = strrchr(argv[0], '/'))) 146 ++p; 147 else 148 p = argv[0]; 149 150 if (strcmp(p, "rlogin")) 151 host = p; 152 153 /* handle "rlogin host flags" */ 154 if (!host && argc > 2 && argv[1][0] != '-') { 155 host = argv[1]; 156 argoff = 1; 157 } 158 159#define OPTIONS "468DEde:i:l:" 160 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 161 switch(ch) { 162 case '4': 163 family = PF_INET; 164 break; 165 166 case '6': 167 family = PF_INET6; 168 break; 169 170 case '8': 171 eight = 1; 172 break; 173 case 'D': 174 Dflag = 1; 175 break; 176 case 'E': 177 noescape = 1; 178 break; 179 case 'd': 180 dflag = 1; 181 break; 182 case 'e': 183 noescape = 0; 184 escapechar = getescape(optarg); 185 break; 186 case 'i': 187 if (getuid() != 0) 188 errx(1, "-i user: permission denied"); 189 localname = optarg; 190 break; 191 case 'l': 192 user = optarg; 193 break; 194 case '?': 195 default: 196 usage(); 197 } 198 optind += argoff; 199 200 /* if haven't gotten a host yet, do so */ 201 if (!host && !(host = argv[optind++])) 202 usage(); 203 204 if (argv[optind]) 205 usage(); 206 207 if (!(pw = getpwuid(uid = getuid()))) 208 errx(1, "unknown user id"); 209 if (!user) 210 user = pw->pw_name; 211 if (!localname) 212 localname = pw->pw_name; 213 214 sp = NULL; 215 sp = getservbyname("login", "tcp"); 216 if (sp == NULL) 217 errx(1, "login/tcp: unknown service"); 218 219 if ((p = getenv("TERM")) != NULL) 220 (void)strlcpy(term, p, sizeof(term)); 221 len = strlen(term); 222 if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) { 223 /* start at 2 to include the / */ 224 for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++) 225 i /= 10; 226 if (len + len2 < sizeof(term)) 227 (void)snprintf(term + len, len2 + 1, "/%d", ospeed); 228 } 229 230 (void)get_window_size(0, &winsize); 231 232 (void)signal(SIGPIPE, lostpeer); 233 /* will use SIGUSR1 for window size hack, so hold it off */ 234 omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 235 /* 236 * We set SIGURG and SIGUSR1 below so that an 237 * incoming signal will be held pending rather than being 238 * discarded. Note that these routines will be ready to get 239 * a signal by the time that they are unblocked below. 240 */ 241 (void)signal(SIGURG, copytochild); 242 (void)signal(SIGUSR1, writeroob); 243 244 rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family); 245 246 if (rem < 0) 247 exit(1); 248 249 if (dflag && 250 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 251 warn("setsockopt"); 252 if (Dflag && 253 setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 254 warn("setsockopt NODELAY (ignored)"); 255 256 sslen = sizeof(ss); 257 one = IPTOS_LOWDELAY; 258 if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && 259 ss.ss_family == AF_INET) { 260 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 261 sizeof(int)) < 0) 262 warn("setsockopt TOS (ignored)"); 263 } else 264 if (ss.ss_family == AF_INET) 265 warn("setsockopt getsockname failed"); 266 267 (void)setuid(uid); 268 doit(omask); 269 /*NOTREACHED*/ 270} 271 272static int child; 273 274static void 275doit(long omask) 276{ 277 278 (void)signal(SIGINT, SIG_IGN); 279 setsignal(SIGHUP); 280 setsignal(SIGQUIT); 281 mode(1); 282 child = fork(); 283 if (child == -1) { 284 warn("fork"); 285 done(1); 286 } 287 if (child == 0) { 288 if (reader(omask) == 0) { 289 msg("connection closed"); 290 exit(0); 291 } 292 sleep(1); 293 msg("\007connection closed"); 294 exit(1); 295 } 296 297 /* 298 * We may still own the socket, and may have a pending SIGURG (or might 299 * receive one soon) that we really want to send to the reader. When 300 * one of these comes in, the trap copytochild simply copies such 301 * signals to the child. We can now unblock SIGURG and SIGUSR1 302 * that were set above. 303 */ 304 (void)sigsetmask(omask); 305 (void)signal(SIGCHLD, catch_child); 306 writer(); 307 msg("closed connection"); 308 done(0); 309} 310 311/* trap a signal, unless it is being ignored. */ 312static void 313setsignal(int sig) 314{ 315 int omask = sigblock(sigmask(sig)); 316 317 if (signal(sig, exit) == SIG_IGN) 318 (void)signal(sig, SIG_IGN); 319 (void)sigsetmask(omask); 320} 321 322static void 323done(int status) 324{ 325 int w, wstatus; 326 327 mode(0); 328 if (child > 0) { 329 /* make sure catch_child does not snap it up */ 330 (void)signal(SIGCHLD, SIG_DFL); 331 if (kill(child, SIGKILL) >= 0) 332 while ((w = wait(&wstatus)) > 0 && w != child); 333 } 334 exit(status); 335} 336 337static int dosigwinch; 338 339/* 340 * This is called when the reader process gets the out-of-band (urgent) 341 * request to turn on the window-changing protocol. 342 */ 343/* ARGSUSED */ 344static void 345writeroob(int signo __unused) 346{ 347 if (dosigwinch == 0) { 348 sendwindow(); 349 (void)signal(SIGWINCH, sigwinch); 350 } 351 dosigwinch = 1; 352} 353 354/* ARGSUSED */ 355static void 356catch_child(int signo __unused) 357{ 358 pid_t pid; 359 int status; 360 361 for (;;) { 362 pid = wait3(&status, WNOHANG|WUNTRACED, NULL); 363 if (pid == 0) 364 return; 365 /* if the child (reader) dies, just quit */ 366 if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 367 done(WTERMSIG(status) | WEXITSTATUS(status)); 368 } 369 /* NOTREACHED */ 370} 371 372/* 373 * writer: write to remote: 0 -> line. 374 * ~. terminate 375 * ~^Z suspend rlogin process. 376 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 377 */ 378static void 379writer(void) 380{ 381 int bol, local, n; 382 char c; 383 384 bol = 1; /* beginning of line */ 385 local = 0; 386 for (;;) { 387 n = read(STDIN_FILENO, &c, 1); 388 if (n <= 0) { 389 if (n < 0 && errno == EINTR) 390 continue; 391 break; 392 } 393 /* 394 * If we're at the beginning of the line and recognize a 395 * command character, then we echo locally. Otherwise, 396 * characters are echo'd remotely. If the command character 397 * is doubled, this acts as a force and local echo is 398 * suppressed. 399 */ 400 if (bol) { 401 bol = 0; 402 if (!noescape && c == escapechar) { 403 local = 1; 404 continue; 405 } 406 } else if (local) { 407 local = 0; 408 if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) { 409 echo(c); 410 break; 411 } 412 if (CCEQ(deftty.c_cc[VSUSP], c) || 413 CCEQ(deftty.c_cc[VDSUSP], c)) { 414 bol = 1; 415 echo(c); 416 stop(c); 417 continue; 418 } 419 if (c != escapechar) 420 (void)write(rem, &escapechar, 1); 421 } 422 423 if (write(rem, &c, 1) == 0) { 424 msg("line gone"); 425 break; 426 } 427 bol = CCEQ(deftty.c_cc[VKILL], c) || 428 CCEQ(deftty.c_cc[VEOF], c) || 429 CCEQ(deftty.c_cc[VINTR], c) || 430 CCEQ(deftty.c_cc[VSUSP], c) || 431 c == '\r' || c == '\n'; 432 } 433} 434 435static void 436echo(char c) 437{ 438 char *p; 439 char buf[8]; 440 441 p = buf; 442 c &= 0177; 443 *p++ = escapechar; 444 if (c < ' ') { 445 *p++ = '^'; 446 *p++ = c + '@'; 447 } else if (c == 0177) { 448 *p++ = '^'; 449 *p++ = '?'; 450 } else 451 *p++ = c; 452 *p++ = '\r'; 453 *p++ = '\n'; 454 (void)write(STDOUT_FILENO, buf, p - buf); 455} 456 457static void 458stop(char cmdc) 459{ 460 mode(0); 461 (void)signal(SIGCHLD, SIG_IGN); 462 (void)kill(CCEQ(deftty.c_cc[VSUSP], cmdc) ? 0 : getpid(), SIGTSTP); 463 (void)signal(SIGCHLD, catch_child); 464 mode(1); 465 sigwinch(0); /* check for size changes */ 466} 467 468/* ARGSUSED */ 469static void 470sigwinch(int signo __unused) 471{ 472 struct winsize ws; 473 474 if (dosigwinch && get_window_size(0, &ws) == 0 && 475 bcmp(&ws, &winsize, sizeof(ws))) { 476 winsize = ws; 477 sendwindow(); 478 } 479} 480 481/* 482 * Send the window size to the server via the magic escape 483 */ 484static void 485sendwindow(void) 486{ 487 struct winsize ws; 488 char obuf[4 + sizeof (struct winsize)]; 489 490 obuf[0] = 0377; 491 obuf[1] = 0377; 492 obuf[2] = 's'; 493 obuf[3] = 's'; 494 ws.ws_row = htons(winsize.ws_row); 495 ws.ws_col = htons(winsize.ws_col); 496 ws.ws_xpixel = htons(winsize.ws_xpixel); 497 ws.ws_ypixel = htons(winsize.ws_ypixel); 498 bcopy(&ws, obuf + 4, sizeof(ws)); 499 500 (void)write(rem, obuf, sizeof(obuf)); 501} 502 503/* 504 * reader: read from remote: line -> 1 505 */ 506#define READING 1 507#define WRITING 2 508 509static jmp_buf rcvtop; 510static int rcvcnt, rcvstate; 511static pid_t ppid; 512static char rcvbuf[8 * 1024]; 513 514/* ARGSUSED */ 515static void 516oob(int signo __unused) 517{ 518 struct termios tty; 519 int atmark, n, rcvd; 520 char waste[BUFSIZ], mark; 521 522 rcvd = 0; 523 while (recv(rem, &mark, 1, MSG_OOB) < 0) { 524 switch (errno) { 525 case EWOULDBLOCK: 526 /* 527 * Urgent data not here yet. It may not be possible 528 * to send it yet if we are blocked for output and 529 * our input buffer is full. 530 */ 531 if (rcvcnt < (int)sizeof(rcvbuf)) { 532 n = read(rem, rcvbuf + rcvcnt, 533 sizeof(rcvbuf) - rcvcnt); 534 if (n <= 0) 535 return; 536 rcvd += n; 537 } else { 538 n = read(rem, waste, sizeof(waste)); 539 if (n <= 0) 540 return; 541 } 542 continue; 543 default: 544 return; 545 } 546 } 547 if (mark & TIOCPKT_WINDOW) { 548 /* Let server know about window size changes */ 549 (void)kill(ppid, SIGUSR1); 550 } 551 if (!eight && (mark & TIOCPKT_NOSTOP)) { 552 (void)tcgetattr(0, &tty); 553 tty.c_iflag &= ~IXON; 554 (void)tcsetattr(0, TCSANOW, &tty); 555 } 556 if (!eight && (mark & TIOCPKT_DOSTOP)) { 557 (void)tcgetattr(0, &tty); 558 tty.c_iflag |= (deftty.c_iflag & IXON); 559 (void)tcsetattr(0, TCSANOW, &tty); 560 } 561 if (mark & TIOCPKT_FLUSHWRITE) { 562 (void)tcflush(1, TCIOFLUSH); 563 for (;;) { 564 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 565 warn("ioctl"); 566 break; 567 } 568 if (atmark) 569 break; 570 n = read(rem, waste, sizeof (waste)); 571 if (n <= 0) 572 break; 573 } 574 /* 575 * Don't want any pending data to be output, so clear the recv 576 * buffer. If we were hanging on a write when interrupted, 577 * don't want it to restart. If we were reading, restart 578 * anyway. 579 */ 580 rcvcnt = 0; 581 longjmp(rcvtop, 1); 582 } 583 584 /* oob does not do FLUSHREAD (alas!) */ 585 586 /* 587 * If we filled the receive buffer while a read was pending, longjmp 588 * to the top to restart appropriately. Don't abort a pending write, 589 * however, or we won't know how much was written. 590 */ 591 if (rcvd && rcvstate == READING) 592 longjmp(rcvtop, 1); 593} 594 595/* reader: read from remote: line -> 1 */ 596static int 597reader(int omask) 598{ 599 int n, remaining; 600 char *bufp; 601 pid_t pid; 602 603 pid = getpid(); 604 (void)signal(SIGTTOU, SIG_IGN); 605 (void)signal(SIGURG, oob); 606 (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */ 607 ppid = getppid(); 608 (void)fcntl(rem, F_SETOWN, pid); 609 (void)setjmp(rcvtop); 610 (void)sigsetmask(omask); 611 bufp = rcvbuf; 612 for (;;) { 613 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 614 rcvstate = WRITING; 615 n = write(STDOUT_FILENO, bufp, remaining); 616 if (n < 0) { 617 if (errno != EINTR) 618 return (-1); 619 continue; 620 } 621 bufp += n; 622 } 623 bufp = rcvbuf; 624 rcvcnt = 0; 625 rcvstate = READING; 626 627 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 628 if (rcvcnt == 0) 629 return (0); 630 if (rcvcnt < 0) { 631 if (errno == EINTR) 632 continue; 633 warn("read"); 634 return (-1); 635 } 636 } 637} 638 639static void 640mode(int f) 641{ 642 struct termios tty; 643 644 switch (f) { 645 case 0: 646 (void)tcsetattr(0, TCSANOW, &deftty); 647 break; 648 case 1: 649 (void)tcgetattr(0, &deftty); 650 tty = deftty; 651 /* This is loosely derived from sys/kern/tty_compat.c. */ 652 tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN); 653 tty.c_iflag &= ~ICRNL; 654 tty.c_oflag &= ~OPOST; 655 tty.c_cc[VMIN] = 1; 656 tty.c_cc[VTIME] = 0; 657 if (eight) { 658 tty.c_iflag &= IXOFF; 659 tty.c_cflag &= ~(CSIZE|PARENB); 660 tty.c_cflag |= CS8; 661 } 662 (void)tcsetattr(0, TCSANOW, &tty); 663 break; 664 default: 665 return; 666 } 667} 668 669/* ARGSUSED */ 670static void 671lostpeer(int signo __unused) 672{ 673 (void)signal(SIGPIPE, SIG_IGN); 674 msg("\007connection closed"); 675 done(1); 676} 677 678/* copy SIGURGs to the child process via SIGUSR1. */ 679/* ARGSUSED */ 680static void 681copytochild(int signo __unused) 682{ 683 (void)kill(child, SIGUSR1); 684} 685 686static void 687msg(const char *str) 688{ 689 (void)fprintf(stderr, "rlogin: %s\r\n", str); 690} 691 692static void 693usage(void) 694{ 695 (void)fprintf(stderr, 696 "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n", 697 "8DEd", " "); 698 exit(1); 699} 700 701static u_int 702getescape(const char *p) 703{ 704 long val; 705 size_t len; 706 707 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 708 return ((u_int)*p); 709 /* otherwise, \nnn */ 710 if (*p == '\\' && len >= 2 && len <= 4) { 711 val = strtol(++p, NULL, 8); 712 for (;;) { 713 if (!*++p) 714 return ((u_int)val); 715 if (*p < '0' || *p > '8') 716 break; 717 } 718 } 719 msg("illegal option value -- e"); 720 usage(); 721 /* NOTREACHED */ 722} 723