NetBSD/crypto/dist/krb4/appl/bsd/rlogind.c

964 lines
22 KiB
C

/*-
* 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.
*/
/*
* remote login server:
* \0
* remuser\0
* locuser\0
* terminal_type/speed\0
* data
*/
#include "bsd_locl.h"
RCSID("$Id: rlogind.c,v 1.1.1.1 2000/06/16 18:46:16 thorpej Exp $");
extern int __check_rhosts_file;
char *INSECURE_MESSAGE =
"\r\n*** Connection not encrypted! Communication may be eavesdropped. ***"
"\r\n*** Use telnet or rlogin -x instead! ***\r\n";
#ifndef NOENCRYPTION
char *SECURE_MESSAGE =
"This rlogin session is using DES encryption for all transmissions.\r\n";
#else
#define SECURE_MESSAGE INSECURE_MESSAGE
#endif
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 "Daip:lnkvxL:"
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;
int no_delay = 0;
struct passwd *pwd;
static const char *new_login = _PATH_LOGIN;
static void doit (int, struct sockaddr_in *);
static int control (int, char *, int);
static void protocol (int, int);
static RETSIGTYPE cleanup (int);
void fatal (int, const char *, int);
static int do_rlogin (struct sockaddr_in *);
static void setup_term (int);
static int do_krb_login (struct sockaddr_in *);
static void usage (void);
static int
readstream(int p, char *ibuf, int bufsize)
{
#ifndef HAVE_GETMSG
return read(p, ibuf, bufsize);
#else
static int flowison = -1; /* current state of flow: -1 is unknown */
static struct strbuf strbufc, strbufd;
static unsigned char ctlbuf[BUFSIZ];
static int use_read = 1;
int flags = 0;
int ret;
struct termios tsp;
struct iocblk ip;
char vstop, vstart;
int ixon;
int newflow;
if (use_read)
{
ret = read(p, ibuf, bufsize);
if (ret < 0 && errno == EBADMSG)
use_read = 0;
else
return ret;
}
strbufc.maxlen = BUFSIZ;
strbufc.buf = (char *)ctlbuf;
strbufd.maxlen = bufsize-1;
strbufd.len = 0;
strbufd.buf = ibuf+1;
ibuf[0] = 0;
ret = getmsg(p, &strbufc, &strbufd, &flags);
if (ret < 0) /* error of some sort -- probably EAGAIN */
return(-1);
if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
/* data message */
if (strbufd.len > 0) { /* real data */
return(strbufd.len + 1); /* count header char */
} else {
/* nothing there */
errno = EAGAIN;
return(-1);
}
}
/*
* It's a control message. Return 1, to look at the flag we set
*/
switch (ctlbuf[0]) {
case M_FLUSH:
if (ibuf[1] & FLUSHW)
ibuf[0] = TIOCPKT_FLUSHWRITE;
return(1);
case M_IOCTL:
memcpy(&ip, (ibuf+1), sizeof(ip));
switch (ip.ioc_cmd) {
#ifdef TCSETS
case TCSETS:
case TCSETSW:
case TCSETSF:
memcpy(&tsp,
(ibuf+1 + sizeof(struct iocblk)),
sizeof(tsp));
vstop = tsp.c_cc[VSTOP];
vstart = tsp.c_cc[VSTART];
ixon = tsp.c_iflag & IXON;
break;
#endif
default:
errno = EAGAIN;
return(-1);
}
newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
if (newflow != flowison) { /* it's a change */
flowison = newflow;
ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
return(1);
}
}
/* nothing worth doing anything about */
errno = EAGAIN;
return(-1);
#endif
}
#ifdef HAVE_UTMPX_H
static int
rlogind_logout(const char *line)
{
struct utmpx utmpx, *utxp;
int ret = 1;
setutxent ();
memset(&utmpx, 0, sizeof(utmpx));
utmpx.ut_type = USER_PROCESS;
strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
utxp = getutxline(&utmpx);
if (utxp) {
utxp->ut_user[0] = '\0';
utxp->ut_type = DEAD_PROCESS;
#ifdef HAVE_STRUCT_UTMPX_UT_EXIT
#ifdef _STRUCT___EXIT_STATUS
utxp->ut_exit.__e_termination = 0;
utxp->ut_exit.__e_exit = 0;
#elif defined(__osf__) /* XXX */
utxp->ut_exit.ut_termination = 0;
utxp->ut_exit.ut_exit = 0;
#else
utxp->ut_exit.e_termination = 0;
utxp->ut_exit.e_exit = 0;
#endif
#endif
gettimeofday(&utxp->ut_tv, NULL);
pututxline(utxp);
#ifdef WTMPX_FILE
updwtmpx(WTMPX_FILE, utxp);
#else
ret = 0;
#endif
}
endutxent();
return ret;
}
#else
static int
rlogind_logout(const char *line)
{
FILE *fp;
struct utmp ut;
int rval;
if (!(fp = fopen(_PATH_UTMP, "r+")))
return(0);
rval = 1;
while (fread(&ut, sizeof(struct utmp), 1, fp) == 1) {
if (!ut.ut_name[0] ||
strncmp(ut.ut_line, line, sizeof(ut.ut_line)))
continue;
memset(ut.ut_name, 0, sizeof(ut.ut_name));
#ifdef HAVE_STRUCT_UTMP_UT_HOST
memset(ut.ut_host, 0, sizeof(ut.ut_host));
#endif
#ifdef HAVE_STRUCT_UTMP_UT_TYPE
ut.ut_type = DEAD_PROCESS;
#endif
#ifdef HAVE_STRUCT_UTMP_UT_EXIT
#ifdef _STRUCT___EXIT_STATUS
ut.ut_exit.__e_termination = 0;
ut.ut_exit.__e_exit = 0;
#elif defined(__osf__) /* XXX */
ut.ut_exit.ut_termination = 0;
ut.ut_exit.ut_exit = 0;
#else
ut.ut_exit.e_termination = 0;
ut.ut_exit.e_exit = 0;
#endif
#endif
time(&ut.ut_time);
fseek(fp, (long)-sizeof(struct utmp), SEEK_CUR);
fwrite(&ut, sizeof(struct utmp), 1, fp);
fseek(fp, (long)0, SEEK_CUR);
rval = 0;
}
fclose(fp);
return(rval);
}
#endif
#ifndef HAVE_LOGWTMP
static void
logwtmp(const char *line, const char *name, const char *host)
{
struct utmp ut;
struct stat buf;
int fd;
memset (&ut, 0, sizeof(ut));
if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0)
return;
if (!fstat(fd, &buf)) {
strncpy(ut.ut_line, line, sizeof(ut.ut_line));
strncpy(ut.ut_name, name, sizeof(ut.ut_name));
#ifdef HAVE_STRUCT_UTMP_UT_ID
strncpy(ut.ut_id, make_id((char *)line), sizeof(ut.ut_id));
#endif
#ifdef HAVE_STRUCT_UTMP_UT_HOST
strncpy(ut.ut_host, host, sizeof(ut.ut_host));
#endif
#ifdef HAVE_STRUCT_UTMP_UT_PID
ut.ut_pid = getpid();
#endif
#ifdef HAVE_STRUCT_UTMP_UT_TYPE
if(name[0])
ut.ut_type = USER_PROCESS;
else
ut.ut_type = DEAD_PROCESS;
#endif
time(&ut.ut_time);
if (write(fd, &ut, sizeof(struct utmp)) !=
sizeof(struct utmp))
ftruncate(fd, buf.st_size);
}
close(fd);
}
#endif
int
main(int argc, char **argv)
{
struct sockaddr_in from;
int ch, fromlen, on;
int interactive = 0;
int portnum = 0;
set_progname(argv[0]);
openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
opterr = 0;
while ((ch = getopt(argc, argv, ARGSTR)) != -1)
switch (ch) {
case 'D':
no_delay = 1;
break;
case 'a':
break;
case 'i':
interactive = 1;
break;
case 'p':
portnum = htons(atoi(optarg));
break;
case 'l':
__check_rhosts_file = 0;
break;
case 'n':
keepalive = 0;
break;
case 'k':
use_kerberos = 1;
break;
case 'v':
vacuous = 1;
break;
case 'x':
doencrypt = 1;
break;
case 'L':
new_login = optarg;
break;
case '?':
default:
usage();
break;
}
argc -= optind;
argv += optind;
if (use_kerberos && vacuous) {
usage();
fatal(STDERR_FILENO, "only one of -k and -v allowed", 0);
}
if (interactive) {
if(portnum == 0)
portnum = get_login_port (use_kerberos, doencrypt);
mini_inetd (portnum);
}
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;
#ifdef HAVE_SETSOCKOPT
#ifdef SO_KEEPALIVE
if (keepalive &&
setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
sizeof (on)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
#endif
#ifdef TCP_NODELAY
if (no_delay &&
setsockopt(0, IPPROTO_TCP, TCP_NODELAY, (void *)&on,
sizeof(on)) < 0)
syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
#endif
#ifdef IP_TOS
on = IPTOS_LOWDELAY;
if (setsockopt(0, IPPROTO_IP, IP_TOS, (void *)&on, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif
#endif /* HAVE_SETSOCKOPT */
doit(0, &from);
return 0;
}
int child;
int netf;
char line[MaxPathLen];
int confirmed;
struct winsize win = { 0, 0, 0, 0 };
static void
doit(int f, struct sockaddr_in *fromp)
{
int master, pid, on = 1;
int authenticated = 0;
char hostname[2 * MaxHostNameLen + 1];
char c;
alarm(60);
read(f, &c, 1);
if (c != 0)
exit(1);
if (vacuous)
fatal(f, "Remote host requires Kerberos authentication", 0);
alarm(0);
inaddr2str (fromp->sin_addr, hostname, sizeof(hostname));
if (use_kerberos) {
retval = do_krb_login(fromp);
if (retval == 0)
authenticated++;
else if (retval > 0)
fatal(f, krb_get_err_text(retval), 0);
write(f, &c, 1);
confirmed = 1; /* we sent the null! */
} else {
fromp->sin_port = ntohs((u_short)fromp->sin_port);
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);
}
ip_options_and_die (0, fromp);
if (do_rlogin(fromp) == 0)
authenticated++;
}
if (confirmed == 0) {
write(f, "", 1);
confirmed = 1; /* we sent the null! */
}
#ifndef NOENCRYPTION
if (doencrypt)
des_enc_write(f, SECURE_MESSAGE,
strlen(SECURE_MESSAGE),
schedule, &kdata->session);
else
#endif
write(f, INSECURE_MESSAGE, strlen(INSECURE_MESSAGE));
netf = f;
#ifdef HAVE_FORKPTY
pid = forkpty(&master, line, NULL, NULL);
#else
pid = forkpty_truncate(&master, line, sizeof(line), NULL, NULL);
#endif
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... */
close(f);
setup_term(0);
if (lusername[0] == '-'){
syslog(LOG_ERR, "tried to pass user \"%s\" to login",
lusername);
fatal(STDERR_FILENO, "invalid user", 0);
}
if (authenticated) {
if (use_kerberos && (pwd->pw_uid == 0))
syslog(LOG_INFO|LOG_AUTH,
"ROOT Kerberos login from %s on %s\n",
krb_unparse_name_long(kdata->pname,
kdata->pinst,
kdata->prealm),
hostname);
execl(new_login, "login", "-p",
"-h", hostname, "-f", "--", lusername, 0);
} else
execl(new_login, "login", "-p",
"-h", hostname, "--", lusername, 0);
fatal(STDERR_FILENO, new_login, 1);
/*NOTREACHED*/
}
/*
* If encrypted, don't turn on NBIO or the des read/write
* routines will croak.
*/
if (!doencrypt)
ioctl(f, FIONBIO, &on);
ioctl(master, FIONBIO, &on);
ioctl(master, TIOCPKT, &on);
#ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN);
#endif
signal(SIGCHLD, cleanup);
setsid();
protocol(f, master);
signal(SIGCHLD, SIG_IGN);
cleanup(0);
}
const char magic[2] = { 0377, 0377 };
/*
* Handle a "control" request (signaled by magic being present)
* in the data stream. For now, we are only willing to handle
* window size changes.
*/
static int
control(int master, char *cp, int n)
{
struct winsize w;
char *p;
u_int32_t tmp;
if (n < 4 + 4 * sizeof (u_int16_t) || cp[2] != 's' || cp[3] != 's')
return (0);
#ifdef TIOCSWINSZ
p = cp + 4;
p += krb_get_int(p, &tmp, 2, 0);
w.ws_row = tmp;
p += krb_get_int(p, &tmp, 2, 0);
w.ws_col = tmp;
p += krb_get_int(p, &tmp, 2, 0);
#ifdef HAVE_WS_XPIXEL
w.ws_xpixel = tmp;
#endif
p += krb_get_int(p, &tmp, 2, 0);
#ifdef HAVE_WS_YPIXEL
w.ws_ypixel = tmp;
#endif
ioctl(master, TIOCSWINSZ, &w);
#endif
return p - cp;
}
static
void
send_oob(int fd, char c)
{
static char last_oob = 0xFF;
#if (SunOS >= 50) || defined(__hpux)
/*
* PSoriasis and HP-UX always send TIOCPKT_DOSTOP at startup so we
* can avoid sending OOB data and thus not break on Linux by merging
* TIOCPKT_DOSTOP into the first TIOCPKT_WINDOW.
*/
static int oob_kludge = 2;
if (oob_kludge == 2)
{
oob_kludge--; /* First time send nothing */
return;
}
else if (oob_kludge == 1)
{
oob_kludge--; /* Second time merge TIOCPKT_WINDOW */
c |= TIOCPKT_WINDOW;
}
#endif
#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
c = pkcontrol(c);
/* Multiple OOB data breaks on Linux, avoid it when possible. */
if (c != last_oob)
send(fd, &c, 1, MSG_OOB);
last_oob = c;
}
/*
* rlogin "protocol" machine.
*/
static void
protocol(int f, int master)
{
char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
int pcc = 0, fcc = 0;
int cc, nfd, n;
char cntl;
unsigned char oob_queue = 0;
#ifdef SIGTTOU
/*
* Must ignore SIGTTOU, otherwise we'll stop
* when we try and set slave pty's window shape
* (our controlling tty is the master pty).
*/
signal(SIGTTOU, SIG_IGN);
#endif
send_oob(f, TIOCPKT_WINDOW); /* indicate new rlogin */
if (f > master)
nfd = f + 1;
else
nfd = master + 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(master, &obits);
omask = &obits;
} else
FD_SET(f, &ibits);
if (pcc >= 0) {
if (pcc) {
FD_SET(f, &obits);
omask = &obits;
} else
FD_SET(master, &ibits);
}
FD_SET(master, &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;
}
if (FD_ISSET(master, &ebits)) {
cc = readstream(master, &cntl, 1);
if (cc == 1 && pkcontrol(cntl)) {
#if 0 /* Kludge around */
send_oob(f, cntl);
#endif
oob_queue = cntl;
if (cntl & TIOCPKT_FLUSHWRITE) {
pcc = 0;
FD_CLR(master, &ibits);
}
}
}
if (FD_ISSET(f, &ibits)) {
#ifndef NOENCRYPTION
if (doencrypt)
fcc = des_enc_read(f, fibuf,
sizeof(fibuf),
schedule, &kdata->session);
else
#endif
fcc = read(f, fibuf, sizeof(fibuf));
if (fcc < 0 && errno == EWOULDBLOCK)
fcc = 0;
else {
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(master, cp, left);
if (n) {
left -= n;
if (left > 0)
memmove(cp, cp+n, left);
fcc -= n;
goto top; /* n^2 */
}
}
FD_SET(master, &obits); /* try write */
}
}
if (FD_ISSET(master, &obits) && fcc > 0) {
cc = write(master, fbp, fcc);
if (cc > 0) {
fcc -= cc;
fbp += cc;
}
}
if (FD_ISSET(master, &ibits)) {
pcc = readstream(master, pibuf, sizeof (pibuf));
pbp = pibuf;
if (pcc < 0 && errno == EWOULDBLOCK)
pcc = 0;
else if (pcc <= 0)
break;
else if (pibuf[0] == 0) {
pbp++, pcc--;
if (!doencrypt)
FD_SET(f, &obits); /* try write */
} else {
if (pkcontrol(pibuf[0])) {
oob_queue = pibuf[0];
#if 0 /* Kludge around */
send_oob(f, pibuf[0]);
#endif
}
pcc = 0;
}
}
if ((FD_ISSET(f, &obits)) && pcc > 0) {
#ifndef NOENCRYPTION
if (doencrypt)
cc = des_enc_write(f, pbp, pcc, schedule, &kdata->session);
else
#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(master, &ibits))
sleep(5);
continue;
}
if (cc > 0) {
pcc -= cc;
pbp += cc;
/* Only send urg data when normal data
* has just been sent.
* Linux has deep problems with more
* than one byte of OOB data.
*/
if (oob_queue) {
send_oob (f, oob_queue);
oob_queue = 0;
}
}
}
}
}
static RETSIGTYPE
cleanup(int signo)
{
char *p = clean_ttyname (line);
if (rlogind_logout(p) == 0)
logwtmp(p, "", "");
chmod(line, 0666);
chown(line, 0, 0);
*p = 'p';
chmod(line, 0666);
chown(line, 0, 0);
shutdown(netf, 2);
signal(SIGHUP, SIG_IGN);
#ifdef HAVE_VHANGUP
vhangup();
#endif /* HAVE_VHANGUP */
exit(1);
}
void
fatal(int f, const 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)
snprintf(bp, sizeof(buf) - (bp - buf),
"rlogind: %s: %s.\r\n",
msg, strerror(errno));
else
snprintf(bp, sizeof(buf) - (bp - buf),
"rlogind: %s.\r\n", msg);
len = strlen(bp);
#ifndef NOENCRYPTION
if (doencrypt)
des_enc_write(f, buf, bp + len - buf, schedule, &kdata->session);
else
#endif
write(f, buf, bp + len - buf);
exit(1);
}
static void
xgetstr(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);
}
static int
do_rlogin(struct sockaddr_in *dest)
{
xgetstr(rusername, sizeof(rusername), "remuser too long");
xgetstr(lusername, sizeof(lusername), "locuser too long");
xgetstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
pwd = k_getpwnam(lusername);
if (pwd == NULL)
return (-1);
if (pwd->pw_uid == 0 && strcmp("root", lusername) != 0)
{
syslog(LOG_ALERT, "NIS attack, user %s has uid 0", lusername);
return (-1);
}
return (iruserok(dest->sin_addr.s_addr,
(pwd->pw_uid == 0),
rusername,
lusername));
}
static void
setup_term(int fd)
{
char *cp = strchr(term+ENVSIZE, '/');
char *speed;
struct termios tt;
tcgetattr(fd, &tt);
if (cp) {
int s;
*cp++ = '\0';
speed = cp;
cp = strchr(speed, '/');
if (cp)
*cp++ = '\0';
s = int2speed_t (atoi (speed));
if (s > 0) {
cfsetospeed (&tt, s);
cfsetispeed (&tt, s);
}
}
tt.c_iflag &= ~INPCK;
tt.c_iflag |= ICRNL|IXON;
tt.c_oflag |= OPOST|ONLCR;
#ifdef TAB3
tt.c_oflag |= TAB3;
#endif /* TAB3 */
#ifdef ONLRET
tt.c_oflag &= ~ONLRET;
#endif /* ONLRET */
tt.c_lflag |= (ECHO|ECHOE|ECHOK|ISIG|ICANON);
tt.c_cflag &= ~PARENB;
tt.c_cflag |= CS8;
tt.c_cc[VMIN] = 1;
tt.c_cc[VTIME] = 0;
tt.c_cc[VEOF] = CEOF;
tcsetattr(fd, TCSAFLUSH, &tt);
env[0] = term;
env[1] = 0;
environ = env;
}
#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
*/
static int
do_krb_login(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;
k_getsockinst(0, instance, sizeof(instance));
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
rc = krb_recvauth(
authopts, 0,
ticket, "rcmd",
instance, dest, (struct sockaddr_in *) 0,
kdata, "", 0, version);
if (rc != KSUCCESS)
return (rc);
xgetstr(lusername, sizeof(lusername), "locuser");
/* get the "cmd" in the rcmd protocol */
xgetstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type");
pwd = k_getpwnam(lusername);
if (pwd == NULL)
return (-1);
if (pwd->pw_uid == 0 && strcmp("root", lusername) != 0)
{
syslog(LOG_ALERT, "NIS attack, user %s has uid 0", lusername);
return (-1);
}
/* returns nonzero for no access */
if (kuserok(kdata, lusername) != 0)
return (-1);
return (0);
}
static void
usage(void)
{
syslog(LOG_ERR,
"usage: rlogind [-Dailn] [-p port] [-x] [-L login] [-k | -v]");
exit(1);
}