From 7d07d2de2f52d4e2eba169e5563165309a795128 Mon Sep 17 00:00:00 2001 From: rgrimes Date: Fri, 27 May 1994 12:39:25 +0000 Subject: BSD 4.4 Lite Libexec Sources --- libexec/rlogind/rlogind.c | 759 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) create mode 100644 libexec/rlogind/rlogind.c (limited to 'libexec/rlogind/rlogind.c') diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c new file mode 100644 index 0000000..6f5f5e1 --- /dev/null +++ b/libexec/rlogind/rlogind.c @@ -0,0 +1,759 @@ +/*- + * Copyright (c) 1983, 1988, 1989, 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * remote login server: + * \0 + * remuser\0 + * locuser\0 + * terminal_type/speed\0 + * data + */ + +#define FD_SETSIZE 16 /* don't need many bits for select */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#ifndef TIOCPKT_WINDOW +#define TIOCPKT_WINDOW 0x80 +#endif + +#ifdef KERBEROS +#include +#include +#define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n" + +AUTH_DAT *kdata; +KTEXT ticket; +u_char auth_buf[sizeof(AUTH_DAT)]; +u_char tick_buf[sizeof(KTEXT_ST)]; +Key_schedule schedule; +int doencrypt, retval, use_kerberos, vacuous; + +#define ARGSTR "alnkvx" +#else +#define ARGSTR "aln" +#endif /* KERBEROS */ + +char *env[2]; +#define NMAX 30 +char lusername[NMAX+1], rusername[NMAX+1]; +static char term[64] = "TERM="; +#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ +int keepalive = 1; +int check_all = 0; + +struct passwd *pwd; + +void doit __P((int, struct sockaddr_in *)); +int control __P((int, char *, int)); +void protocol __P((int, int)); +void cleanup __P((int)); +void fatal __P((int, char *, int)); +int do_rlogin __P((struct sockaddr_in *)); +void getstr __P((char *, int, char *)); +void setup_term __P((int)); +int do_krb_login __P((struct sockaddr_in *)); +void usage __P((void)); +int local_domain __P((char *)); +char *topdomain __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int __check_rhosts_file; + struct sockaddr_in from; + int ch, fromlen, on; + + openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); + + opterr = 0; + while ((ch = getopt(argc, argv, ARGSTR)) != EOF) + switch (ch) { + case 'a': + check_all = 1; + break; + case 'l': + __check_rhosts_file = 0; + break; + case 'n': + keepalive = 0; + break; +#ifdef KERBEROS + case 'k': + use_kerberos = 1; + break; + case 'v': + vacuous = 1; + break; +#ifdef CRYPT + case 'x': + doencrypt = 1; + break; +#endif +#endif + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + +#ifdef KERBEROS + if (use_kerberos && vacuous) { + usage(); + fatal(STDERR_FILENO, "only one of -k and -v allowed", 0); + } +#endif + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + syslog(LOG_ERR,"Can't get peer name of remote host: %m"); + fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); + } + on = 1; + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + on = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + doit(0, &from); +} + +int child; +int netf; +char line[MAXPATHLEN]; +int confirmed; + +struct winsize win = { 0, 0, 0, 0 }; + + +void +doit(f, fromp) + int f; + struct sockaddr_in *fromp; +{ + int master, pid, on = 1; + int authenticated = 0; + register struct hostent *hp; + char hostname[2 * MAXHOSTNAMELEN + 1]; + char c; + + alarm(60); + read(f, &c, 1); + + if (c != 0) + exit(1); +#ifdef KERBEROS + if (vacuous) + fatal(f, "Remote host requires Kerberos authentication", 0); +#endif + + alarm(0); + fromp->sin_port = ntohs((u_short)fromp->sin_port); + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr), + fromp->sin_family); + if (hp) + (void)strcpy(hostname, hp->h_name); + else + (void)strcpy(hostname, inet_ntoa(fromp->sin_addr)); + +#ifdef KERBEROS + if (use_kerberos) { + retval = do_krb_login(fromp); + if (retval == 0) + authenticated++; + else if (retval > 0) + fatal(f, krb_err_txt[retval], 0); + write(f, &c, 1); + confirmed = 1; /* we sent the null! */ + } else +#endif + { + if (fromp->sin_family != AF_INET || + fromp->sin_port >= IPPORT_RESERVED || + fromp->sin_port < IPPORT_RESERVED/2) { + syslog(LOG_NOTICE, "Connection from %s on illegal port", + inet_ntoa(fromp->sin_addr)); + fatal(f, "Permission denied", 0); + } +#ifdef IP_OPTIONS + { + u_char optbuf[BUFSIZ/3], *cp; + char lbuf[BUFSIZ], *lp; + int optsize = sizeof(optbuf), ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, + &optsize) == 0 && optsize != 0) { + lp = lbuf; + for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) + sprintf(lp, " %2.2x", *cp); + syslog(LOG_NOTICE, + "Connection received using IP options (ignored):%s", + lbuf); + if (setsockopt(0, ipproto, IP_OPTIONS, + (char *)NULL, optsize) != 0) { + syslog(LOG_ERR, + "setsockopt IP_OPTIONS NULL: %m"); + exit(1); + } + } + } +#endif + if (do_rlogin(fromp) == 0) + authenticated++; + } + if (confirmed == 0) { + write(f, "", 1); + confirmed = 1; /* we sent the null! */ + } +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE) - 1); +#endif +#endif + netf = f; + + pid = forkpty(&master, line, NULL, &win); + if (pid < 0) { + if (errno == ENOENT) + fatal(f, "Out of ptys", 0); + else + fatal(f, "Forkpty", 1); + } + if (pid == 0) { + if (f > 2) /* f should always be 0, but... */ + (void) close(f); + setup_term(0); + if (authenticated) { +#ifdef KERBEROS + if (use_kerberos && (pwd->pw_uid == 0)) + syslog(LOG_INFO|LOG_AUTH, + "ROOT Kerberos login from %s.%s@%s on %s\n", + kdata->pname, kdata->pinst, kdata->prealm, + hostname); +#endif + + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, "-f", lusername, (char *)NULL); + } else + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, lusername, (char *)NULL); + fatal(STDERR_FILENO, _PATH_LOGIN, 1); + /*NOTREACHED*/ + } +#ifdef CRYPT +#ifdef KERBEROS + /* + * If encrypted, don't turn on NBIO or the des read/write + * routines will croak. + */ + + if (!doencrypt) +#endif +#endif + ioctl(f, FIONBIO, &on); + ioctl(master, FIONBIO, &on); + ioctl(master, TIOCPKT, &on); + signal(SIGCHLD, cleanup); + protocol(f, master); + signal(SIGCHLD, SIG_IGN); + cleanup(0); +} + +char magic[2] = { 0377, 0377 }; +char oobdata[] = {TIOCPKT_WINDOW}; + +/* + * Handle a "control" request (signaled by magic being present) + * in the data stream. For now, we are only willing to handle + * window size changes. + */ +int +control(pty, cp, n) + int pty; + char *cp; + int n; +{ + struct winsize w; + + if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') + return (0); + oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ + bcopy(cp+4, (char *)&w, sizeof(w)); + w.ws_row = ntohs(w.ws_row); + w.ws_col = ntohs(w.ws_col); + w.ws_xpixel = ntohs(w.ws_xpixel); + w.ws_ypixel = ntohs(w.ws_ypixel); + (void)ioctl(pty, TIOCSWINSZ, &w); + return (4+sizeof (w)); +} + +/* + * rlogin "protocol" machine. + */ +void +protocol(f, p) + register int f, p; +{ + char pibuf[1024+1], fibuf[1024], *pbp, *fbp; + register pcc = 0, fcc = 0; + int cc, nfd, n; + char cntl; + + /* + * Must ignore SIGTTOU, otherwise we'll stop + * when we try and set slave pty's window shape + * (our controlling tty is the master pty). + */ + (void) signal(SIGTTOU, SIG_IGN); + send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ + if (f > p) + nfd = f + 1; + else + nfd = p + 1; + if (nfd > FD_SETSIZE) { + syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); + fatal(f, "internal error (select mask too small)", 0); + } + for (;;) { + fd_set ibits, obits, ebits, *omask; + + FD_ZERO(&ebits); + FD_ZERO(&ibits); + FD_ZERO(&obits); + omask = (fd_set *)NULL; + if (fcc) { + FD_SET(p, &obits); + omask = &obits; + } else + FD_SET(f, &ibits); + if (pcc >= 0) + if (pcc) { + FD_SET(f, &obits); + omask = &obits; + } else + FD_SET(p, &ibits); + FD_SET(p, &ebits); + if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { + if (errno == EINTR) + continue; + fatal(f, "select", 1); + } + if (n == 0) { + /* shouldn't happen... */ + sleep(5); + continue; + } +#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) + if (FD_ISSET(p, &ebits)) { + cc = read(p, &cntl, 1); + if (cc == 1 && pkcontrol(cntl)) { + cntl |= oobdata[0]; + send(f, &cntl, 1, MSG_OOB); + if (cntl & TIOCPKT_FLUSHWRITE) { + pcc = 0; + FD_CLR(p, &ibits); + } + } + } + if (FD_ISSET(f, &ibits)) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + fcc = des_read(f, fibuf, sizeof(fibuf)); + else +#endif +#endif + fcc = read(f, fibuf, sizeof(fibuf)); + if (fcc < 0 && errno == EWOULDBLOCK) + fcc = 0; + else { + register char *cp; + int left, n; + + if (fcc <= 0) + break; + fbp = fibuf; + + top: + for (cp = fibuf; cp < fibuf+fcc-1; cp++) + if (cp[0] == magic[0] && + cp[1] == magic[1]) { + left = fcc - (cp-fibuf); + n = control(p, cp, left); + if (n) { + left -= n; + if (left > 0) + bcopy(cp+n, cp, left); + fcc -= n; + goto top; /* n^2 */ + } + } + FD_SET(p, &obits); /* try write */ + } + } + + if (FD_ISSET(p, &obits) && fcc > 0) { + cc = write(p, fbp, fcc); + if (cc > 0) { + fcc -= cc; + fbp += cc; + } + } + + if (FD_ISSET(p, &ibits)) { + pcc = read(p, pibuf, sizeof (pibuf)); + pbp = pibuf; + if (pcc < 0 && errno == EWOULDBLOCK) + pcc = 0; + else if (pcc <= 0) + break; + else if (pibuf[0] == 0) { + pbp++, pcc--; +#ifdef CRYPT +#ifdef KERBEROS + if (!doencrypt) +#endif +#endif + FD_SET(f, &obits); /* try write */ + } else { + if (pkcontrol(pibuf[0])) { + pibuf[0] |= oobdata[0]; + send(f, &pibuf[0], 1, MSG_OOB); + } + pcc = 0; + } + } + if ((FD_ISSET(f, &obits)) && pcc > 0) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + cc = des_write(f, pbp, pcc); + else +#endif +#endif + cc = write(f, pbp, pcc); + if (cc < 0 && errno == EWOULDBLOCK) { + /* + * This happens when we try write after read + * from p, but some old kernels balk at large + * writes even when select returns true. + */ + if (!FD_ISSET(p, &ibits)) + sleep(5); + continue; + } + if (cc > 0) { + pcc -= cc; + pbp += cc; + } + } + } +} + +void +cleanup(signo) + int signo; +{ + char *p; + + p = line + sizeof(_PATH_DEV) - 1; + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + shutdown(netf, 2); + exit(1); +} + +void +fatal(f, msg, syserr) + int f; + char *msg; + int syserr; +{ + int len; + char buf[BUFSIZ], *bp = buf; + + /* + * Prepend binary one to message if we haven't sent + * the magic null as confirmation. + */ + if (!confirmed) + *bp++ = '\01'; /* error indicator */ + if (syserr) + len = sprintf(bp, "rlogind: %s: %s.\r\n", + msg, strerror(errno)); + else + len = sprintf(bp, "rlogind: %s.\r\n", msg); + (void) write(f, buf, bp + len - buf); + exit(1); +} + +int +do_rlogin(dest) + struct sockaddr_in *dest; +{ + getstr(rusername, sizeof(rusername), "remuser too long"); + getstr(lusername, sizeof(lusername), "locuser too long"); + getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); + + pwd = getpwnam(lusername); + if (pwd == NULL) + return (-1); + if (pwd->pw_uid == 0) + return (-1); + /* XXX why don't we syslog() failure? */ + return (iruserok(dest->sin_addr.s_addr, 0, rusername, lusername)); +} + +void +getstr(buf, cnt, errmsg) + char *buf; + int cnt; + char *errmsg; +{ + char c; + + do { + if (read(0, &c, 1) != 1) + exit(1); + if (--cnt < 0) + fatal(STDOUT_FILENO, errmsg, 0); + *buf++ = c; + } while (c != 0); +} + +extern char **environ; + +void +setup_term(fd) + int fd; +{ + register char *cp = index(term+ENVSIZE, '/'); + char *speed; + struct termios tt; + +#ifndef notyet + tcgetattr(fd, &tt); + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = index(speed, '/'); + if (cp) + *cp++ = '\0'; + cfsetspeed(&tt, atoi(speed)); + } + + tt.c_iflag = TTYDEF_IFLAG; + tt.c_oflag = TTYDEF_OFLAG; + tt.c_lflag = TTYDEF_LFLAG; + tcsetattr(fd, TCSAFLUSH, &tt); +#else + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = index(speed, '/'); + if (cp) + *cp++ = '\0'; + tcgetattr(fd, &tt); + cfsetspeed(&tt, atoi(speed)); + tcsetattr(fd, TCSAFLUSH, &tt); + } +#endif + + env[0] = term; + env[1] = 0; + environ = env; +} + +#ifdef KERBEROS +#define VERSION_SIZE 9 + +/* + * Do the remote kerberos login to the named host with the + * given inet address + * + * Return 0 on valid authorization + * Return -1 on valid authentication, no authorization + * Return >0 for error conditions + */ +int +do_krb_login(dest) + struct sockaddr_in *dest; +{ + int rc; + char instance[INST_SZ], version[VERSION_SIZE]; + long authopts = 0L; /* !mutual */ + struct sockaddr_in faddr; + + kdata = (AUTH_DAT *) auth_buf; + ticket = (KTEXT) tick_buf; + + instance[0] = '*'; + instance[1] = '\0'; + +#ifdef CRYPT + if (doencrypt) { + rc = sizeof(faddr); + if (getsockname(0, (struct sockaddr *)&faddr, &rc)) + return (-1); + authopts = KOPT_DO_MUTUAL; + rc = krb_recvauth( + authopts, 0, + ticket, "rcmd", + instance, dest, &faddr, + kdata, "", schedule, version); + des_set_key(kdata->session, schedule); + + } else +#endif + rc = krb_recvauth( + authopts, 0, + ticket, "rcmd", + instance, dest, (struct sockaddr_in *) 0, + kdata, "", (bit_64 *) 0, version); + + if (rc != KSUCCESS) + return (rc); + + getstr(lusername, sizeof(lusername), "locuser"); + /* get the "cmd" in the rcmd protocol */ + getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type"); + + pwd = getpwnam(lusername); + if (pwd == NULL) + return (-1); + + /* returns nonzero for no access */ + if (kuserok(kdata, lusername) != 0) + return (-1); + + return (0); + +} +#endif /* KERBEROS */ + +void +usage() +{ +#ifdef KERBEROS + syslog(LOG_ERR, "usage: rlogind [-aln] [-k | -v]"); +#else + syslog(LOG_ERR, "usage: rlogind [-aln]"); +#endif +} + +/* + * Check whether host h is in our local domain, + * defined as sharing the last two components of the domain part, + * or the entire domain part if the local domain has only one component. + * If either name is unqualified (contains no '.'), + * assume that the host is local, as it will be + * interpreted as such. + */ +int +local_domain(h) + char *h; +{ + char localhost[MAXHOSTNAMELEN]; + char *p1, *p2; + + localhost[0] = 0; + (void) gethostname(localhost, sizeof(localhost)); + p1 = topdomain(localhost); + p2 = topdomain(h); + if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) + return (1); + return (0); +} + +char * +topdomain(h) + char *h; +{ + register char *p; + char *maybe = NULL; + int dots = 0; + + for (p = h + strlen(h); p >= h; p--) { + if (*p == '.') { + if (++dots == 2) + return (p); + maybe = p; + } + } + return (maybe); +} -- cgit v1.1