diff options
Diffstat (limited to 'crypto/kerberosIV/appl/bsd/rlogin.c')
-rw-r--r-- | crypto/kerberosIV/appl/bsd/rlogin.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/crypto/kerberosIV/appl/bsd/rlogin.c b/crypto/kerberosIV/appl/bsd/rlogin.c new file mode 100644 index 0000000..d057ede --- /dev/null +++ b/crypto/kerberosIV/appl/bsd/rlogin.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * rlogin - remote login + */ +#include "bsd_locl.h" + +RCSID("$Id: rlogin.c,v 1.67 1999/11/13 06:13:02 assar Exp $"); + +CREDENTIALS cred; +Key_schedule schedule; +int use_kerberos = 1, doencrypt; +char dst_realm_buf[REALM_SZ], *dest_realm = NULL; + +#ifndef CCEQ +#define c2uc(x) ((unsigned char) x) +#define CCEQ__(val, c) (c == val ? val != c2uc(_POSIX_VDISABLE) : 0) +#define CCEQ(val, c) CCEQ__(c2uc(val), c2uc(c)) +#endif + +int eight, rem; +struct termios deftty; + +int noescape; +char escapechar = '~'; + +struct winsize winsize; + +int parent, rcvcnt; +char rcvbuf[8 * 1024]; + +int child; + +static void +echo(char c) +{ + char *p; + char buf[8]; + + p = buf; + c &= 0177; + *p++ = escapechar; + if (c < ' ') { + *p++ = '^'; + *p++ = c + '@'; + } else if (c == 0177) { + *p++ = '^'; + *p++ = '?'; + } else + *p++ = c; + *p++ = '\r'; + *p++ = '\n'; + write(STDOUT_FILENO, buf, p - buf); +} + +static void +mode(int f) +{ + struct termios tty; + + switch (f) { + case 0: + tcsetattr(0, TCSANOW, &deftty); + break; + case 1: + tcgetattr(0, &deftty); + tty = deftty; + /* This is loosely derived from sys/compat/tty_compat.c. */ + tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN); + tty.c_iflag &= ~ICRNL; + tty.c_oflag &= ~OPOST; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + if (eight) { + tty.c_iflag &= IXOFF; + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + } + tcsetattr(0, TCSANOW, &tty); + break; + default: + return; + } +} + +static void +done(int status) +{ + int w, wstatus; + + mode(0); + if (child > 0) { + /* make sure catch_child does not snap it up */ + signal(SIGCHLD, SIG_DFL); + if (kill(child, SIGKILL) >= 0) + while ((w = wait(&wstatus)) > 0 && w != child); + } + exit(status); +} + +static +RETSIGTYPE +catch_child(int foo) +{ + int status; + int pid; + + for (;;) { + pid = waitpid(-1, &status, WNOHANG|WUNTRACED); + if (pid == 0) + return; + /* if the child (reader) dies, just quit */ + if (pid < 0 || (pid == child && !WIFSTOPPED(status))) + done(WTERMSIG(status) | WEXITSTATUS(status)); + } + /* NOTREACHED */ +} + +/* + * There is a race in the SunOS5 rlogind. If the slave end has not yet + * been opened by the child when setting tty size the size is reset to + * zero when the child opens it. Therefore we send the window update + * twice. + */ + +static int tty_kludge = 1; + +/* Return the number of OOB bytes processed. */ +static int +oob_real(void) +{ + struct termios tty; + int atmark, n, out, rcvd; + char waste[BUFSIZ], mark; + + out = O_RDWR; + rcvd = 0; + if (recv(rem, &mark, 1, MSG_OOB) < 0) { + return -1; + } + if (mark & TIOCPKT_WINDOW) { + /* Let server know about window size changes */ + kill(parent, SIGUSR1); + } else if (tty_kludge) { + /* Let server know about window size changes */ + kill(parent, SIGUSR1); + tty_kludge = 0; + } + if (!eight && (mark & TIOCPKT_NOSTOP)) { + tcgetattr(0, &tty); + tty.c_iflag &= ~IXON; + tcsetattr(0, TCSANOW, &tty); + } + if (!eight && (mark & TIOCPKT_DOSTOP)) { + tcgetattr(0, &tty); + tty.c_iflag |= (deftty.c_iflag & IXON); + tcsetattr(0, TCSANOW, &tty); + } + if (mark & TIOCPKT_FLUSHWRITE) { +#ifdef TCOFLUSH + tcflush(1, TCOFLUSH); +#else + ioctl(1, TIOCFLUSH, (char *)&out); +#endif + for (;;) { + if (ioctl(rem, SIOCATMARK, &atmark) < 0) { + warn("ioctl"); + break; + } + if (atmark) + break; + n = read(rem, waste, sizeof (waste)); + if (n <= 0) + break; + } + /* + * Don't want any pending data to be output, so clear the recv + * buffer. If we were hanging on a write when interrupted, + * don't want it to restart. If we were reading, restart + * anyway. + */ + rcvcnt = 0; + } + + /* oob does not do FLUSHREAD (alas!) */ + return 1; +} + +/* reader: read from remote: line -> 1 */ +static int +reader(void) +{ + int n, remaining; + char *bufp; + int kludgep = 1; + + bufp = rcvbuf; + for (;;) { + fd_set readfds, exceptfds; + while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { + n = write(STDOUT_FILENO, bufp, remaining); + if (n < 0) { + if (errno != EINTR) + return (-1); + continue; + } + bufp += n; + } + bufp = rcvbuf; + rcvcnt = 0; + + FD_ZERO (&readfds); + FD_SET (rem, &readfds); + FD_ZERO (&exceptfds); + if (kludgep) + FD_SET (rem, &exceptfds); + if (select(rem+1, &readfds, 0, &exceptfds, 0) == -1) { + if (errno == EINTR) + continue; /* Got signal */ + else + errx(1, "select failed mysteriously"); + } + + if (!FD_ISSET(rem, &exceptfds) && !FD_ISSET(rem, &readfds)) { + warnx("select: nothing to read?"); + continue; + } + + if (FD_ISSET(rem, &exceptfds)) { + int foo = oob_real (); + if (foo >= 1) + continue; /* First check if there is more OOB data. */ + else if (foo < 0) + kludgep = 0; + } + + if (!FD_ISSET(rem, &readfds)) + continue; /* Nothing to read. */ + + kludgep = 1; +#ifndef NOENCRYPTION + if (doencrypt) + rcvcnt = des_enc_read(rem, rcvbuf, + sizeof(rcvbuf), + schedule, &cred.session); + else +#endif + rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); + if (rcvcnt == 0) + return (0); + if (rcvcnt < 0) { + if (errno == EINTR) + continue; + warn("read"); + return (-1); + } + } +} + +/* + * Send the window size to the server via the magic escape + */ +static void +sendwindow(void) +{ + char obuf[4 + 4 * sizeof (u_int16_t)]; + unsigned short *p; + + p = (u_int16_t *)(obuf + 4); + obuf[0] = 0377; + obuf[1] = 0377; + obuf[2] = 's'; + obuf[3] = 's'; + *p++ = htons(winsize.ws_row); + *p++ = htons(winsize.ws_col); +#ifdef HAVE_WS_XPIXEL + *p++ = htons(winsize.ws_xpixel); +#else + *p++ = htons(0); +#endif +#ifdef HAVE_WS_YPIXEL + *p++ = htons(winsize.ws_ypixel); +#else + *p++ = htons(0); +#endif + +#ifndef NOENCRYPTION + if(doencrypt) + des_enc_write(rem, obuf, sizeof(obuf), schedule, + &cred.session); + else +#endif + write(rem, obuf, sizeof(obuf)); +} + +static +RETSIGTYPE +sigwinch(int foo) +{ + struct winsize ws; + + if (get_window_size(0, &ws) == 0 && + memcmp(&ws, &winsize, sizeof(ws))) { + winsize = ws; + sendwindow(); + } +} + +static void +stop(int all) +{ + mode(0); + signal(SIGCHLD, SIG_IGN); + kill(all ? 0 : getpid(), SIGTSTP); + signal(SIGCHLD, catch_child); + mode(1); +#ifdef SIGWINCH + kill(SIGWINCH, getpid()); /* check for size changes, if caught */ +#endif +} + +/* + * writer: write to remote: 0 -> line. + * ~. terminate + * ~^Z suspend rlogin process. + * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. + */ +static void +writer(void) +{ + int bol, local, n; + char c; + + bol = 1; /* beginning of line */ + local = 0; + for (;;) { + n = read(STDIN_FILENO, &c, 1); + if (n <= 0) { + if (n < 0 && errno == EINTR) + continue; + break; + } + /* + * If we're at the beginning of the line and recognize a + * command character, then we echo locally. Otherwise, + * characters are echo'd remotely. If the command character + * is doubled, this acts as a force and local echo is + * suppressed. + */ + if (bol) { + bol = 0; + if (!noescape && c == escapechar) { + local = 1; + continue; + } + } else if (local) { + local = 0; + if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) { + echo(c); + break; + } + if (CCEQ(deftty.c_cc[VSUSP], c)) { + bol = 1; + echo(c); + stop(1); + continue; + } +#ifdef VDSUSP + /* Is VDSUSP called something else on Linux? + * Perhaps VDELAY is a better thing? */ + if (CCEQ(deftty.c_cc[VDSUSP], c)) { + bol = 1; + echo(c); + stop(0); + continue; + } +#endif /* VDSUSP */ + if (c != escapechar) { +#ifndef NOENCRYPTION + if (doencrypt) + des_enc_write(rem, &escapechar,1, schedule, &cred.session); + else +#endif + write(rem, &escapechar, 1); + } + } + + if (doencrypt) { +#ifdef NOENCRYPTION + if (write(rem, &c, 1) == 0) { +#else + if (des_enc_write(rem, &c, 1, schedule, &cred.session) == 0) { +#endif + warnx("line gone"); + break; + } + } else + if (write(rem, &c, 1) == 0) { + warnx("line gone"); + break; + } + bol = CCEQ(deftty.c_cc[VKILL], c) || + CCEQ(deftty.c_cc[VEOF], c) || + CCEQ(deftty.c_cc[VINTR], c) || + CCEQ(deftty.c_cc[VSUSP], c) || + c == '\r' || c == '\n'; + } +} + +static +RETSIGTYPE +lostpeer(int foo) +{ + signal(SIGPIPE, SIG_IGN); + warnx("\aconnection closed.\r"); + done(1); +} + +/* + * This is called in the parent when the reader process gets the + * out-of-band (urgent) request to turn on the window-changing + * protocol. It is signalled from the child(reader). + */ +static +RETSIGTYPE +sigusr1(int foo) +{ + /* + * Now we now daemon supports winsize hack, + */ + sendwindow(); +#ifdef SIGWINCH + signal(SIGWINCH, sigwinch); /* so we start to support it */ +#endif + SIGRETURN(0); +} + +static void +doit(void) +{ + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + signal(SIGCHLD, catch_child); + + /* + * Child sends parent this signal for window size hack. + */ + signal(SIGUSR1, sigusr1); + + signal(SIGPIPE, lostpeer); + + mode(1); + parent = getpid(); + child = fork(); + if (child == -1) { + warn("fork"); + done(1); + } + if (child == 0) { + signal(SIGCHLD, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + if (reader() == 0) + errx(1, "connection closed.\r"); + sleep(1); + errx(1, "\aconnection closed.\r"); + } + + writer(); + warnx("closed connection.\r"); + done(0); +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n", + "8DEKLdx", " [-k realm] "); + exit(1); +} + +static u_int +getescape(char *p) +{ + long val; + int len; + + if ((len = strlen(p)) == 1) /* use any single char, including '\' */ + return ((u_int)*p); + /* otherwise, \nnn */ + if (*p == '\\' && len >= 2 && len <= 4) { + val = strtol(++p, NULL, 8); + for (;;) { + if (!*++p) + return ((u_int)val); + if (*p < '0' || *p > '8') + break; + } + } + warnx("illegal option value -- e"); + usage(); + return 0; +} + +int +main(int argc, char **argv) +{ + struct passwd *pw; + int sv_port, user_port = 0; + int argoff, ch, dflag, Dflag, one, uid; + char *host, *user, term[1024]; + + argoff = dflag = Dflag = 0; + one = 1; + host = user = NULL; + + set_progname(argv[0]); + + /* handle "rlogin host flags" */ + if (argc > 2 && argv[1][0] != '-') { + host = argv[1]; + argoff = 1; + } + +#define OPTIONS "8DEKLde:k:l:xp:" + while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) + switch(ch) { + case '8': + eight = 1; + break; + case 'D': + Dflag = 1; + break; + case 'E': + noescape = 1; + break; + case 'K': + use_kerberos = 0; + break; + case 'd': + dflag = 1; + break; + case 'e': + noescape = 0; + escapechar = getescape(optarg); + break; + case 'k': + dest_realm = dst_realm_buf; + strlcpy(dest_realm, optarg, REALM_SZ); + break; + case 'l': + user = optarg; + break; + case 'x': + doencrypt = 1; + break; + case 'p': { + char *endptr; + + user_port = strtol (optarg, &endptr, 0); + if (user_port == 0 && optarg == endptr) + errx (1, "Bad port `%s'", optarg); + user_port = htons(user_port); + break; + } + case '?': + default: + usage(); + } + optind += argoff; + + /* if haven't gotten a host yet, do so */ + if (!host && !(host = argv[optind++])) + usage(); + + if (argv[optind]) + usage(); + + if (!(pw = k_getpwuid(uid = getuid()))) + errx(1, "unknown user id."); + if (!user) + user = pw->pw_name; + + if (user_port) + sv_port = user_port; + else + sv_port = get_login_port(use_kerberos, doencrypt); + + { + char *p = getenv("TERM"); + struct termios tty; + int i; + + if (p == NULL) + p = "network"; + + if (tcgetattr(0, &tty) == 0 + && (i = speed_t2int (cfgetospeed(&tty))) > 0) + snprintf (term, sizeof(term), + "%s/%d", + p, i); + else + snprintf (term, sizeof(term), + "%s", + p); + } + + get_window_size(0, &winsize); + + if (use_kerberos) { + setuid(getuid()); + rem = KSUCCESS; + errno = 0; + if (dest_realm == NULL) + dest_realm = krb_realmofhost(host); + + if (doencrypt) + rem = krcmd_mutual(&host, sv_port, user, term, 0, + dest_realm, &cred, schedule); + else + rem = krcmd(&host, sv_port, user, term, 0, + dest_realm); + if (rem < 0) { + int i; + char **newargv; + + if (errno == ECONNREFUSED) + warning("remote host doesn't support Kerberos"); + if (errno == ENOENT) + warning("can't provide Kerberos auth data"); + newargv = malloc((argc + 2) * sizeof(*newargv)); + if (newargv == NULL) + err(1, "malloc"); + newargv[0] = argv[0]; + newargv[1] = "-K"; + for(i = 1; i < argc; ++i) + newargv[i + 1] = argv[i]; + newargv[argc + 1] = NULL; + execv(_PATH_RLOGIN, newargv); + } + } else { + if (doencrypt) + errx(1, "the -x flag requires Kerberos authentication."); + if (geteuid() != 0) + errx(1, "not installed setuid root, " + "only root may use non kerberized rlogin"); + rem = rcmd(&host, sv_port, pw->pw_name, user, term, 0); + } + + if (rem < 0) + exit(1); + +#ifdef HAVE_SETSOCKOPT +#ifdef SO_DEBUG + if (dflag && + setsockopt(rem, SOL_SOCKET, SO_DEBUG, (void *)&one, + sizeof(one)) < 0) + warn("setsockopt"); +#endif +#ifdef TCP_NODELAY + if (Dflag && + setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, (void *)&one, + sizeof(one)) < 0) + warn("setsockopt(TCP_NODELAY)"); +#endif +#ifdef IP_TOS + one = IPTOS_LOWDELAY; + if (setsockopt(rem, IPPROTO_IP, IP_TOS, (void *)&one, sizeof(int)) < 0) + warn("setsockopt(IP_TOS)"); +#endif /* IP_TOS */ +#endif /* HAVE_SETSOCKOPT */ + + setuid(uid); + doit(); + return 0; +} |