#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)sys_term.c 8.4+1 (Berkeley) 5/30/95";
#else
__RCSID("$NetBSD: sys_term.c,v 1.51 2025/06/27 21:36:23 andvar Exp $");
#endif
#endif
#include "telnetd.h"
#include "pathnames.h"
#include <util.h>
#include <vis.h>
#ifdef SUPPORT_UTMP
#include <utmp.h>
#endif
#ifdef SUPPORT_UTMPX
#include <utmpx.h>
#endif
struct termios termbuf, termbuf2;
void getptyslave(void);
int cleanopen(char *);
char **addarg(char **, const char *);
void scrub_env(void);
int getent(char *, char *);
char *getstr(const char *, char **);
#ifdef KRB5
extern void kerberos5_cleanup(void);
#endif
void
init_termbuf(void)
{
(void) tcgetattr(pty, &termbuf);
termbuf2 = termbuf;
}
#if defined(LINEMODE) && defined(TIOCPKT_IOCTL)
void
copy_termbuf(char *cp, int len)
{
if ((size_t)len > sizeof(termbuf))
len = sizeof(termbuf);
memmove((char *)&termbuf, cp, len);
termbuf2 = termbuf;
}
#endif
void
set_termbuf(void)
{
if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
(void) tcsetattr(pty, TCSANOW, &termbuf);
}
int
spcset(int func, cc_t *valp, cc_t **valpp)
{
#define setval(a, b) *valp = termbuf.c_cc[a]; \
*valpp = &termbuf.c_cc[a]; \
return(b);
#define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
switch(func) {
case SLC_EOF:
setval(VEOF, SLC_VARIABLE);
case SLC_EC:
setval(VERASE, SLC_VARIABLE);
case SLC_EL:
setval(VKILL, SLC_VARIABLE);
case SLC_IP:
setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
case SLC_ABORT:
setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
case SLC_XON:
setval(VSTART, SLC_VARIABLE);
case SLC_XOFF:
setval(VSTOP, SLC_VARIABLE);
case SLC_EW:
setval(VWERASE, SLC_VARIABLE);
case SLC_RP:
setval(VREPRINT, SLC_VARIABLE);
case SLC_LNEXT:
setval(VLNEXT, SLC_VARIABLE);
case SLC_AO:
setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
case SLC_SUSP:
setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
case SLC_FORW1:
setval(VEOL, SLC_VARIABLE);
case SLC_FORW2:
setval(VEOL2, SLC_VARIABLE);
case SLC_AYT:
setval(VSTATUS, SLC_VARIABLE);
case SLC_BRK:
case SLC_SYNCH:
case SLC_EOR:
defval(0);
default:
*valp = 0;
*valpp = 0;
return(SLC_NOSUPPORT);
}
}
static int ptyslavefd;
int
getpty(int *ptynum)
{
int ptyfd;
ptyfd = openpty(ptynum, &ptyslavefd, line, NULL, NULL);
if (ptyfd == 0)
return *ptynum;
ptyslavefd = -1;
return (-1);
}
#ifdef LINEMODE
int
tty_linemode(void)
{
return(termbuf.c_lflag & EXTPROC);
}
void
tty_setlinemode(int on)
{
set_termbuf();
(void) ioctl(pty, TIOCEXT, (char *)&on);
init_termbuf();
}
#endif
int
tty_isecho(void)
{
return (termbuf.c_lflag & ECHO);
}
int
tty_flowmode(void)
{
return((termbuf.c_iflag & IXON) ? 1 : 0);
}
int
tty_restartany(void)
{
return((termbuf.c_iflag & IXANY) ? 1 : 0);
}
void
tty_setecho(int on)
{
if (on)
termbuf.c_lflag |= ECHO;
else
termbuf.c_lflag &= ~ECHO;
}
int
tty_israw(void)
{
return(!(termbuf.c_lflag & ICANON));
}
void
tty_binaryin(int on)
{
if (on) {
termbuf.c_iflag &= ~ISTRIP;
} else {
termbuf.c_iflag |= ISTRIP;
}
}
void
tty_binaryout(int on)
{
if (on) {
termbuf.c_cflag &= ~(CSIZE|PARENB);
termbuf.c_cflag |= CS8;
termbuf.c_oflag &= ~OPOST;
} else {
termbuf.c_cflag &= ~CSIZE;
termbuf.c_cflag |= CS7|PARENB;
termbuf.c_oflag |= OPOST;
}
}
int
tty_isbinaryin(void)
{
return(!(termbuf.c_iflag & ISTRIP));
}
int
tty_isbinaryout(void)
{
return(!(termbuf.c_oflag&OPOST));
}
#ifdef LINEMODE
int
tty_isediting(void)
{
return(termbuf.c_lflag & ICANON);
}
int
tty_istrapsig(void)
{
return(termbuf.c_lflag & ISIG);
}
void
tty_setedit(int on)
{
if (on)
termbuf.c_lflag |= ICANON;
else
termbuf.c_lflag &= ~ICANON;
}
void
tty_setsig(int on)
{
if (on)
termbuf.c_lflag |= ISIG;
else
termbuf.c_lflag &= ~ISIG;
}
#endif
int
tty_issofttab(void)
{
# ifdef OXTABS
return (termbuf.c_oflag & OXTABS);
# endif
# ifdef TABDLY
return ((termbuf.c_oflag & TABDLY) == TAB3);
# endif
}
void
tty_setsofttab(int on)
{
if (on) {
# ifdef OXTABS
termbuf.c_oflag |= OXTABS;
# endif
# ifdef TABDLY
termbuf.c_oflag &= ~TABDLY;
termbuf.c_oflag |= TAB3;
# endif
} else {
# ifdef OXTABS
termbuf.c_oflag &= ~OXTABS;
# endif
# ifdef TABDLY
termbuf.c_oflag &= ~TABDLY;
termbuf.c_oflag |= TAB0;
# endif
}
}
int
tty_islitecho(void)
{
# ifdef ECHOCTL
return (!(termbuf.c_lflag & ECHOCTL));
# endif
# ifdef TCTLECH
return (!(termbuf.c_lflag & TCTLECH));
# endif
# if !defined(ECHOCTL) && !defined(TCTLECH)
return (0);
# endif
}
void
tty_setlitecho(int on)
{
# ifdef ECHOCTL
if (on)
termbuf.c_lflag &= ~ECHOCTL;
else
termbuf.c_lflag |= ECHOCTL;
# endif
# ifdef TCTLECH
if (on)
termbuf.c_lflag &= ~TCTLECH;
else
termbuf.c_lflag |= TCTLECH;
# endif
}
int
tty_iscrnl(void)
{
return (termbuf.c_iflag & ICRNL);
}
void
tty_tspeed(int val)
{
cfsetospeed(&termbuf, val);
}
void
tty_rspeed(int val)
{
cfsetispeed(&termbuf, val);
}
extern int def_tspeed, def_rspeed;
extern int def_row, def_col;
void
getptyslave(void)
{
int t = -1;
#ifdef LINEMODE
int waslm;
#endif
struct winsize ws;
#ifdef LINEMODE
waslm = tty_linemode();
#endif
t = open(_PATH_TTY, O_RDWR);
if (t >= 0) {
(void) ioctl(t, TIOCNOTTY, (char *)0);
(void) close(t);
}
t = cleanopen(line);
if (t < 0)
fatalperror(net, line);
init_termbuf();
if (def_row || def_col) {
memset((char *)&ws, 0, sizeof(ws));
ws.ws_col = def_col;
ws.ws_row = def_row;
(void)ioctl(t, TIOCSWINSZ, (char *)&ws);
}
tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
#ifdef LINEMODE
if (waslm)
tty_setlinemode(1);
#endif
set_termbuf();
if (login_tty(t) == -1)
fatalperror(net, "login_tty");
if (net > 2)
(void) close(net);
if (pty > 2) {
(void) close(pty);
pty = -1;
}
}
int
cleanopen(char *ttyline)
{
return ptyslavefd;
}
void
startslave(char *host, int autologin, char *autoname)
{
int i;
#ifdef AUTHENTICATION
if (!autoname || !autoname[0])
autologin = 0;
if (autologin < auth_level) {
fatal(net, "Authorization failed");
exit(1);
}
#endif
if ((i = fork()) < 0)
fatalperror(net, "fork");
if (i) {
} else {
getptyslave();
start_login(host, autologin, autoname);
}
}
char *envinit[3];
void
init_env(void)
{
char **envp;
envp = envinit;
if ((*envp = getenv("TZ")))
*envp++ -= 3;
*envp = 0;
environ = envinit;
}
extern char *gettyname;
void
start_login(char *host, int autologin, char *name)
{
char **argv;
#define TABBUFSIZ 512
char defent[TABBUFSIZ];
char defstrs[TABBUFSIZ];
#undef TABBUFSIZ
const char *loginprog = NULL;
extern struct sockaddr_storage from;
char buf[sizeof(from) * 4 + 1];
char *user;
user = getenv("USER");
user = (user != NULL) ? strdup(user) : NULL;
scrub_env();
argv = addarg(0, "login");
argv = addarg(argv, "-a");
(void)strvisx(buf, (const char *)(const void *)&from, sizeof(from),
VIS_WHITE);
argv = addarg(argv, buf);
argv = addarg(argv, "-h");
argv = addarg(argv, host);
argv = addarg(argv, "-p");
#ifdef LINEMODE
if (lmodetype == REAL_LINEMODE)
setenv("LINEMODE", "real", 1);
# ifdef KLUDGELINEMODE
else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK)
setenv("LINEMODE", "kludge", 1);
# endif
#endif
#ifdef SECURELOGIN
if (require_secure_login)
argv = addarg(argv, "-s");
#endif
#ifdef AUTHENTICATION
if (auth_level >= 0 && autologin == AUTH_VALID) {
argv = addarg(argv, "-f");
argv = addarg(argv, "--");
argv = addarg(argv, name);
} else
#endif
if (user != NULL) {
argv = addarg(argv, "--");
argv = addarg(argv, user);
unsetenv("USER");
}
if (getent(defent, gettyname) == 1) {
char *cp = defstrs;
loginprog = getstr("lo", &cp);
}
if (loginprog == NULL)
loginprog = _PATH_LOGIN;
closelog();
sleep(1);
execv(loginprog, argv);
syslog(LOG_ERR, "%s: %m", loginprog);
fatalperror(net, loginprog);
}
char **
addarg(char **argv, const char *val)
{
char **cpp;
char **nargv;
if (argv == NULL) {
argv = malloc(sizeof(*argv) * 12);
if (argv == NULL)
return(NULL);
*argv++ = (char *)10;
*argv = (char *)0;
}
for (cpp = argv; *cpp; cpp++)
;
if (cpp == &argv[(long)argv[-1]]) {
--argv;
nargv = realloc(argv, sizeof(*argv) * ((long)(*argv) + 10 + 2));
if (nargv == NULL) {
fatal(net, "not enough memory");
}
argv = nargv;
*argv = (char *)((long)(*argv) + 10);
argv++;
cpp = &argv[(long)argv[-1] - 10];
}
*cpp++ = __UNCONST(val);
*cpp = 0;
return(argv);
}
void
scrub_env(void)
{
static const char *reject[] = {
"TERMCAP=/",
NULL
};
static const char *acceptstr[] = {
"XAUTH=", "XAUTHORITY=", "DISPLAY=",
"TERM=",
"EDITOR=",
"PAGER=",
"LOGNAME=",
"POSIXLY_CORRECT=",
"TERMCAP=",
"PRINTER=",
NULL
};
char **cpp, **cpp2;
const char **p;
for (cpp2 = cpp = environ; *cpp; cpp++) {
int reject_it = 0;
for(p = reject; *p; p++)
if(strncmp(*cpp, *p, strlen(*p)) == 0) {
reject_it = 1;
break;
}
if (reject_it)
continue;
for(p = acceptstr; *p; p++)
if(strncmp(*cpp, *p, strlen(*p)) == 0)
break;
if(*p != NULL)
*cpp2++ = *cpp;
}
*cpp2 = NULL;
}
void
cleanup(int sig)
{
char *p, c;
p = line + sizeof(_PATH_DEV) - 1;
#ifdef SUPPORT_UTMP
if (logout(p))
logwtmp(p, "", "");
#endif
#ifdef SUPPORT_UTMPX
if (logoutx(p, 0, DEAD_PROCESS))
logwtmpx(p, "", "", 0, DEAD_PROCESS);
#endif
(void)chmod(line, 0666);
(void)chown(line, 0, 0);
c = *p; *p = 'p';
(void)chmod(line, 0666);
(void)chown(line, 0, 0);
*p = c;
if (ttyaction(line, "telnetd", "root"))
syslog(LOG_ERR, "%s: ttyaction failed", line);
(void) shutdown(net, 2);
exit(1);
}