rlogin.c revision 105268
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 105268 2002-10-16 16:09:05Z markm $"); 55 56/* 57 * rlogin - remote login 58 */ 59 60#include <sys/param.h> 61#include <sys/socket.h> 62#include <sys/time.h> 63#include <sys/resource.h> 64#include <sys/wait.h> 65 66#include <netinet/in.h> 67#include <netinet/in_systm.h> 68#include <netinet/ip.h> 69#include <netinet/tcp.h> 70 71#include <err.h> 72#include <errno.h> 73#include <fcntl.h> 74#include <libutil.h> 75#include <netdb.h> 76#include <paths.h> 77#include <pwd.h> 78#include <setjmp.h> 79#include <sgtty.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 95int eight, litout, rem; 96int family = PF_UNSPEC; 97 98int noescape; 99u_char escapechar = '~'; 100 101const char *speeds[] = { 102 "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", 103 "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200" 104#define MAX_SPEED_LENGTH (sizeof("115200") - 1) 105}; 106 107#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 108struct winsize winsize; 109 110void catch_child(int); 111void copytochild(int); 112void doit(long) __dead2; 113void done(int) __dead2; 114void echo(char); 115u_int getescape(const char *); 116void lostpeer(int); 117void mode(int); 118void msg(const char *); 119void oob(int); 120int reader(int); 121void sendwindow(void); 122void setsignal(int); 123void sigwinch(int); 124void stop(char); 125void usage(void) __dead2; 126void writer(void); 127void writeroob(int); 128 129int 130main(int argc, char *argv[]) 131{ 132 struct passwd *pw; 133 struct servent *sp; 134 struct sgttyb ttyb; 135 long omask; 136 int argoff, ch, dflag, Dflag, one; 137 uid_t uid; 138 char *host, *localname, *p, *user, term[1024]; 139 struct sockaddr_storage ss; 140 int sslen; 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 "468DEKLde: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 'L': 181 litout = 1; 182 break; 183 case 'd': 184 dflag = 1; 185 break; 186 case 'e': 187 noescape = 0; 188 escapechar = getescape(optarg); 189 break; 190 case 'i': 191 if (getuid() != 0) 192 errx(1, "-i user: permission denied"); 193 localname = optarg; 194 break; 195 case 'l': 196 user = optarg; 197 break; 198 case '?': 199 default: 200 usage(); 201 } 202 optind += argoff; 203 204 /* if haven't gotten a host yet, do so */ 205 if (!host && !(host = argv[optind++])) 206 usage(); 207 208 if (argv[optind]) 209 usage(); 210 211 if (!(pw = getpwuid(uid = getuid()))) 212 errx(1, "unknown user id"); 213 if (!user) 214 user = pw->pw_name; 215 if (!localname) 216 localname = pw->pw_name; 217 218 sp = NULL; 219 sp = getservbyname("login", "tcp"); 220 if (sp == NULL) 221 errx(1, "login/tcp: unknown service"); 222 223#define MAX_TERM_LENGTH (sizeof(term) - 1 - MAX_SPEED_LENGTH - 1) 224 225 (void)strncpy(term, (p = getenv("TERM")) ? p : "network", 226 MAX_TERM_LENGTH); 227 term[MAX_TERM_LENGTH] = '\0'; 228 if (ioctl(0, TIOCGETP, &ttyb) == 0) { 229 (void)strcat(term, "/"); 230 (void)strcat(term, speeds[(int)ttyb.sg_ospeed]); 231 } 232 233 (void)get_window_size(0, &winsize); 234 235 (void)signal(SIGPIPE, lostpeer); 236 /* will use SIGUSR1 for window size hack, so hold it off */ 237 omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 238 /* 239 * We set SIGURG and SIGUSR1 below so that an 240 * incoming signal will be held pending rather than being 241 * discarded. Note that these routines will be ready to get 242 * a signal by the time that they are unblocked below. 243 */ 244 (void)signal(SIGURG, copytochild); 245 (void)signal(SIGUSR1, writeroob); 246 247 rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family); 248 249 if (rem < 0) 250 exit(1); 251 252 if (dflag && 253 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 254 warn("setsockopt"); 255 if (Dflag && 256 setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 257 warn("setsockopt NODELAY (ignored)"); 258 259 sslen = sizeof(ss); 260 one = IPTOS_LOWDELAY; 261 if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 && 262 ss.ss_family == AF_INET) { 263 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 264 sizeof(int)) < 0) 265 warn("setsockopt TOS (ignored)"); 266 } else 267 if (ss.ss_family == AF_INET) 268 warn("setsockopt getsockname failed"); 269 270 (void)setuid(uid); 271 doit(omask); 272 /*NOTREACHED*/ 273} 274 275int child, defflags, deflflags, tabflag; 276char deferase, defkill; 277struct tchars deftc; 278struct ltchars defltc; 279struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 280struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 281 282void 283doit(long omask) 284{ 285 struct sgttyb sb; 286 287 (void)ioctl(0, TIOCGETP, (char *)&sb); 288 defflags = sb.sg_flags; 289 tabflag = defflags & TBDELAY; 290 defflags &= ECHO | CRMOD; 291 deferase = sb.sg_erase; 292 defkill = sb.sg_kill; 293 (void)ioctl(0, TIOCLGET, &deflflags); 294 (void)ioctl(0, TIOCGETC, &deftc); 295 notc.t_startc = deftc.t_startc; 296 notc.t_stopc = deftc.t_stopc; 297 (void)ioctl(0, TIOCGLTC, &defltc); 298 (void)signal(SIGINT, SIG_IGN); 299 setsignal(SIGHUP); 300 setsignal(SIGQUIT); 301 child = fork(); 302 if (child == -1) { 303 warn("fork"); 304 done(1); 305 } 306 if (child == 0) { 307 mode(1); 308 if (reader(omask) == 0) { 309 msg("connection closed"); 310 exit(0); 311 } 312 sleep(1); 313 msg("\007connection closed"); 314 exit(1); 315 } 316 317 /* 318 * We may still own the socket, and may have a pending SIGURG (or might 319 * receive one soon) that we really want to send to the reader. When 320 * one of these comes in, the trap copytochild simply copies such 321 * signals to the child. We can now unblock SIGURG and SIGUSR1 322 * that were set above. 323 */ 324 (void)sigsetmask(omask); 325 (void)signal(SIGCHLD, catch_child); 326 writer(); 327 msg("closed connection"); 328 done(0); 329} 330 331/* trap a signal, unless it is being ignored. */ 332void 333setsignal(int sig) 334{ 335 int omask = sigblock(sigmask(sig)); 336 337 if (signal(sig, exit) == SIG_IGN) 338 (void)signal(sig, SIG_IGN); 339 (void)sigsetmask(omask); 340} 341 342void 343done(int status) 344{ 345 int w, wstatus; 346 347 mode(0); 348 if (child > 0) { 349 /* make sure catch_child does not snap it up */ 350 (void)signal(SIGCHLD, SIG_DFL); 351 if (kill(child, SIGKILL) >= 0) 352 while ((w = wait(&wstatus)) > 0 && w != child); 353 } 354 exit(status); 355} 356 357int dosigwinch; 358 359/* 360 * This is called when the reader process gets the out-of-band (urgent) 361 * request to turn on the window-changing protocol. 362 */ 363/* ARGSUSED */ 364void 365writeroob(int signo __unused) 366{ 367 if (dosigwinch == 0) { 368 sendwindow(); 369 (void)signal(SIGWINCH, sigwinch); 370 } 371 dosigwinch = 1; 372} 373 374/* ARGSUSED */ 375void 376catch_child(int signo __unused) 377{ 378 pid_t pid; 379 int status; 380 381 for (;;) { 382 pid = wait3(&status, WNOHANG|WUNTRACED, NULL); 383 if (pid == 0) 384 return; 385 /* if the child (reader) dies, just quit */ 386 if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 387 done(WTERMSIG(status) | WEXITSTATUS(status)); 388 } 389 /* NOTREACHED */ 390} 391 392/* 393 * writer: write to remote: 0 -> line. 394 * ~. terminate 395 * ~^Z suspend rlogin process. 396 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 397 */ 398void 399writer(void) 400{ 401 int bol, local, n; 402 char c; 403 404 bol = 1; /* beginning of line */ 405 local = 0; 406 for (;;) { 407 n = read(STDIN_FILENO, &c, 1); 408 if (n <= 0) { 409 if (n < 0 && errno == EINTR) 410 continue; 411 break; 412 } 413 /* 414 * If we're at the beginning of the line and recognize a 415 * command character, then we echo locally. Otherwise, 416 * characters are echo'd remotely. If the command character 417 * is doubled, this acts as a force and local echo is 418 * suppressed. 419 */ 420 if (bol) { 421 bol = 0; 422 if (!noescape && c == escapechar) { 423 local = 1; 424 continue; 425 } 426 } else if (local) { 427 local = 0; 428 if (c == '.' || c == deftc.t_eofc) { 429 echo(c); 430 break; 431 } 432 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 433 bol = 1; 434 echo(c); 435 stop(c); 436 continue; 437 } 438 if (c != escapechar) 439 (void)write(rem, &escapechar, 1); 440 } 441 442 if (write(rem, &c, 1) == 0) { 443 msg("line gone"); 444 break; 445 } 446 bol = c == defkill || c == deftc.t_eofc || 447 c == deftc.t_intrc || c == defltc.t_suspc || 448 c == '\r' || c == '\n'; 449 } 450} 451 452void 453echo(char c) 454{ 455 char *p; 456 char buf[8]; 457 458 p = buf; 459 c &= 0177; 460 *p++ = escapechar; 461 if (c < ' ') { 462 *p++ = '^'; 463 *p++ = c + '@'; 464 } else if (c == 0177) { 465 *p++ = '^'; 466 *p++ = '?'; 467 } else 468 *p++ = c; 469 *p++ = '\r'; 470 *p++ = '\n'; 471 (void)write(STDOUT_FILENO, buf, p - buf); 472} 473 474void 475stop(char cmdc) 476{ 477 mode(0); 478 (void)signal(SIGCHLD, SIG_IGN); 479 (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 480 (void)signal(SIGCHLD, catch_child); 481 mode(1); 482 sigwinch(0); /* check for size changes */ 483} 484 485/* ARGSUSED */ 486void 487sigwinch(int signo __unused) 488{ 489 struct winsize ws; 490 491 if (dosigwinch && get_window_size(0, &ws) == 0 && 492 bcmp(&ws, &winsize, sizeof(ws))) { 493 winsize = ws; 494 sendwindow(); 495 } 496} 497 498/* 499 * Send the window size to the server via the magic escape 500 */ 501void 502sendwindow(void) 503{ 504 struct winsize *wp; 505 char obuf[4 + sizeof (struct winsize)]; 506 507 wp = (struct winsize *)(obuf+4); 508 obuf[0] = 0377; 509 obuf[1] = 0377; 510 obuf[2] = 's'; 511 obuf[3] = 's'; 512 wp->ws_row = htons(winsize.ws_row); 513 wp->ws_col = htons(winsize.ws_col); 514 wp->ws_xpixel = htons(winsize.ws_xpixel); 515 wp->ws_ypixel = htons(winsize.ws_ypixel); 516 517 (void)write(rem, obuf, sizeof(obuf)); 518} 519 520/* 521 * reader: read from remote: line -> 1 522 */ 523#define READING 1 524#define WRITING 2 525 526jmp_buf rcvtop; 527int rcvcnt, rcvstate; 528pid_t ppid; 529char rcvbuf[8 * 1024]; 530 531/* ARGSUSED */ 532void 533oob(int signo __unused) 534{ 535 struct sgttyb sb; 536 int atmark, n, out, rcvd; 537 char waste[BUFSIZ], mark; 538 539 out = O_RDWR; 540 rcvd = 0; 541 while (recv(rem, &mark, 1, MSG_OOB) < 0) { 542 switch (errno) { 543 case EWOULDBLOCK: 544 /* 545 * Urgent data not here yet. It may not be possible 546 * to send it yet if we are blocked for output and 547 * our input buffer is full. 548 */ 549 if (rcvcnt < (int)sizeof(rcvbuf)) { 550 n = read(rem, rcvbuf + rcvcnt, 551 sizeof(rcvbuf) - rcvcnt); 552 if (n <= 0) 553 return; 554 rcvd += n; 555 } else { 556 n = read(rem, waste, sizeof(waste)); 557 if (n <= 0) 558 return; 559 } 560 continue; 561 default: 562 return; 563 } 564 } 565 if (mark & TIOCPKT_WINDOW) { 566 /* Let server know about window size changes */ 567 (void)kill(ppid, SIGUSR1); 568 } 569 if (!eight && (mark & TIOCPKT_NOSTOP)) { 570 (void)ioctl(0, TIOCGETP, (char *)&sb); 571 sb.sg_flags &= ~CBREAK; 572 sb.sg_flags |= RAW; 573 (void)ioctl(0, TIOCSETN, (char *)&sb); 574 notc.t_stopc = -1; 575 notc.t_startc = -1; 576 (void)ioctl(0, TIOCSETC, (char *)¬c); 577 } 578 if (!eight && (mark & TIOCPKT_DOSTOP)) { 579 (void)ioctl(0, TIOCGETP, (char *)&sb); 580 sb.sg_flags &= ~RAW; 581 sb.sg_flags |= CBREAK; 582 (void)ioctl(0, TIOCSETN, (char *)&sb); 583 notc.t_stopc = deftc.t_stopc; 584 notc.t_startc = deftc.t_startc; 585 (void)ioctl(0, TIOCSETC, (char *)¬c); 586 } 587 if (mark & TIOCPKT_FLUSHWRITE) { 588 (void)ioctl(1, TIOCFLUSH, (char *)&out); 589 for (;;) { 590 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 591 warn("ioctl"); 592 break; 593 } 594 if (atmark) 595 break; 596 n = read(rem, waste, sizeof (waste)); 597 if (n <= 0) 598 break; 599 } 600 /* 601 * Don't want any pending data to be output, so clear the recv 602 * buffer. If we were hanging on a write when interrupted, 603 * don't want it to restart. If we were reading, restart 604 * anyway. 605 */ 606 rcvcnt = 0; 607 longjmp(rcvtop, 1); 608 } 609 610 /* oob does not do FLUSHREAD (alas!) */ 611 612 /* 613 * If we filled the receive buffer while a read was pending, longjmp 614 * to the top to restart appropriately. Don't abort a pending write, 615 * however, or we won't know how much was written. 616 */ 617 if (rcvd && rcvstate == READING) 618 longjmp(rcvtop, 1); 619} 620 621/* reader: read from remote: line -> 1 */ 622int 623reader(int omask) 624{ 625 int n, remaining; 626 char *bufp; 627 pid_t pid; 628 629 pid = getpid(); 630 (void)signal(SIGTTOU, SIG_IGN); 631 (void)signal(SIGURG, oob); 632 (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */ 633 ppid = getppid(); 634 (void)fcntl(rem, F_SETOWN, pid); 635 (void)setjmp(rcvtop); 636 (void)sigsetmask(omask); 637 bufp = rcvbuf; 638 for (;;) { 639 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 640 rcvstate = WRITING; 641 n = write(STDOUT_FILENO, bufp, remaining); 642 if (n < 0) { 643 if (errno != EINTR) 644 return (-1); 645 continue; 646 } 647 bufp += n; 648 } 649 bufp = rcvbuf; 650 rcvcnt = 0; 651 rcvstate = READING; 652 653 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 654 if (rcvcnt == 0) 655 return (0); 656 if (rcvcnt < 0) { 657 if (errno == EINTR) 658 continue; 659 warn("read"); 660 return (-1); 661 } 662 } 663} 664 665void 666mode(int f) 667{ 668 struct ltchars *ltc; 669 struct sgttyb sb; 670 struct tchars *tc; 671 int lflags; 672 673 (void)ioctl(0, TIOCGETP, (char *)&sb); 674 (void)ioctl(0, TIOCLGET, (char *)&lflags); 675 switch(f) { 676 case 0: 677 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 678 sb.sg_flags |= defflags|tabflag; 679 tc = &deftc; 680 ltc = &defltc; 681 sb.sg_kill = defkill; 682 sb.sg_erase = deferase; 683 lflags = deflflags; 684 break; 685 case 1: 686 sb.sg_flags |= (eight ? RAW : CBREAK); 687 sb.sg_flags &= ~defflags; 688 /* preserve tab delays, but turn off XTABS */ 689 if ((sb.sg_flags & TBDELAY) == XTABS) 690 sb.sg_flags &= ~TBDELAY; 691 tc = ¬c; 692 ltc = &noltc; 693 sb.sg_kill = sb.sg_erase = -1; 694 if (litout) 695 lflags |= LLITOUT; 696 break; 697 default: 698 return; 699 } 700 (void)ioctl(0, TIOCSLTC, (char *)ltc); 701 (void)ioctl(0, TIOCSETC, (char *)tc); 702 (void)ioctl(0, TIOCSETN, (char *)&sb); 703 (void)ioctl(0, TIOCLSET, (char *)&lflags); 704} 705 706/* ARGSUSED */ 707void 708lostpeer(int signo __unused) 709{ 710 (void)signal(SIGPIPE, SIG_IGN); 711 msg("\007connection closed"); 712 done(1); 713} 714 715/* copy SIGURGs to the child process via SIGUSR1. */ 716/* ARGSUSED */ 717void 718copytochild(int signo __unused) 719{ 720 (void)kill(child, SIGUSR1); 721} 722 723void 724msg(const char *str) 725{ 726 (void)fprintf(stderr, "rlogin: %s\r\n", str); 727} 728 729void 730usage(void) 731{ 732 (void)fprintf(stderr, 733 "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n", 734 "8DEKLd", " "); 735 exit(1); 736} 737 738u_int 739getescape(const char *p) 740{ 741 long val; 742 size_t len; 743 744 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 745 return ((u_int)*p); 746 /* otherwise, \nnn */ 747 if (*p == '\\' && len >= 2 && len <= 4) { 748 val = strtol(++p, NULL, 8); 749 for (;;) { 750 if (!*++p) 751 return ((u_int)val); 752 if (*p < '0' || *p > '8') 753 break; 754 } 755 } 756 msg("illegal option value -- e"); 757 usage(); 758 /* NOTREACHED */ 759} 760