2 * Copyright (c) 1983, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * rlogin - remote login
39 RCSID("$Id: rlogin.c,v 1.67.2.2 2000/10/10 12:54:26 assar Exp $");
42 Key_schedule schedule;
43 int use_kerberos = 1, doencrypt;
44 char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
47 #define c2uc(x) ((unsigned char) x)
48 #define CCEQ__(val, c) (c == val ? val != c2uc(_POSIX_VDISABLE) : 0)
49 #define CCEQ(val, c) CCEQ__(c2uc(val), c2uc(c))
53 struct termios deftty;
56 char escapechar = '~';
58 struct winsize winsize;
61 char rcvbuf[8 * 1024];
77 } else if (c == 0177) {
84 write(STDOUT_FILENO, buf, p - buf);
94 tcsetattr(0, TCSANOW, &deftty);
97 tcgetattr(0, &deftty);
99 /* This is loosely derived from sys/compat/tty_compat.c. */
100 tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
101 tty.c_iflag &= ~ICRNL;
102 tty.c_oflag &= ~OPOST;
106 tty.c_iflag &= IXOFF;
107 tty.c_cflag &= ~(CSIZE|PARENB);
110 tcsetattr(0, TCSANOW, &tty);
124 /* make sure catch_child does not snap it up */
125 signal(SIGCHLD, SIG_DFL);
126 if (kill(child, SIGKILL) >= 0)
127 while ((w = wait(&wstatus)) > 0 && w != child);
140 pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
143 /* if the child (reader) dies, just quit */
144 if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
145 done(WTERMSIG(status) | WEXITSTATUS(status));
151 * There is a race in the SunOS5 rlogind. If the slave end has not yet
152 * been opened by the child when setting tty size the size is reset to
153 * zero when the child opens it. Therefore we send the window update
157 static int tty_kludge = 1;
159 /* Return the number of OOB bytes processed. */
164 int atmark, n, out, rcvd;
165 char waste[BUFSIZ], mark;
169 if (recv(rem, &mark, 1, MSG_OOB) < 0) {
172 if (mark & TIOCPKT_WINDOW) {
173 /* Let server know about window size changes */
174 kill(parent, SIGUSR1);
175 } else if (tty_kludge) {
176 /* Let server know about window size changes */
177 kill(parent, SIGUSR1);
180 if (!eight && (mark & TIOCPKT_NOSTOP)) {
182 tty.c_iflag &= ~IXON;
183 tcsetattr(0, TCSANOW, &tty);
185 if (!eight && (mark & TIOCPKT_DOSTOP)) {
187 tty.c_iflag |= (deftty.c_iflag & IXON);
188 tcsetattr(0, TCSANOW, &tty);
190 if (mark & TIOCPKT_FLUSHWRITE) {
192 tcflush(1, TCOFLUSH);
194 ioctl(1, TIOCFLUSH, (char *)&out);
197 if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
203 n = read(rem, waste, sizeof (waste));
208 * Don't want any pending data to be output, so clear the recv
209 * buffer. If we were hanging on a write when interrupted,
210 * don't want it to restart. If we were reading, restart
216 /* oob does not do FLUSHREAD (alas!) */
220 /* reader: read from remote: line -> 1 */
230 fd_set readfds, exceptfds;
231 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
232 n = write(STDOUT_FILENO, bufp, remaining);
244 if (rem >= FD_SETSIZE)
245 errx (1, "fd too large");
246 FD_SET (rem, &readfds);
247 FD_ZERO (&exceptfds);
249 FD_SET (rem, &exceptfds);
250 if (select(rem+1, &readfds, 0, &exceptfds, 0) == -1) {
252 continue; /* Got signal */
254 errx(1, "select failed mysteriously");
257 if (!FD_ISSET(rem, &exceptfds) && !FD_ISSET(rem, &readfds)) {
258 warnx("select: nothing to read?");
262 if (FD_ISSET(rem, &exceptfds)) {
263 int foo = oob_real ();
265 continue; /* First check if there is more OOB data. */
270 if (!FD_ISSET(rem, &readfds))
271 continue; /* Nothing to read. */
276 rcvcnt = des_enc_read(rem, rcvbuf,
278 schedule, &cred.session);
281 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
294 * Send the window size to the server via the magic escape
299 char obuf[4 + 4 * sizeof (u_int16_t)];
302 p = (u_int16_t *)(obuf + 4);
307 *p++ = htons(winsize.ws_row);
308 *p++ = htons(winsize.ws_col);
309 #ifdef HAVE_WS_XPIXEL
310 *p++ = htons(winsize.ws_xpixel);
314 #ifdef HAVE_WS_YPIXEL
315 *p++ = htons(winsize.ws_ypixel);
322 des_enc_write(rem, obuf, sizeof(obuf), schedule,
326 write(rem, obuf, sizeof(obuf));
335 if (get_window_size(0, &ws) == 0 &&
336 memcmp(&ws, &winsize, sizeof(ws))) {
346 signal(SIGCHLD, SIG_IGN);
347 kill(all ? 0 : getpid(), SIGTSTP);
348 signal(SIGCHLD, catch_child);
351 kill(SIGWINCH, getpid()); /* check for size changes, if caught */
356 * writer: write to remote: 0 -> line.
358 * ~^Z suspend rlogin process.
359 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
367 bol = 1; /* beginning of line */
370 n = read(STDIN_FILENO, &c, 1);
372 if (n < 0 && errno == EINTR)
377 * If we're at the beginning of the line and recognize a
378 * command character, then we echo locally. Otherwise,
379 * characters are echo'd remotely. If the command character
380 * is doubled, this acts as a force and local echo is
385 if (!noescape && c == escapechar) {
391 if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
395 if (CCEQ(deftty.c_cc[VSUSP], c)) {
402 /* Is VDSUSP called something else on Linux?
403 * Perhaps VDELAY is a better thing? */
404 if (CCEQ(deftty.c_cc[VDSUSP], c)) {
411 if (c != escapechar) {
414 des_enc_write(rem, &escapechar,1, schedule, &cred.session);
417 write(rem, &escapechar, 1);
423 if (write(rem, &c, 1) == 0) {
425 if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) {
431 if (write(rem, &c, 1) == 0) {
435 bol = CCEQ(deftty.c_cc[VKILL], c) ||
436 CCEQ(deftty.c_cc[VEOF], c) ||
437 CCEQ(deftty.c_cc[VINTR], c) ||
438 CCEQ(deftty.c_cc[VSUSP], c) ||
439 c == '\r' || c == '\n';
447 signal(SIGPIPE, SIG_IGN);
448 warnx("\aconnection closed.\r");
453 * This is called in the parent when the reader process gets the
454 * out-of-band (urgent) request to turn on the window-changing
455 * protocol. It is signalled from the child(reader).
462 * Now we now daemon supports winsize hack,
466 signal(SIGWINCH, sigwinch); /* so we start to support it */
474 signal(SIGINT, SIG_IGN);
475 signal(SIGHUP, SIG_IGN);
476 signal(SIGQUIT, SIG_IGN);
478 signal(SIGCHLD, catch_child);
481 * Child sends parent this signal for window size hack.
483 signal(SIGUSR1, sigusr1);
485 signal(SIGPIPE, lostpeer);
495 signal(SIGCHLD, SIG_IGN);
496 signal(SIGTTOU, SIG_IGN);
498 errx(1, "connection closed.\r");
500 errx(1, "\aconnection closed.\r");
504 warnx("closed connection.\r");
512 "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
513 "8DEKLdx", " [-k realm] ");
523 if ((len = strlen(p)) == 1) /* use any single char, including '\' */
525 /* otherwise, \nnn */
526 if (*p == '\\' && len >= 2 && len <= 4) {
527 val = strtol(++p, NULL, 8);
531 if (*p < '0' || *p > '8')
535 warnx("illegal option value -- e");
541 main(int argc, char **argv)
544 int sv_port, user_port = 0;
545 int argoff, ch, dflag, Dflag, one, uid;
546 char *host, *user, term[1024];
548 argoff = dflag = Dflag = 0;
552 set_progname(argv[0]);
554 /* handle "rlogin host flags" */
555 if (argc > 2 && argv[1][0] != '-') {
560 #define OPTIONS "8DEKLde:k:l:xp:"
561 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
580 escapechar = getescape(optarg);
583 dest_realm = dst_realm_buf;
584 strlcpy(dest_realm, optarg, REALM_SZ);
595 user_port = strtol (optarg, &endptr, 0);
596 if (user_port == 0 && optarg == endptr)
597 errx (1, "Bad port `%s'", optarg);
598 user_port = htons(user_port);
607 /* if haven't gotten a host yet, do so */
608 if (!host && !(host = argv[optind++]))
614 if (!(pw = k_getpwuid(uid = getuid())))
615 errx(1, "unknown user id.");
622 sv_port = get_login_port(use_kerberos, doencrypt);
625 char *p = getenv("TERM");
632 if (tcgetattr(0, &tty) == 0
633 && (i = speed_t2int (cfgetospeed(&tty))) > 0)
634 snprintf (term, sizeof(term),
638 snprintf (term, sizeof(term),
643 get_window_size(0, &winsize);
646 paranoid_setuid(getuid());
649 if (dest_realm == NULL)
650 dest_realm = krb_realmofhost(host);
653 rem = krcmd_mutual(&host, sv_port, user, term, 0,
654 dest_realm, &cred, schedule);
656 rem = krcmd(&host, sv_port, user, term, 0,
662 if (errno == ECONNREFUSED)
663 warning("remote host doesn't support Kerberos");
665 warning("can't provide Kerberos auth data");
666 newargv = malloc((argc + 2) * sizeof(*newargv));
669 newargv[0] = argv[0];
671 for(i = 1; i < argc; ++i)
672 newargv[i + 1] = argv[i];
673 newargv[argc + 1] = NULL;
674 execv(_PATH_RLOGIN, newargv);
678 errx(1, "the -x flag requires Kerberos authentication.");
680 errx(1, "not installed setuid root, "
681 "only root may use non kerberized rlogin");
682 rem = rcmd(&host, sv_port, pw->pw_name, user, term, 0);
688 #ifdef HAVE_SETSOCKOPT
691 setsockopt(rem, SOL_SOCKET, SO_DEBUG, (void *)&one,
697 setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
699 warn("setsockopt(TCP_NODELAY)");
702 one = IPTOS_LOWDELAY;
703 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (void *)&one, sizeof(int)) < 0)
704 warn("setsockopt(IP_TOS)");
706 #endif /* HAVE_SETSOCKOPT */
708 paranoid_setuid(uid);