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