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