#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/filio.h>
#include <sys/time.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/tihdr.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <netinet/in.h>
#define AUTHWHO_STR
#define AUTHTYPE_NAMES
#define AUTHHOW_NAMES
#define AUTHRSP_NAMES
#define ENCRYPT_NAMES
#include <arpa/telnet.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include <syslog.h>
#include <ctype.h>
#include <fcntl.h>
#include <sac.h>
#include <utmpx.h>
#include <sys/ttold.h>
#include <malloc.h>
#include <string.h>
#include <security/pam_appl.h>
#include <sys/tihdr.h>
#include <sys/logindmux.h>
#include <sys/telioctl.h>
#include <deflt.h>
#include <stdlib.h>
#include <string.h>
#include <stropts.h>
#include <termios.h>
#include <com_err.h>
#include <krb5.h>
#include <krb5_repository.h>
#include <des/des.h>
#include <rpc/des_crypt.h>
#include <sys/cryptmod.h>
#include <bsm/adt.h>
#define TELNETD_OPTS "Ss:a:dEXUhR:M:"
#ifdef DEBUG
#define DEBUG_OPTS "p:e"
#else
#define DEBUG_OPTS ""
#endif
#define OPT_NO 0
#define OPT_YES 1
#define OPT_YES_BUT_ALWAYS_LOOK 2
#define OPT_NO_BUT_ALWAYS_LOOK 3
#define MAXOPTLEN 256
#define MAXUSERNAMELEN 256
static char remopts[MAXOPTLEN];
static char myopts[MAXOPTLEN];
static uchar_t doopt[] = { (uchar_t)IAC, (uchar_t)DO, '%', 'c', 0 };
static uchar_t dont[] = { (uchar_t)IAC, (uchar_t)DONT, '%', 'c', 0 };
static uchar_t will[] = { (uchar_t)IAC, (uchar_t)WILL, '%', 'c', 0 };
static uchar_t wont[] = { (uchar_t)IAC, (uchar_t)WONT, '%', 'c', 0 };
static char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
static char *netibuf, *netip;
static int netibufsize;
#define NIACCUM(c) { *netip++ = c; \
ncc++; \
}
static char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
static char *neturg = 0;
static int not42 = 1;
static char defaultfile[] = "/etc/default/telnetd";
static char bannervar[] = "BANNER=";
static char BANNER1[] = "\r\n\r\n";
static char BANNER2[] = "\r\n\r\0\r\n\r\0";
static char subbuffer[4096], *subpointer = subbuffer, *subend = subbuffer;
#define SB_CLEAR() subpointer = subbuffer;
#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof (subbuffer))) { \
*subpointer++ = (c); \
}
#define SB_GET() ((*subpointer++)&0xff)
#define SB_EOF() (subpointer >= subend)
#define SB_LEN() (subend - subpointer)
#define MAXERRSTRLEN 1024
#define MAXPRINCLEN 256
extern uint_t kwarn_add_warning(char *, int);
extern uint_t kwarn_del_warning(char *);
static boolean_t auth_debug = 0;
static boolean_t negotiate_auth_krb5 = 1;
static boolean_t auth_negotiated = 0;
static int auth_status = 0;
static int auth_level = 0;
static char *AuthenticatingUser = NULL;
static char *krb5_name = NULL;
static krb5_address rsaddr = { 0, 0, 0, NULL };
static krb5_address rsport = { 0, 0, 0, NULL };
static krb5_context telnet_context = 0;
static krb5_auth_context auth_context = 0;
static krb5_ticket *ticket = NULL;
static krb5_keyblock *session_key = NULL;
static char *telnet_srvtab = NULL;
typedef struct {
uchar_t AuthName;
uchar_t AuthHow;
char *AuthString;
} AuthInfo;
static AuthInfo auth_list[] = {
{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL |
AUTH_ENCRYPT_ON, "KRB5 MUTUAL CRYPTO"},
{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL,
"KRB5 MUTUAL" },
{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_ONE_WAY,
"KRB5 1-WAY" },
{0, 0, "NONE"}
};
static AuthInfo NoAuth = {0, 0, NULL};
static AuthInfo *authenticated = NULL;
#define PREAMBLE_SIZE 5
#define POSTAMBLE_SIZE 5
#define STR_DATA_LEN(len) ((len) * 2 + PREAMBLE_SIZE + POSTAMBLE_SIZE)
static void auth_name(uchar_t *, int);
static void auth_is(uchar_t *, int);
#define NO_ENCRYPTION 0x00
#define SEND_ENCRYPTED 0x01
#define RECV_ENCRYPTED 0x02
#define ENCRYPT_BOTH_WAYS (SEND_ENCRYPTED | RECV_ENCRYPTED)
static telnet_enc_data_t encr_data;
static boolean_t negotiate_encrypt = B_TRUE;
static boolean_t sent_encrypt_support = B_FALSE;
static boolean_t sent_will_encrypt = B_FALSE;
static boolean_t sent_do_encrypt = B_FALSE;
static boolean_t enc_debug = 0;
static void encrypt_session_key(Session_Key *key, cipher_info_t *cinfo);
static int encrypt_send_encrypt_is();
extern void mit_des_fixup_key_parity(Block);
extern int krb5_setenv(const char *, const char *, int);
static int cryptmod_fd = -1;
#define LOGIN_PROGRAM "/bin/login"
#define TS_DATA 0
#define TS_IAC 1
#define TS_CR 2
#define TS_SB 3
#define TS_SE 4
#define TS_WILL 5
#define TS_WONT 6
#define TS_DO 7
#define TS_DONT 8
static int ncc;
static int manager;
static int pty;
static int net;
static int inter;
extern char **environ;
static char *line;
static int SYNCHing = 0;
static int state = TS_DATA;
static int env_ovar = -1;
static int env_ovalue = -1;
static char pam_svc_name[64];
static boolean_t telmod_init_done = B_FALSE;
static void doit(int, struct sockaddr_storage *);
static void willoption(int);
static void wontoption(int);
static void dooption(int);
static void dontoption(int);
static void fatal(int, char *);
static void fatalperror(int, char *, int);
static void mode(int, int);
static void interrupt(void);
static void drainstream(int);
static int readstream(int, char *, int);
static int send_oob(int fd, char *ptr, int count);
static int local_setenv(const char *name, const char *value, int rewrite);
static void local_unsetenv(const char *name);
static void suboption(void);
static int removemod(int f, char *modname);
static void willoption(int option);
static void wontoption(int option);
static void dooption(int option);
static void dontoption(int option);
static void write_data(const char *, ...);
static void write_data_len(const char *, int);
static void rmut(void);
static void cleanup(int);
static void telnet(int, int);
static void telrcv(void);
static void sendbrk(void);
static void ptyflush(void);
static void netclear(void);
static void netflush(void);
static void showbanner(void);
static void map_banner(char *);
static void defbanner(void);
static void ttloop(void);
struct envlist {
struct envlist *next;
char *name;
char *value;
int delete;
};
static struct envlist *envlist_head = NULL;
static struct {
int
system,
echotoggle,
modenegotiated,
didnetreceive,
ttypeopt,
ttypesubopt,
getterminal,
xdisplocopt,
xdisplocsubopt,
nawsopt,
nawssubopt,
environopt,
oenvironopt,
environsubopt,
oenvironsubopt,
gotDM;
int getauth;
int authopt;
int authdone;
int getencr;
int encropt;
int encr_support;
} clocks;
static int init_neg_done = 0;
static boolean_t resolve_hostname = 0;
static boolean_t show_hostinfo = 1;
#define settimer(x) (clocks.x = ++clocks.system)
#define sequenceIs(x, y) (clocks.x < clocks.y)
static void send_will(int);
static void send_wont(int);
static void send_do(int);
static char *__findenv(const char *name, int *offset);
static void
auth_finished(AuthInfo *ap, int result)
{
if ((authenticated = ap) == NULL) {
authenticated = &NoAuth;
if (myopts[TELOPT_ENCRYPT] == OPT_YES)
send_wont(TELOPT_ENCRYPT);
myopts[TELOPT_ENCRYPT] = remopts[TELOPT_ENCRYPT] = OPT_NO;
encr_data.encrypt.autoflag = 0;
} else if (result != AUTH_REJECT &&
myopts[TELOPT_ENCRYPT] == OPT_YES &&
remopts[TELOPT_ENCRYPT] == OPT_YES) {
write_data("%c%c%c%c%c%c%c",
(uchar_t)IAC,
(uchar_t)SB,
(uchar_t)TELOPT_ENCRYPT,
(uchar_t)ENCRYPT_SUPPORT,
(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
(uchar_t)IAC,
(uchar_t)SE);
netflush();
sent_encrypt_support = B_TRUE;
if (enc_debug)
(void) fprintf(stderr,
"SENT ENCRYPT SUPPORT\n");
(void) encrypt_send_encrypt_is();
}
auth_status = result;
settimer(authdone);
}
static void
reply_to_client(AuthInfo *ap, int type, void *data, int len)
{
uchar_t reply[BUFSIZ];
uchar_t *p = reply;
uchar_t *cd = (uchar_t *)data;
if (len == -1 && data != NULL)
len = strlen((char *)data);
else if (len > (sizeof (reply) - 9)) {
syslog(LOG_ERR,
"krb5 auth reply length too large (%d)", len);
if (auth_debug)
(void) fprintf(stderr,
"krb5 auth reply length too large (%d)\n",
len);
return;
} else if (data == NULL)
len = 0;
*p++ = IAC;
*p++ = SB;
*p++ = TELOPT_AUTHENTICATION;
*p++ = AUTHTYPE_KERBEROS_V5;
*p++ = ap->AuthName;
*p++ = ap->AuthHow;
*p++ = type;
while (len-- > 0) {
if ((*p++ = *cd++) == IAC)
*p++ = IAC;
}
*p++ = IAC;
*p++ = SE;
write_data_len((const char *)reply, p-reply);
#if defined(AUTHTYPE_NAMES) && defined(AUTHWHO_STR) &&\
defined(AUTHHOW_NAMES) && defined(AUTHRSP_NAMES)
if (auth_debug) {
(void) fprintf(stderr, "SENT TELOPT_AUTHENTICATION REPLY "
"%s %s|%s %s\n",
AUTHTYPE_NAME(ap->AuthName),
AUTHWHO_NAME(ap->AuthHow & AUTH_WHO_MASK),
AUTHHOW_NAME(ap->AuthHow & AUTH_HOW_MASK),
AUTHRSP_NAME(type));
}
#endif
netflush();
}
static krb5_error_code
rd_and_store_forwarded_creds(krb5_context context,
krb5_auth_context auth_context,
krb5_data *inbuf, krb5_ticket *ticket,
char *username)
{
krb5_creds **creds;
krb5_error_code retval;
char ccname[MAXPATHLEN];
krb5_ccache ccache = NULL;
char *client_name = NULL;
if (retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))
return (retval);
(void) sprintf(ccname, "FILE:/tmp/krb5cc_p%ld", getpid());
(void) krb5_setenv("KRB5CCNAME", ccname, 1);
if ((retval = krb5_cc_default(context, &ccache)))
goto cleanup;
if ((retval = krb5_cc_initialize(context, ccache,
ticket->enc_part2->client)) != 0)
goto cleanup;
if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0)
goto cleanup;
if ((retval = krb5_cc_close(context, ccache)) != 0)
goto cleanup;
if ((retval = krb5_unparse_name(context, (*creds)->client,
&client_name)) != 0)
goto cleanup;
(void) kwarn_del_warning(client_name);
if (kwarn_add_warning(client_name, (*creds)->times.endtime) != 0) {
syslog(LOG_AUTH|LOG_NOTICE,
"rd_and_store_forwarded_creds: kwarn_add_warning"
" failed: ktkt_warnd(8) down? ");
if (auth_debug)
(void) fprintf(stderr,
"kwarn_add_warning failed:"
" ktkt_warnd(8) down?\n");
}
free(client_name);
client_name = NULL;
if (username != NULL) {
(void) krb5_kuserok(context, ticket->enc_part2->client,
username);
}
if (auth_debug)
(void) fprintf(stderr,
"Successfully stored forwarded creds\n");
cleanup:
krb5_free_creds(context, *creds);
return (retval);
}
static void
kerberos5_is(AuthInfo *ap, uchar_t *data, int cnt)
{
krb5_error_code err = 0;
krb5_principal server;
krb5_keyblock *newkey = NULL;
krb5_keytab keytabid = 0;
krb5_data outbuf;
krb5_data inbuf;
krb5_authenticator *authenticator;
char errbuf[MAXERRSTRLEN];
char *name;
krb5_data auth;
Session_Key skey;
if (cnt-- < 1)
return;
switch (*data++) {
case KRB_AUTH:
auth.data = (char *)data;
auth.length = cnt;
if (auth_context == NULL) {
err = krb5_auth_con_init(telnet_context, &auth_context);
if (err)
syslog(LOG_ERR,
"Error getting krb5 auth "
"context: %s", error_message(err));
}
if (!err) {
krb5_rcache rcache;
err = krb5_auth_con_getrcache(telnet_context,
auth_context,
&rcache);
if (!err && !rcache) {
err = krb5_sname_to_principal(telnet_context,
0, 0,
KRB5_NT_SRV_HST,
&server);
if (!err) {
err = krb5_get_server_rcache(
telnet_context,
krb5_princ_component(
telnet_context,
server, 0),
&rcache);
krb5_free_principal(telnet_context,
server);
}
}
if (err)
syslog(LOG_ERR,
"Error allocating krb5 replay cache: %s",
error_message(err));
else {
err = krb5_auth_con_setrcache(telnet_context,
auth_context,
rcache);
if (err)
syslog(LOG_ERR,
"Error creating krb5 "
"replay cache: %s",
error_message(err));
}
}
if (!err && telnet_srvtab != NULL)
err = krb5_kt_resolve(telnet_context,
telnet_srvtab, &keytabid);
if (!err)
err = krb5_rd_req(telnet_context, &auth_context, &auth,
NULL, keytabid, NULL, &ticket);
if (err) {
(void) snprintf(errbuf, sizeof (errbuf),
"Error reading krb5 auth information:"
" %s", error_message(err));
goto errout;
}
if (krb5_princ_component(telnet_context,
ticket->server, 0)->length < MAXPRINCLEN) {
char princ[MAXPRINCLEN];
(void) strncpy(princ,
krb5_princ_component(telnet_context,
ticket->server, 0)->data,
krb5_princ_component(telnet_context,
ticket->server, 0)->length);
princ[krb5_princ_component(telnet_context,
ticket->server, 0)->length] = '\0';
if (strcmp("host", princ)) {
if (strlen(princ) < sizeof (errbuf) - 39) {
(void) snprintf(errbuf, sizeof (errbuf),
"incorrect service "
"name: \"%s\" != "
"\"host\"",
princ);
} else {
(void) strncpy(errbuf,
"incorrect service "
"name: principal != "
"\"host\"",
sizeof (errbuf));
}
goto errout;
}
} else {
(void) strlcpy(errbuf, "service name too long",
sizeof (errbuf));
goto errout;
}
err = krb5_auth_con_getauthenticator(telnet_context,
auth_context,
&authenticator);
if (err) {
(void) snprintf(errbuf, sizeof (errbuf),
"Failed to get authenticator: %s",
error_message(err));
goto errout;
}
if ((ap->AuthHow & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
!authenticator->checksum) {
(void) strlcpy(errbuf,
"authenticator is missing checksum",
sizeof (errbuf));
goto errout;
}
if (authenticator->checksum) {
char type_check[2];
krb5_checksum *cksum = authenticator->checksum;
krb5_keyblock *key;
krb5_data input;
krb5_boolean valid;
type_check[0] = ap->AuthName;
type_check[1] = ap->AuthHow;
err = krb5_auth_con_getkey(telnet_context,
auth_context, &key);
if (err) {
(void) snprintf(errbuf, sizeof (errbuf),
"Failed to get key from "
"authenticator: %s",
error_message(err));
goto errout;
}
input.data = type_check;
input.length = 2;
err = krb5_c_verify_checksum(telnet_context,
key, 0,
&input,
cksum,
&valid);
if (!err && !valid)
err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
if (err) {
(void) snprintf(errbuf, sizeof (errbuf),
"Kerberos checksum "
"verification failed: "
"%s",
error_message(err));
goto errout;
}
krb5_free_keyblock(telnet_context, key);
}
krb5_free_authenticator(telnet_context, authenticator);
if ((ap->AuthHow & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
if ((err = krb5_mk_rep(telnet_context, auth_context,
&outbuf))) {
(void) snprintf(errbuf, sizeof (errbuf),
"Failed to make "
"Kerberos auth reply: "
"%s",
error_message(err));
goto errout;
}
reply_to_client(ap, KRB_RESPONSE, outbuf.data,
outbuf.length);
}
if (krb5_unparse_name(telnet_context,
ticket->enc_part2->client,
&name))
name = 0;
reply_to_client(ap, KRB_ACCEPT, name, name ? -1 : 0);
if (auth_debug) {
syslog(LOG_NOTICE,
"\tKerberos5 identifies user as ``%s''\r\n",
name ? name : "");
}
if (name != NULL) {
krb5_name = (char *)strdup(name);
}
auth_finished(ap, AUTH_USER);
if (name != NULL)
free(name);
(void) krb5_auth_con_getremotesubkey(telnet_context,
auth_context, &newkey);
if (session_key != NULL) {
krb5_free_keyblock(telnet_context, session_key);
session_key = 0;
}
if (newkey != NULL) {
(void) krb5_copy_keyblock(telnet_context,
newkey, &session_key);
krb5_free_keyblock(telnet_context, newkey);
} else {
(void) krb5_copy_keyblock(telnet_context,
ticket->enc_part2->session, &session_key);
}
skey.type = SK_DES;
skey.length = DES_BLOCKSIZE;
skey.data = session_key->contents;
encrypt_session_key(&skey, &encr_data.encrypt);
encrypt_session_key(&skey, &encr_data.decrypt);
break;
case KRB_FORWARD:
inbuf.length = cnt;
inbuf.data = (char *)data;
if (auth_debug)
(void) fprintf(stderr,
"RCVD KRB_FORWARD data (%d bytes)\n", cnt);
if (auth_context != NULL) {
krb5_rcache rcache;
err = krb5_auth_con_getrcache(telnet_context,
auth_context, &rcache);
if (!err && !rcache) {
err = krb5_sname_to_principal(telnet_context,
0, 0, KRB5_NT_SRV_HST, &server);
if (!err) {
err = krb5_get_server_rcache(
telnet_context,
krb5_princ_component(
telnet_context,
server, 0),
&rcache);
krb5_free_principal(telnet_context,
server);
}
}
if (err) {
syslog(LOG_ERR,
"Error allocating krb5 replay cache: %s",
error_message(err));
} else {
err = krb5_auth_con_setrcache(telnet_context,
auth_context, rcache);
if (err)
syslog(LOG_ERR,
"Error creating krb5 replay cache:"
" %s",
error_message(err));
}
}
if (!(err = krb5_auth_con_setaddrs(telnet_context, auth_context,
NULL, &rsaddr)))
err = krb5_auth_con_setports(telnet_context,
auth_context, NULL, &rsport);
if (err == 0)
err = rd_and_store_forwarded_creds(telnet_context,
auth_context, &inbuf,
ticket,
AuthenticatingUser);
if (err) {
(void) snprintf(errbuf, sizeof (errbuf),
"Read forwarded creds failed: %s",
error_message(err));
syslog(LOG_ERR, "%s", errbuf);
reply_to_client(ap, KRB_FORWARD_REJECT, errbuf, -1);
if (auth_debug)
(void) fprintf(stderr,
"\tCould not read "
"forwarded credentials\r\n");
} else
reply_to_client(ap, KRB_FORWARD_ACCEPT, (void *) 0, 0);
if (rsaddr.contents != NULL)
free(rsaddr.contents);
if (rsport.contents != NULL)
free(rsport.contents);
if (auth_debug)
(void) fprintf(stderr, "\tForwarded "
"credentials obtained\r\n");
break;
default:
if (auth_debug)
(void) fprintf(stderr,
"\tUnknown Kerberos option %d\r\n",
data[-1]);
reply_to_client(ap, KRB_REJECT, (void *) 0, 0);
break;
}
return;
errout:
reply_to_client(ap, KRB_REJECT, errbuf, -1);
if (auth_debug)
(void) fprintf(stderr, "\tKerberos V5 error: %s\r\n", errbuf);
syslog(LOG_ERR, "%s", errbuf);
if (auth_context != NULL) {
(void) krb5_auth_con_free(telnet_context, auth_context);
auth_context = 0;
}
}
static int
krb5_init()
{
int code = 0;
if (telnet_context == NULL) {
code = krb5_init_context(&telnet_context);
if (code != 0 && auth_debug)
syslog(LOG_NOTICE,
"Cannot initialize Kerberos V5: %s",
error_message(code));
}
return (code);
}
static void
auth_name(uchar_t *data, int cnt)
{
char namebuf[MAXPRINCLEN];
if (cnt < 1) {
if (auth_debug)
(void) fprintf(stderr,
"\t(auth_name) Empty NAME in auth "
"reply\n");
return;
}
if (cnt > sizeof (namebuf)-1) {
if (auth_debug)
(void) fprintf(stderr,
"\t(auth_name) NAME exceeds %d bytes\n",
sizeof (namebuf)-1);
return;
}
(void) memcpy((void *)namebuf, (void *)data, cnt);
namebuf[cnt] = 0;
if (auth_debug)
(void) fprintf(stderr, "\t(auth_name) name [%s]\n", namebuf);
AuthenticatingUser = (char *)strdup(namebuf);
}
static void
auth_is(uchar_t *data, int cnt)
{
AuthInfo *aptr = auth_list;
if (cnt < 2)
return;
if (data[0] == AUTHTYPE_NULL) {
auth_finished(0, AUTH_REJECT);
return;
}
while (aptr->AuthName != 0 &&
(aptr->AuthName != data[0] || aptr->AuthHow != data[1]))
aptr++;
if (aptr != NULL) {
if (auth_debug)
(void) fprintf(stderr, "\t(auth_is) auth type is %s "
"(%d bytes)\n", aptr->AuthString, cnt);
if (aptr->AuthName == AUTHTYPE_KERBEROS_V5)
kerberos5_is(aptr, data+2, cnt-2);
}
}
static int
krb5_user_status(char *name, int namelen, int level)
{
int retval = AUTH_USER;
if (auth_debug)
(void) fprintf(stderr, "\t(krb5_user_status) level = %d "
"auth_level = %d user = %s\n",
level, auth_level,
(AuthenticatingUser != NULL ? AuthenticatingUser : ""));
if (level < AUTH_USER)
return (level);
if (AuthenticatingUser != NULL &&
(retval = krb5_kuserok(telnet_context, ticket->enc_part2->client,
AuthenticatingUser))) {
(void) strncpy(name, AuthenticatingUser, namelen);
return (AUTH_VALID);
} else {
if (!retval)
syslog(LOG_ERR,
"Krb5 principal lacks permission to "
"access local account for %s",
AuthenticatingUser);
return (AUTH_USER);
}
}
static int
getrandom(char *buf, int buflen)
{
static int devrandom = -1;
if (devrandom == -1 &&
(devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
fatalperror(net, "Unable to open /dev/urandom: ",
errno);
return (-1);
}
if (read(devrandom, buf, buflen) == -1) {
fatalperror(net, "Unable to read from /dev/urandom: ",
errno);
return (-1);
}
return (0);
}
static void
encrypt_init()
{
(void) memset(&encr_data.encrypt, 0, sizeof (cipher_info_t));
(void) memset(&encr_data.decrypt, 0, sizeof (cipher_info_t));
encr_data.encrypt.state = ENCR_STATE_NOT_READY;
encr_data.decrypt.state = ENCR_STATE_NOT_READY;
}
static void
encrypt_send_request_start()
{
uchar_t buf[6+TELNET_MAXKEYIDLEN], *p;
p = buf;
*p++ = IAC;
*p++ = SB;
*p++ = TELOPT_ENCRYPT;
*p++ = ENCRYPT_REQSTART;
(void) memcpy(p, encr_data.decrypt.keyid, encr_data.decrypt.keyidlen);
p += encr_data.decrypt.keyidlen;
*p++ = IAC;
*p++ = SE;
write_data_len((const char *)buf, p-buf);
netflush();
if (enc_debug)
(void) fprintf(stderr,
"SENT TELOPT_ENCRYPT ENCRYPT_REQSTART\n");
}
static void
encrypt_is(uchar_t *data, int cnt)
{
register int type;
register int iv_status = CFB64_IV_OK;
register int lstate = 0;
uchar_t sbbuf[] = {
(uchar_t)IAC,
(uchar_t)SB,
(uchar_t)TELOPT_ENCRYPT,
(uchar_t)ENCRYPT_REPLY,
(uchar_t)0,
(uchar_t)CFB64_IV_OK,
(uchar_t)IAC,
(uchar_t)SE,
};
if (--cnt < 0)
return;
type = sbbuf[4] = *data++;
switch (type) {
case TELOPT_ENCTYPE_DES_CFB64:
encr_data.decrypt.type = type;
lstate = encr_data.decrypt.state;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_is) initial state = %d\n",
lstate);
if (cnt < sizeof (Block)) {
iv_status = CFB64_IV_BAD;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_is) Not enough "
"IV bytes\n");
lstate = ENCR_STATE_NOT_READY;
} else {
data++;
(void) memcpy(encr_data.decrypt.ivec, data,
sizeof (Block));
lstate = ENCR_STATE_IN_PROGRESS;
}
break;
case TELOPT_ENCTYPE_NULL:
encr_data.decrypt.type = type;
lstate &= ~ENCR_STATE_NO_RECV_IV;
lstate &= ~ENCR_STATE_NO_SEND_IV;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_is) We accept NULL encr\n");
break;
default:
iv_status = CFB64_IV_BAD;
encr_data.decrypt.type = 0;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_is) Can't find type (%d) "
"for initial negotiation\r\n",
type);
lstate = ENCR_STATE_NOT_READY;
break;
}
sbbuf[5] = (uchar_t)iv_status;
if (iv_status == CFB64_IV_OK) {
lstate &= ~ENCR_STATE_NO_RECV_IV;
lstate &= ~ENCR_STATE_NO_SEND_IV;
} else {
lstate = ENCR_STATE_NOT_READY;
}
write_data_len((const char *)sbbuf, sizeof (sbbuf));
netflush();
#ifdef ENCRYPT_NAMES
if (enc_debug)
(void) fprintf(stderr,
"SENT TELOPT_ENCRYPT ENCRYPT_REPLY %s %s\n",
ENCTYPE_NAME(type),
(iv_status == CFB64_IV_OK ? "CFB64_IV_OK" :
"CFB64_IV_BAD"));
#endif
encr_data.decrypt.state = lstate;
if (lstate == ENCR_STATE_NOT_READY)
encr_data.decrypt.autoflag = 0;
else {
if (lstate == ENCR_STATE_OK && encr_data.decrypt.autoflag)
encrypt_send_request_start();
}
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_is) final DECRYPT state = %d\n",
encr_data.decrypt.state);
}
static int
encrypt_send_encrypt_is()
{
register int lstate;
krb5_error_code kret;
uchar_t sbbuf[MAXOPTLEN], *p;
int i;
lstate = encr_data.encrypt.state;
if (encr_data.encrypt.type == ENCTYPE_NULL) {
return (lstate);
}
if (lstate == ENCR_STATE_NOT_READY)
lstate = ENCR_STATE_IN_PROGRESS;
else if ((lstate & ENCR_STATE_NO_SEND_IV) == 0) {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_send_is) IV already sent,"
" state = %d\n", lstate);
return (lstate);
}
if (!VALIDKEY(encr_data.encrypt.krbdes_key)) {
encr_data.encrypt.need_start = 1;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_send_is) No Key, cannot "
"start encryption yet\n");
return (lstate);
}
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_send_is) Creating new feed\n");
kret = getrandom((char *)encr_data.encrypt.ivec, sizeof (Block));
if (kret) {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_send_is) error from "
"getrandom: %d\n", kret);
syslog(LOG_ERR, "Failed to create encryption key (err %d)\n");
encr_data.encrypt.type = ENCTYPE_NULL;
} else {
mit_des_fixup_key_parity(encr_data.encrypt.ivec);
}
p = sbbuf;
*p++ = IAC;
*p++ = SB;
*p++ = TELOPT_ENCRYPT;
*p++ = ENCRYPT_IS;
*p++ = encr_data.encrypt.type;
*p++ = CFB64_IV;
for (i = 0; i < sizeof (Block); i++)
if ((*p++ = encr_data.encrypt.ivec[i]) == IAC)
*p++ = IAC;
*p++ = IAC;
*p++ = SE;
write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
netflush();
if (!kret) {
lstate &= ~ENCR_STATE_NO_SEND_IV;
lstate &= ~ENCR_STATE_NO_SEND_IV;
}
encr_data.encrypt.state = lstate;
if (enc_debug) {
int i;
(void) fprintf(stderr,
"SENT TELOPT_ENCRYPT ENCRYPT_IS %d CFB64_IV ",
encr_data.encrypt.type);
for (i = 0; i < (p-sbbuf); i++)
(void) fprintf(stderr, "%d ", (int)sbbuf[i]);
(void) fprintf(stderr, "\n");
}
return (lstate);
}
static void
stop_stream(int fd, int dir)
{
struct strioctl crioc;
uint32_t stopdir = dir;
crioc.ic_cmd = CRYPTIOCSTOP;
crioc.ic_timout = -1;
crioc.ic_len = sizeof (stopdir);
crioc.ic_dp = (char *)&stopdir;
if (ioctl(fd, I_STR, &crioc)) {
syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
}
}
static void
start_stream(int fd, int dir, int datalen, char *data)
{
struct strioctl crioc;
crioc.ic_cmd = (dir == CRYPT_ENCRYPT ? CRYPTIOCSTARTENC :
CRYPTIOCSTARTDEC);
crioc.ic_timout = -1;
crioc.ic_len = datalen;
crioc.ic_dp = data;
if (ioctl(fd, I_STR, &crioc)) {
syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
}
}
static void
encrypt_start_output()
{
int lstate;
uchar_t *p;
uchar_t sbbuf[MAXOPTLEN];
struct strioctl crioc;
struct cr_info_t cki;
lstate = encrypt_send_encrypt_is();
if (lstate != ENCR_STATE_OK) {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start_output) ENCRYPT state "
"= %d\n", lstate);
return;
}
p = sbbuf;
*p++ = IAC;
*p++ = SB;
*p++ = TELOPT_ENCRYPT;
*p++ = ENCRYPT_START;
(void) memcpy(p, encr_data.encrypt.keyid, encr_data.encrypt.keyidlen);
p += encr_data.encrypt.keyidlen;
*p++ = IAC;
*p++ = SE;
write_data_len((const char *)sbbuf, (int)(p-sbbuf));
netflush();
if (enc_debug)
(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_START %d "
"(lstate = %d) data waiting = %d\n",
(int)encr_data.encrypt.keyid[0],
lstate, nfrontp-nbackp);
encr_data.encrypt.state = lstate;
cki.direction_mask = CRYPT_ENCRYPT;
if (encr_data.encrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
cki.crypto_method = CRYPT_METHOD_DES_CFB;
} else {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start_output) - unknown "
"crypto_method %d\n",
encr_data.encrypt.type);
syslog(LOG_ERR, "unrecognized crypto encrypt method: %d",
encr_data.encrypt.type);
return;
}
if (encr_data.encrypt.setup == cki.crypto_method) {
cki.keylen = 0;
cki.iveclen = 0;
} else {
cki.keylen = DES_BLOCKSIZE;
(void) memcpy(cki.key, (void *)encr_data.encrypt.krbdes_key,
DES_BLOCKSIZE);
cki.iveclen = DES_BLOCKSIZE;
(void) memcpy(cki.ivec, (void *)encr_data.encrypt.ivec,
DES_BLOCKSIZE);
cki.ivec_usage = IVEC_ONETIME;
}
cki.option_mask = 0;
stop_stream(cryptmod_fd, CRYPT_ENCRYPT);
crioc.ic_cmd = CRYPTIOCSETUP;
crioc.ic_timout = -1;
crioc.ic_len = sizeof (struct cr_info_t);
crioc.ic_dp = (char *)&cki;
if (ioctl(cryptmod_fd, I_STR, &crioc)) {
perror("ioctl(CRYPTIOCSETUP) [encrypt_start_output] error");
} else {
encr_data.encrypt.setup = cki.crypto_method;
}
start_stream(cryptmod_fd, CRYPT_ENCRYPT, 0, NULL);
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start_output) Encrypting output\n");
}
static void
encrypt_request_start(void)
{
if (encr_data.encrypt.type == ENCTYPE_NULL) {
encr_data.encrypt.autoflag = 1;
if (enc_debug)
(void) fprintf(stderr, "\t(encrypt_request_start) "
"autoencrypt = ON\n");
} else {
encrypt_start_output();
}
}
static void
encrypt_end(int direction)
{
struct cr_info_t cki;
struct strioctl crioc;
uint32_t stopdir;
stopdir = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
CRYPT_ENCRYPT);
stop_stream(cryptmod_fd, stopdir);
cki.direction_mask = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
CRYPT_ENCRYPT);
cki.crypto_method = CRYPT_METHOD_NONE;
cki.option_mask = 0;
cki.keylen = 0;
cki.iveclen = 0;
cki.ivec_usage = IVEC_ONETIME;
crioc.ic_cmd = CRYPTIOCSETUP;
crioc.ic_timout = -1;
crioc.ic_len = sizeof (cki);
crioc.ic_dp = (char *)&cki;
if (ioctl(cryptmod_fd, I_STR, &crioc)) {
perror("ioctl(CRYPTIOCSETUP) [encrypt_end] error");
}
start_stream(cryptmod_fd, stopdir, 0, NULL);
}
static void
encrypt_request_end()
{
write_data("%c%c%c%c%c%c",
(uchar_t)IAC,
(uchar_t)SB,
(uchar_t)TELOPT_ENCRYPT,
(uchar_t)ENCRYPT_END,
(uchar_t)IAC,
(uchar_t)SE);
netflush();
if (enc_debug)
(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_END\n");
encrypt_end(TELNET_DIR_ENCRYPT);
}
static void
encrypt_send_request_end()
{
write_data("%c%c%c%c%c%c",
(uchar_t)IAC,
(uchar_t)SB,
(uchar_t)TELOPT_ENCRYPT,
(uchar_t)ENCRYPT_REQEND,
(uchar_t)IAC,
(uchar_t)SE);
netflush();
if (enc_debug)
(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_REQEND\n");
}
static void
encrypt_start(void)
{
struct cr_info_t cki;
struct strioctl crioc;
int bytes = 0;
char *dataptr = NULL;
if (encr_data.decrypt.type == ENCTYPE_NULL) {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start) No DECRYPT method "
"defined yet\n");
encrypt_send_request_end();
return;
}
cki.direction_mask = CRYPT_DECRYPT;
if (encr_data.decrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
cki.crypto_method = CRYPT_METHOD_DES_CFB;
} else {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start) - unknown "
"crypto_method %d\n", encr_data.decrypt.type);
syslog(LOG_ERR, "unrecognized crypto decrypt method: %d",
encr_data.decrypt.type);
return;
}
if (encr_data.decrypt.setup != cki.crypto_method) {
(void) memcpy(cki.key, (void *)encr_data.decrypt.krbdes_key,
DES_BLOCKSIZE);
(void) memcpy(cki.ivec, (void *)encr_data.decrypt.ivec,
DES_BLOCKSIZE);
cki.keylen = DES_BLOCKSIZE;
cki.iveclen = DES_BLOCKSIZE;
cki.ivec_usage = IVEC_ONETIME;
} else {
cki.keylen = 0;
cki.iveclen = 0;
}
cki.option_mask = 0;
stop_stream(cryptmod_fd, CRYPT_DECRYPT);
crioc.ic_cmd = CRYPTIOCSETUP;
crioc.ic_timout = -1;
crioc.ic_len = sizeof (struct cr_info_t);
crioc.ic_dp = (char *)&cki;
if (ioctl(cryptmod_fd, I_STR, &crioc)) {
syslog(LOG_ERR, "ioctl(CRYPTIOCSETUP) [encrypt_start] "
"error: %m");
} else {
encr_data.decrypt.setup = cki.crypto_method;
}
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start) called CRYPTIOCSETUP for "
"decrypt side\n");
if (ioctl(cryptmod_fd, I_NREAD, &bytes) < 0) {
syslog(LOG_ERR, "I_NREAD returned error %m");
bytes = 0;
}
if (ncc || bytes) {
drainstream(bytes);
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_start) after drainstream, "
"ncc=%d bytes = %d\n", ncc, bytes);
bytes += ncc;
dataptr = netip;
}
start_stream(cryptmod_fd, CRYPT_DECRYPT, bytes, dataptr);
ncc = 0;
netip = netibuf;
(void) memset(netip, 0, netibufsize);
#ifdef ENCRYPT_NAMES
if (enc_debug) {
(void) fprintf(stderr,
"\t(encrypt_start) Start DECRYPT using %s\n",
ENCTYPE_NAME(encr_data.decrypt.type));
}
#endif
}
static void
encrypt_support(char *data, int cnt)
{
int lstate = ENCR_STATE_NOT_READY;
int type, use_type = 0;
while (cnt-- > 0 && use_type == 0) {
type = *data++;
#ifdef ENCRYPT_NAMES
if (enc_debug)
(void) fprintf(stderr,
"RCVD ENCRYPT SUPPORT %s\n",
ENCTYPE_NAME(type));
#endif
if (type == TELOPT_ENCTYPE_DES_CFB64) {
use_type = type;
}
}
encr_data.encrypt.type = use_type;
if (use_type != TELOPT_ENCTYPE_NULL &&
authenticated != NULL && authenticated != &NoAuth &&
auth_status != AUTH_REJECT) {
lstate = encrypt_send_encrypt_is();
if (lstate == ENCR_STATE_OK)
encrypt_start_output();
} else if (use_type == TELOPT_ENCTYPE_NULL) {
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_support) Cannot agree "
"on crypto algorithm, output encryption "
"disabled.\n");
write_data("%c%c%c%c%c%c%c",
(uchar_t)IAC,
(uchar_t)SB,
(uchar_t)TELOPT_ENCRYPT,
(uchar_t)ENCRYPT_IS,
(uchar_t)TELOPT_ENCTYPE_NULL,
(uchar_t)IAC,
(uchar_t)SE);
send_wont(TELOPT_ENCRYPT);
netflush();
if (enc_debug)
(void) fprintf(stderr,
"SENT TELOPT_ENCRYPT ENCRYPT_IS "
"[NULL]\n");
remopts[TELOPT_ENCRYPT] = OPT_NO;
}
settimer(encr_support);
}
static void
encrypt_send_keyid(int dir, uchar_t *keyid, int keylen, boolean_t saveit)
{
uchar_t sbbuf[128], *p;
p = sbbuf;
*p++ = IAC;
*p++ = SB;
*p++ = TELOPT_ENCRYPT;
*p++ = (dir == TELNET_DIR_ENCRYPT ? ENCRYPT_ENC_KEYID :
ENCRYPT_DEC_KEYID);
if (saveit) {
if (enc_debug)
(void) fprintf(stderr,
"\t(send_keyid) store %d byte %s keyid\n",
keylen,
(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
"DECRYPT"));
if (dir == TELNET_DIR_ENCRYPT) {
(void) memcpy(encr_data.encrypt.keyid, keyid, keylen);
encr_data.encrypt.keyidlen = keylen;
} else {
(void) memcpy(encr_data.decrypt.keyid, keyid, keylen);
encr_data.decrypt.keyidlen = keylen;
}
}
(void) memcpy(p, keyid, keylen);
p += keylen;
*p++ = IAC;
*p++ = SE;
write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
netflush();
if (enc_debug)
(void) fprintf(stderr, "SENT TELOPT_ENCRYPT %s %d\n",
(dir == TELNET_DIR_ENCRYPT ? "ENC_KEYID" :
"DEC_KEYID"), keyid[0]);
}
static void
encrypt_reply(char *data, int len)
{
uchar_t type = (uchar_t)(*data++);
uchar_t result = (uchar_t)(*data);
int lstate;
#ifdef ENCRYPT_NAMES
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_reply) ENCRYPT REPLY %s %s [len=%d]\n",
ENCRYPT_NAME(type),
(result == CFB64_IV_OK ? "CFB64_IV_OK" :
"CFB64_IV_BAD"), len);
#endif
lstate = encr_data.encrypt.state;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_reply) initial ENCRYPT state = %d\n",
lstate);
switch (result) {
case CFB64_IV_OK:
if (lstate == ENCR_STATE_NOT_READY)
lstate = ENCR_STATE_IN_PROGRESS;
lstate &= ~ENCR_STATE_NO_RECV_IV;
lstate &= ~ENCR_STATE_NO_SEND_IV;
encrypt_send_keyid(TELNET_DIR_ENCRYPT, (uchar_t *)"\0", 1, 1);
break;
case CFB64_IV_BAD:
(void) memset(encr_data.encrypt.ivec, 0, sizeof (Block));
lstate = ENCR_STATE_NOT_READY;
break;
default:
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_reply) Got unknown IV value in "
"REPLY message\n");
lstate = ENCR_STATE_NOT_READY;
break;
}
encr_data.encrypt.state = lstate;
if (lstate == ENCR_STATE_NOT_READY) {
encr_data.encrypt.autoflag = 0;
encr_data.encrypt.type = ENCTYPE_NULL;
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_reply) encrypt.autoflag = "
"OFF\n");
} else {
encr_data.encrypt.type = type;
if ((lstate == ENCR_STATE_OK) && encr_data.encrypt.autoflag)
encrypt_start_output();
}
if (enc_debug)
(void) fprintf(stderr,
"\t(encrypt_reply) ENCRYPT final state = %d\n",
lstate);
}
static void
encrypt_set_keyid_state(uchar_t *keyid, int *keyidlen, int dir)
{
int lstate;
lstate = (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state :
encr_data.decrypt.state);
if (enc_debug)
(void) fprintf(stderr,
"\t(set_keyid_state) %s initial state = %d\n",
(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
"DECRYPT"), lstate);
if (*keyidlen != 1 || (*keyid != '\0')) {
if (enc_debug)
(void) fprintf(stderr,
"\t(set_keyid_state) unexpected keyid: "
"len=%d value=%d\n", *keyidlen, *keyid);
*keyidlen = 0;
syslog(LOG_ERR, "rcvd unexpected keyid %d - only keyid of 0 "
"is supported", *keyid);
} else {
if (lstate == ENCR_STATE_NOT_READY)
lstate = ENCR_STATE_IN_PROGRESS;
lstate &= ~ENCR_STATE_NO_KEYID;
}
if (enc_debug)
(void) fprintf(stderr,
"\t(set_keyid_state) %s final state = %d\n",
(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
"DECRYPT"), lstate);
if (dir == TELNET_DIR_ENCRYPT)
encr_data.encrypt.state = lstate;
else
encr_data.decrypt.state = lstate;
}
static void
encrypt_keyid(uchar_t *newkeyid, int *keyidlen, uchar_t *keyid,
int len, int dir)
{
if (len > TELNET_MAXNUMKEYS) {
if (enc_debug)
(void) fprintf(stderr,
"\t(keyid) keylen too big (%d)\n", len);
return;
}
if (enc_debug) {
(void) fprintf(stderr, "\t(keyid) set KEYID for %s len = %d\n",
(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
"DECRYPT"), len);
}
if (len == 0) {
if (*keyidlen == 0) {
if (enc_debug)
(void) fprintf(stderr,
"\t(keyid) Got 0 length keyid - "
"failure\n");
return;
}
*keyidlen = 0;
encrypt_set_keyid_state(newkeyid, keyidlen, dir);
} else if (len != *keyidlen || memcmp(keyid, newkeyid, len)) {
if (enc_debug)
(void) fprintf(stderr,
"\t(keyid) Setting new key (%d bytes)\n",
len);
*keyidlen = len;
(void) memcpy(newkeyid, keyid, len);
encrypt_set_keyid_state(newkeyid, keyidlen, dir);
} else {
encrypt_set_keyid_state(newkeyid, keyidlen, dir);
if (enc_debug)
(void) fprintf(stderr,
"\t(keyid) %s Key already in place,"
"state = %d autoflag=%d\n",
(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" : "DECRYPT"),
(dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state:
encr_data.decrypt.state),
(dir == TELNET_DIR_ENCRYPT ?
encr_data.encrypt.autoflag:
encr_data.decrypt.autoflag));
if ((encr_data.encrypt.state == ENCR_STATE_OK) &&
dir == TELNET_DIR_ENCRYPT && encr_data.encrypt.autoflag) {
encrypt_start_output();
}
return;
}
if (enc_debug)
(void) fprintf(stderr, "\t(keyid) %s final state = %d\n",
(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
"DECRYPT"),
(dir == TELNET_DIR_ENCRYPT ?
encr_data.encrypt.state :
encr_data.decrypt.state));
encrypt_send_keyid(dir, newkeyid, *keyidlen, 0);
}
static void
encrypt_enc_keyid(char *data, int cnt)
{
encrypt_keyid(encr_data.decrypt.keyid, &encr_data.decrypt.keyidlen,
(uchar_t *)data, cnt, TELNET_DIR_DECRYPT);
}
static void
encrypt_dec_keyid(char *data, int cnt)
{
encrypt_keyid(encr_data.encrypt.keyid, &encr_data.encrypt.keyidlen,
(uchar_t *)data, cnt, TELNET_DIR_ENCRYPT);
}
static void
encrypt_session_key(Session_Key *key, cipher_info_t *cinfo)
{
if (key == NULL || key->type != SK_DES) {
if (enc_debug)
(void) fprintf(stderr,
"\t(session_key) Cannot set krb5 "
"session key (unknown type = %d)\n",
key ? key->type : -1);
}
if (enc_debug)
(void) fprintf(stderr,
"\t(session_key) Settting session key "
"for server\n");
(void) memcpy(cinfo->krbdes_key, (void *)key->data, sizeof (Block));
if (cinfo->need_start) {
if (encrypt_send_encrypt_is() == ENCR_STATE_OK) {
cinfo->need_start = 0;
}
}
}
static int
new_env(const char *name, const char *value)
{
struct envlist *env;
env = malloc(sizeof (struct envlist));
if (env == NULL)
return (1);
if ((env->name = strdup(name)) == NULL) {
free(env);
return (1);
}
if ((env->value = strdup(value)) == NULL) {
free(env->name);
free(env);
return (1);
}
env->delete = 0;
env->next = envlist_head;
envlist_head = env;
return (0);
}
static int
del_env(const char *name)
{
struct envlist *env;
for (env = envlist_head; env; env = env->next) {
if (strcmp(env->name, name) == 0) {
env->delete = 1;
break;
}
}
return (0);
}
static int
issock(int fd)
{
struct stat stats;
if (fstat(fd, &stats) == -1)
return (0);
return (S_ISSOCK(stats.st_mode));
}
static int
audit_telnet_settid(int sock) {
adt_session_data_t *ah;
adt_termid_t *termid;
int rc;
if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
if ((rc = adt_load_termid(sock, &termid)) == 0) {
if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
termid, ADT_SETTID)) == 0)
(void) adt_set_proc(ah);
free(termid);
}
(void) adt_end_session(ah);
}
return (rc);
}
int
main(int argc, char *argv[])
{
struct sockaddr_storage from;
int on = 1;
socklen_t fromlen;
int issocket;
#if defined(DEBUG)
ushort_t porttouse = 0;
boolean_t standalone = 0;
#endif
extern char *optarg;
int c;
int tos = -1;
while ((c = getopt(argc, argv, TELNETD_OPTS DEBUG_OPTS)) != -1) {
switch (c) {
#if defined(DEBUG)
case 'p':
porttouse = atoi(optarg);
standalone = 1;
break;
case 'e':
enc_debug = 1;
break;
#endif
case 'a':
if (strcasecmp(optarg, "none") == 0) {
auth_level = 0;
} else if (strcasecmp(optarg, "user") == 0) {
auth_level = AUTH_USER;
} else if (strcasecmp(optarg, "valid") == 0) {
auth_level = AUTH_VALID;
} else if (strcasecmp(optarg, "off") == 0) {
auth_level = -1;
negotiate_auth_krb5 = 0;
} else if (strcasecmp(optarg, "debug") == 0) {
auth_debug = 1;
} else {
syslog(LOG_ERR,
"unknown authentication level specified "
"with \'-a\' option (%s)", optarg);
auth_level = AUTH_USER;
}
break;
case 'X':
negotiate_auth_krb5 = 0;
break;
case 'R':
case 'M':
if (optarg != NULL) {
int ret = krb5_init();
if (ret) {
syslog(LOG_ERR,
"Unable to use Kerberos V5 as "
"requested, exiting");
exit(1);
}
(void) krb5_set_default_realm(telnet_context,
optarg);
syslog(LOG_NOTICE,
"using %s as default KRB5 realm", optarg);
}
break;
case 'S':
telnet_srvtab = (char *)strdup(optarg);
break;
case 'E':
negotiate_encrypt = B_FALSE;
break;
case 'U':
resolve_hostname = 1;
break;
case 's':
if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
tos > 255) {
syslog(LOG_ERR, "telnetd: illegal tos value: "
"%s\n", optarg);
} else {
if (tos < 0)
tos = 020;
}
break;
case 'h':
show_hostinfo = 0;
break;
default:
syslog(LOG_ERR, "telnetd: illegal cmd line option %c",
c);
break;
}
}
netibufsize = BUFSIZ;
if (!(netibuf = (char *)malloc(netibufsize)))
syslog(LOG_ERR, "netibuf malloc failed\n");
(void) memset(netibuf, 0, netibufsize);
netip = netibuf;
#if defined(DEBUG)
if (standalone) {
int s, ns, foo;
struct servent *sp;
static struct sockaddr_in6 sin6 = { AF_INET6 };
int option = 1;
if (porttouse) {
sin6.sin6_port = htons(porttouse);
} else {
sp = getservbyname("telnet", "tcp");
if (sp == 0) {
(void) fprintf(stderr,
"telnetd: tcp/telnet: "
"unknown service\n");
exit(EXIT_FAILURE);
}
sin6.sin6_port = sp->s_port;
}
s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (s < 0) {
perror("telnetd: socket");
exit(EXIT_FAILURE);
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&option,
sizeof (option)) == -1)
perror("setsockopt SO_REUSEADDR");
if (bind(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(s, 32) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
(void) signal(SIGCHLD, SIG_IGN);
for (;;) {
pid_t pid;
foo = sizeof (sin6);
ns = accept(s, (struct sockaddr *)&sin6, &foo);
if (ns < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
(void) dup2(ns, 0);
(void) close(s);
(void) signal(SIGCHLD, SIG_DFL);
break;
}
(void) close(ns);
}
}
#endif
openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
issocket = issock(0);
if (!issocket)
fatal(0, "stdin is not a socket file descriptor");
fromlen = (socklen_t)sizeof (from);
(void) memset((char *)&from, 0, sizeof (from));
if (getpeername(0, (struct sockaddr *)&from, &fromlen)
< 0) {
(void) fprintf(stderr, "%s: ", argv[0]);
perror("getpeername");
_exit(EXIT_FAILURE);
}
if (audit_telnet_settid(0)) {
(void) fprintf(stderr, "%s: ", argv[0]);
perror("audit");
exit(EXIT_FAILURE);
}
if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on,
sizeof (on)) < 0) {
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
}
if (tos != -1 &&
setsockopt(0, IPPROTO_IP, IP_TOS,
(char *)&tos, sizeof (tos)) < 0 &&
errno != ENOPROTOOPT) {
syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m", tos);
}
if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
sizeof (on)) < 0) {
syslog(LOG_WARNING, "setsockopt (SO_OOBINLINE): %m");
}
(void) strcpy(pam_svc_name, "telnet");
doit(0, &from);
return (EXIT_SUCCESS);
}
static char *terminaltype = 0;
static void
ttloop(void)
{
if (nfrontp-nbackp) {
netflush();
}
read_again:
ncc = read(net, netibuf, netibufsize);
if (ncc < 0) {
if (errno == EINTR)
goto read_again;
syslog(LOG_INFO, "ttloop: read: %m");
exit(EXIT_FAILURE);
} else if (ncc == 0) {
syslog(LOG_INFO, "ttloop: peer closed connection\n");
exit(EXIT_FAILURE);
}
netip = netibuf;
telrcv();
if (ncc > 0) {
pfrontp = pbackp = ptyobuf;
telrcv();
}
}
static void
send_do(int option)
{
write_data("%c%c%c", (uchar_t)IAC, (uchar_t)DO, (uchar_t)option);
}
static void
send_will(int option)
{
write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WILL, (uchar_t)option);
}
static void
send_wont(int option)
{
write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WONT, (uchar_t)option);
}
static int
getauthtype(char *username, int *len)
{
int init_status = -1;
init_status = krb5_init();
if (auth_level == -1 || init_status != 0) {
remopts[TELOPT_AUTHENTICATION] = OPT_NO;
myopts[TELOPT_AUTHENTICATION] = OPT_NO;
negotiate_auth_krb5 = B_FALSE;
negotiate_encrypt = B_FALSE;
return (AUTH_REJECT);
}
if (init_status == 0 && auth_level != -1) {
if (negotiate_auth_krb5) {
send_do(TELOPT_AUTHENTICATION);
remopts[TELOPT_AUTHENTICATION] =
OPT_YES_BUT_ALWAYS_LOOK;
}
while (sequenceIs(authopt, getauth))
ttloop();
if (remopts[TELOPT_AUTHENTICATION] == OPT_YES) {
uchar_t sbbuf[MAXOPTLEN], *p;
p = sbbuf;
*p++ = (uchar_t)IAC;
*p++ = (uchar_t)SB;
*p++ = (uchar_t)TELOPT_AUTHENTICATION;
*p++ = (uchar_t)TELQUAL_SEND;
if (negotiate_auth_krb5) {
*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
*p++ = (uchar_t)(AUTH_WHO_CLIENT |
AUTH_HOW_MUTUAL |
AUTH_ENCRYPT_ON);
*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
*p++ = (uchar_t)(AUTH_WHO_CLIENT |
AUTH_HOW_MUTUAL);
*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
*p++ = (uchar_t)(AUTH_WHO_CLIENT|
AUTH_HOW_ONE_WAY);
} else {
*p++ = (uchar_t)AUTHTYPE_NULL;
}
*p++ = (uchar_t)IAC;
*p++ = (uchar_t)SE;
write_data_len((const char *)sbbuf,
(size_t)(p - sbbuf));
netflush();
if (auth_debug)
(void) fprintf(stderr,
"SENT TELOPT_AUTHENTICATION "
"[data]\n");
while (sequenceIs(authdone, getauth))
ttloop();
if (authenticated == NULL || authenticated == &NoAuth)
auth_status = AUTH_REJECT;
else {
if (auth_status == AUTH_VALID)
auth_status = AUTH_USER;
if (authenticated->AuthName ==
AUTHTYPE_KERBEROS_V5)
auth_status = krb5_user_status(
username, *len, auth_status);
}
}
}
return (auth_status);
}
static void
getencrtype(void)
{
if (krb5_privacy_allowed() && negotiate_encrypt) {
if (myopts[TELOPT_ENCRYPT] != OPT_YES) {
if (!sent_will_encrypt) {
send_will(TELOPT_ENCRYPT);
sent_will_encrypt = B_TRUE;
}
if (enc_debug)
(void) fprintf(stderr, "SENT WILL ENCRYPT\n");
}
if (remopts[TELOPT_ENCRYPT] != OPT_YES) {
if (!sent_do_encrypt) {
send_do(TELOPT_ENCRYPT);
sent_do_encrypt = B_TRUE;
remopts[TELOPT_ENCRYPT] =
OPT_YES_BUT_ALWAYS_LOOK;
}
if (enc_debug)
(void) fprintf(stderr, "SENT DO ENCRYPT\n");
}
myopts[TELOPT_ENCRYPT] = OPT_YES;
while (sequenceIs(encropt, getencr))
ttloop();
if (auth_status != AUTH_REJECT &&
remopts[TELOPT_ENCRYPT] == OPT_YES &&
myopts[TELOPT_ENCRYPT] == OPT_YES) {
if (sent_encrypt_support == B_FALSE) {
write_data("%c%c%c%c%c%c%c",
(uchar_t)IAC,
(uchar_t)SB,
(uchar_t)TELOPT_ENCRYPT,
(uchar_t)ENCRYPT_SUPPORT,
(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
(uchar_t)IAC,
(uchar_t)SE);
netflush();
}
while (sequenceIs(encr_support, getencr))
ttloop();
}
} else {
settimer(encropt);
remopts[TELOPT_ENCRYPT] = OPT_NO;
myopts[TELOPT_ENCRYPT] = OPT_NO;
}
}
static void
getterminaltype(void)
{
if (sequenceIs(ttypeopt, getterminal)) {
send_do(TELOPT_TTYPE);
remopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
}
if (sequenceIs(nawsopt, getterminal)) {
send_do(TELOPT_NAWS);
remopts[TELOPT_NAWS] = OPT_YES_BUT_ALWAYS_LOOK;
}
if (sequenceIs(xdisplocopt, getterminal)) {
send_do(TELOPT_XDISPLOC);
remopts[TELOPT_XDISPLOC] = OPT_YES_BUT_ALWAYS_LOOK;
}
if (sequenceIs(environopt, getterminal)) {
send_do(TELOPT_NEW_ENVIRON);
remopts[TELOPT_NEW_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
}
if (sequenceIs(oenvironopt, getterminal)) {
send_do(TELOPT_OLD_ENVIRON);
remopts[TELOPT_OLD_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
}
while (auth_status != AUTH_REJECT &&
authenticated != &NoAuth && authenticated != NULL &&
remopts[TELOPT_ENCRYPT] == OPT_YES &&
encr_data.encrypt.autoflag &&
encr_data.encrypt.state != ENCR_STATE_OK) {
if (enc_debug)
(void) fprintf(stderr, "getterminaltype() forcing encrypt\n");
ttloop();
}
if (enc_debug) {
(void) fprintf(stderr, "getterminaltype() encryption %sstarted\n",
encr_data.encrypt.state == ENCR_STATE_OK ? "" : "not ");
}
while (sequenceIs(ttypeopt, getterminal) ||
sequenceIs(nawsopt, getterminal) ||
sequenceIs(xdisplocopt, getterminal) ||
sequenceIs(environopt, getterminal) ||
sequenceIs(oenvironopt, getterminal)) {
ttloop();
}
if (remopts[TELOPT_TTYPE] == OPT_YES) {
static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
(uchar_t)TELOPT_TTYPE, (uchar_t)TELQUAL_SEND,
(uchar_t)IAC, (uchar_t)SE };
write_data_len((const char *)sbbuf, sizeof (sbbuf));
}
if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
(uchar_t)TELOPT_XDISPLOC, (uchar_t)TELQUAL_SEND,
(uchar_t)IAC, (uchar_t)SE };
write_data_len((const char *)sbbuf, sizeof (sbbuf));
}
if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
(uchar_t)TELOPT_NEW_ENVIRON, (uchar_t)TELQUAL_SEND,
(uchar_t)IAC, (uchar_t)SE };
write_data_len((const char *)sbbuf, sizeof (sbbuf));
}
if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
(uchar_t)TELOPT_OLD_ENVIRON, (uchar_t)TELQUAL_SEND,
(uchar_t)IAC, (uchar_t)SE };
write_data_len((const char *)sbbuf, sizeof (sbbuf));
}
if (remopts[TELOPT_TTYPE] == OPT_YES) {
while (sequenceIs(ttypesubopt, getterminal)) {
ttloop();
}
}
if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
while (sequenceIs(xdisplocsubopt, getterminal)) {
ttloop();
}
}
if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
while (sequenceIs(environsubopt, getterminal)) {
ttloop();
}
}
if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
while (sequenceIs(oenvironsubopt, getterminal)) {
ttloop();
}
}
init_neg_done = 1;
}
pid_t pid;
static void
doit(int f, struct sockaddr_storage *who)
{
char *host;
char host_name[MAXHOSTNAMELEN];
int p, t, tt;
struct sgttyb b;
int ptmfd;
int netfd;
struct stat buf;
struct protocol_arg telnetp;
struct strioctl telnetmod;
struct envlist *env, *next;
int nsize = 0;
char abuf[INET6_ADDRSTRLEN];
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
socklen_t wholen;
char username[MAXUSERNAMELEN];
int len;
uchar_t passthru;
char *subsidname;
if ((p = open("/dev/ptmx", O_RDWR | O_NOCTTY)) == -1) {
fatalperror(f, "open /dev/ptmx", errno);
}
if (grantpt(p) == -1)
fatal(f, "could not grant subsidiary pty");
if (unlockpt(p) == -1)
fatal(f, "could not unlock subsidiary pty");
if ((subsidname = ptsname(p)) == NULL)
fatal(f, "could not enable subsidiary pty");
(void) dup2(f, 0);
if ((t = open(subsidname, O_RDWR | O_NOCTTY)) == -1)
fatal(f, "could not open subsidiary pty");
if (ioctl(t, I_PUSH, "ptem") == -1)
fatalperror(f, "ioctl I_PUSH ptem", errno);
if (ioctl(t, I_PUSH, "ldterm") == -1)
fatalperror(f, "ioctl I_PUSH ldterm", errno);
if (ioctl(t, I_PUSH, "ttcompat") == -1)
fatalperror(f, "ioctl I_PUSH ttcompat", errno);
line = subsidname;
pty = t;
if (ioctl(t, TIOCGETP, &b) == -1)
syslog(LOG_INFO, "ioctl TIOCGETP pty t: %m\n");
b.sg_flags = O_CRMOD|O_XTABS|O_ANYP;
b.sg_ispeed = B38400;
b.sg_ospeed = B38400;
if (ioctl(t, TIOCSETN, &b) == -1)
syslog(LOG_INFO, "ioctl TIOCSETN pty t: %m\n");
if (ioctl(pty, TIOCGETP, &b) == -1)
syslog(LOG_INFO, "ioctl TIOCGETP pty pty: %m\n");
b.sg_flags &= ~O_ECHO;
if (ioctl(pty, TIOCSETN, &b) == -1)
syslog(LOG_INFO, "ioctl TIOCSETN pty pty: %m\n");
if (who->ss_family == AF_INET) {
char *addrbuf = NULL;
char *portbuf = NULL;
sin = (struct sockaddr_in *)who;
wholen = sizeof (struct sockaddr_in);
addrbuf = (char *)malloc(wholen);
if (addrbuf == NULL)
fatal(f, "Cannot alloc memory for address info\n");
portbuf = (char *)malloc(sizeof (sin->sin_port));
if (portbuf == NULL) {
free(addrbuf);
fatal(f, "Cannot alloc memory for port info\n");
}
(void) memcpy(addrbuf, (const void *)&sin->sin_addr, wholen);
(void) memcpy(portbuf, (const void *)&sin->sin_port,
sizeof (sin->sin_port));
if (rsaddr.contents != NULL)
free(rsaddr.contents);
rsaddr.contents = (krb5_octet *)addrbuf;
rsaddr.length = wholen;
rsaddr.addrtype = ADDRTYPE_INET;
if (rsport.contents != NULL)
free(rsport.contents);
rsport.contents = (krb5_octet *)portbuf;
rsport.length = sizeof (sin->sin_port);
rsport.addrtype = ADDRTYPE_IPPORT;
} else if (who->ss_family == AF_INET6) {
struct in_addr ipv4_addr;
char *addrbuf = NULL;
char *portbuf = NULL;
sin6 = (struct sockaddr_in6 *)who;
wholen = sizeof (struct sockaddr_in6);
IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
&ipv4_addr);
addrbuf = (char *)malloc(wholen);
if (addrbuf == NULL)
fatal(f, "Cannot alloc memory for address info\n");
portbuf = (char *)malloc(sizeof (sin6->sin6_port));
if (portbuf == NULL) {
free(addrbuf);
fatal(f, "Cannot alloc memory for port info\n");
}
(void) memcpy((void *) addrbuf,
(const void *)&ipv4_addr,
wholen);
if (rsaddr.contents != NULL)
free(rsaddr.contents);
rsaddr.contents = (krb5_octet *)addrbuf;
rsaddr.length = sizeof (ipv4_addr);
rsaddr.addrtype = ADDRTYPE_INET;
(void) memcpy((void *) portbuf, (const void *)&sin6->sin6_port,
sizeof (sin6->sin6_port));
if (rsport.contents != NULL)
free(rsport.contents);
rsport.contents = (krb5_octet *)portbuf;
rsport.length = sizeof (sin6->sin6_port);
rsport.addrtype = ADDRTYPE_IPPORT;
} else {
syslog(LOG_ERR, "unknown address family %d\n",
who->ss_family);
fatal(f, "getpeername: unknown address family\n");
}
if (getnameinfo((const struct sockaddr *) who, wholen, host_name,
sizeof (host_name), NULL, 0, 0) == 0) {
host = host_name;
} else {
if (resolve_hostname) {
fatal(f, "Couldn't resolve your address into a "
"host name.\r\nPlease contact your net "
"administrator");
}
if (who->ss_family == AF_INET6) {
if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
struct in_addr ipv4_addr;
IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
&ipv4_addr);
host = (char *)inet_ntop(AF_INET,
&ipv4_addr, abuf, sizeof (abuf));
} else {
host = (char *)inet_ntop(AF_INET6,
&sin6->sin6_addr, abuf,
sizeof (abuf));
}
} else if (who->ss_family == AF_INET) {
host = (char *)inet_ntop(AF_INET,
&sin->sin_addr, abuf, sizeof (abuf));
}
}
if (removemod(f, "sockmod") < 0)
fatalperror(f, "couldn't remove sockmod", errno);
encrypt_init();
if (ioctl(f, I_PUSH, "cryptmod") < 0)
fatalperror(f, "ioctl I_PUSH cryptmod", errno);
cryptmod_fd = f;
settimer(getencr);
username[0] = '\0';
len = sizeof (username);
settimer(getterminal);
settimer(getauth);
auth_status = getauthtype(username, &len);
getencrtype();
getterminaltype();
if (ioctl(f, I_PUSH, "telmod") < 0)
fatalperror(f, "ioctl I_PUSH telmod", errno);
passthru = 1;
telnetmod.ic_cmd = CRYPTPASSTHRU;
telnetmod.ic_timout = -1;
telnetmod.ic_len = sizeof (uchar_t);
telnetmod.ic_dp = (char *)&passthru;
if (ioctl(f, I_STR, &telnetmod) < 0)
fatal(f, "ioctl CRPASSTHRU failed\n");
if (!ncc)
netip = netibuf;
while ((nsize = readstream(f, netibuf, ncc + netip - netibuf)) > 0) {
ncc += nsize;
}
if (nsize < 0) {
fatalperror(f, "readstream failed\n", errno);
}
if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1) {
fatalperror(f, "open /dev/logindmux", errno);
}
if ((netfd = open("/dev/logindmux", O_RDWR)) == -1) {
fatalperror(f, "open /dev/logindmux", errno);
}
if (ioctl(ptmfd, I_LINK, p) < 0)
fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
if (ioctl(netfd, I_LINK, f) < 0)
fatal(f, "ioctl I_LINK of tcp connection failed\n");
if (fstat(ptmfd, &buf) < 0) {
fatalperror(f, "fstat ptmfd failed", errno);
}
telnetp.dev = buf.st_rdev;
telnetp.flag = 0;
telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
telnetmod.ic_timout = -1;
telnetmod.ic_len = sizeof (struct protocol_arg);
telnetmod.ic_dp = (char *)&telnetp;
if (ioctl(netfd, I_STR, &telnetmod) < 0)
fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
if (fstat(netfd, &buf) < 0) {
fatalperror(f, "fstat netfd failed", errno);
}
telnetp.dev = buf.st_rdev;
telnetp.flag = 1;
telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
telnetmod.ic_timout = -1;
telnetmod.ic_len = sizeof (struct protocol_arg);
telnetmod.ic_dp = (char *)&telnetp;
if (ioctl(ptmfd, I_STR, &telnetmod) < 0)
fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of ptmfd failed\n");
net = netfd;
manager = ptmfd;
cryptmod_fd = netfd;
if (getenv("USER") == NULL && auth_status < AUTH_USER)
showbanner();
if (auth_level >= 0 && auth_status >= AUTH_USER &&
(AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) {
(void) strcpy(pam_svc_name, "ktelnet");
}
if (!myopts[TELOPT_SGA]) {
dooption(TELOPT_SGA);
}
if (!myopts[TELOPT_ECHO]) {
dooption(TELOPT_ECHO);
}
send_do(TELOPT_ECHO);
remopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
if ((pid = fork()) < 0)
fatalperror(netfd, "fork", errno);
if (pid)
telnet(net, manager);
(void) setsid();
tt = open(line, O_RDWR);
if (tt < 0)
fatalperror(netfd, line, errno);
(void) close(netfd);
(void) close(ptmfd);
(void) close(f);
(void) close(p);
(void) close(t);
if (tt != 0)
(void) dup2(tt, 0);
if (tt != 1)
(void) dup2(tt, 1);
if (tt != 2)
(void) dup2(tt, 2);
if (tt > 2)
(void) close(tt);
if (terminaltype)
(void) local_setenv("TERM", terminaltype+5, 1);
{
struct utmpx ut;
(void) memset((char *)&ut, 0, sizeof (ut));
(void) strncpy(ut.ut_user, ".telnet", sizeof (ut.ut_user));
(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
ut.ut_pid = getpid();
ut.ut_id[0] = 't';
ut.ut_id[1] = (char)SC_WILDC;
ut.ut_id[2] = (char)SC_WILDC;
ut.ut_id[3] = (char)SC_WILDC;
ut.ut_type = LOGIN_PROCESS;
ut.ut_exit.e_termination = 0;
ut.ut_exit.e_exit = 0;
(void) time(&ut.ut_tv.tv_sec);
if (makeutx(&ut) == NULL)
syslog(LOG_INFO, "in.telnetd:\tmakeutx failed");
}
for (next = envlist_head; next; ) {
env = next;
if (env->delete)
(void) local_unsetenv(env->name);
else
(void) local_setenv(env->name, env->value, 1);
free(env->name);
free(env->value);
next = env->next;
free(env);
}
if (!username || !username[0])
auth_status = AUTH_REJECT;
if (auth_status < auth_level) {
fatal(net, "Authentication failed\n");
exit(EXIT_FAILURE);
}
if (auth_level >= 0 &&
(auth_status == AUTH_VALID || auth_status == AUTH_USER) &&
((krb5_name != NULL) && strlen(krb5_name)) &&
((AuthenticatingUser != NULL) && strlen(AuthenticatingUser))) {
(void) execl(LOGIN_PROGRAM, "login",
"-p",
"-d", subsidname,
"-h", host,
"-u", krb5_name,
"-s", pam_svc_name,
"-R", KRB5_REPOSITORY_NAME,
AuthenticatingUser, 0);
} else if (auth_level >= 0 &&
auth_status >= AUTH_USER &&
(((AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) ||
getenv("USER"))) {
(void) execl(LOGIN_PROGRAM, "login",
"-p",
"-d", subsidname,
"-h", host,
"-s", pam_svc_name, "--",
(AuthenticatingUser != NULL ? AuthenticatingUser :
getenv("USER")), 0);
} else {
(void) execl(LOGIN_PROGRAM, "login",
"-p", "-h", host, "-d", subsidname, "--",
getenv("USER"), 0);
}
fatalperror(netfd, LOGIN_PROGRAM, errno);
}
static void
fatal(int f, char *msg)
{
char buf[BUFSIZ];
(void) snprintf(buf, sizeof (buf), "telnetd: %s.\r\n", msg);
(void) write(f, buf, strlen(buf));
exit(EXIT_FAILURE);
}
static void
fatalperror(int f, char *msg, int errnum)
{
char buf[BUFSIZ];
(void) snprintf(buf, sizeof (buf),
"%s: %s\r\n", msg, strerror(errnum));
fatal(f, buf);
}
static void
telnet(int net, int manager)
{
int on = 1;
char mode;
struct strioctl telnetmod;
int nsize = 0;
char binary_in = 0;
char binary_out = 0;
if (ioctl(net, FIONBIO, &on) == -1)
syslog(LOG_INFO, "ioctl FIONBIO net: %m\n");
if (ioctl(manager, FIONBIO, &on) == -1)
syslog(LOG_INFO, "ioctl FIONBIO pty p: %m\n");
(void) signal(SIGTSTP, SIG_IGN);
(void) signal(SIGCHLD, (void (*)())cleanup);
(void) setpgrp();
telrcv();
netflush();
ptyflush();
for (;;) {
fd_set ibits, obits, xbits;
int c;
if (ncc < 0)
break;
FD_ZERO(&ibits);
FD_ZERO(&obits);
FD_ZERO(&xbits);
if (nfrontp - nbackp)
FD_SET(net, &obits);
if (pfrontp - pbackp) {
FD_SET(manager, &obits);
} else {
FD_SET(net, &ibits);
}
if (!SYNCHing) {
FD_SET(net, &xbits);
}
#define max(x, y) (((x) < (y)) ? (y) : (x))
if (binary_in != myopts[TELOPT_BINARY] ||
binary_out != remopts[TELOPT_BINARY]) {
mode = 0;
if (myopts[TELOPT_BINARY] != OPT_NO)
mode |= TEL_BINARY_IN;
if (remopts[TELOPT_BINARY] != OPT_NO)
mode |= TEL_BINARY_OUT;
telnetmod.ic_cmd = TEL_IOC_MODE;
telnetmod.ic_timout = -1;
telnetmod.ic_len = 1;
telnetmod.ic_dp = &mode;
syslog(LOG_DEBUG, "TEL_IOC_MODE binary has changed\n");
if (ioctl(net, I_STR, &telnetmod) < 0)
fatal(net, "ioctl TEL_IOC_MODE failed\n");
binary_in = myopts[TELOPT_BINARY];
binary_out = remopts[TELOPT_BINARY];
}
if (state == TS_DATA) {
if ((nfrontp == nbackp) &&
(pfrontp == pbackp)) {
if (ioctl(net, I_NREAD, &nsize) < 0)
fatalperror(net,
"ioctl I_NREAD failed\n", errno);
if (nsize)
drainstream(nsize);
telnetmod.ic_cmd = TEL_IOC_ENABLE;
telnetmod.ic_timout = -1;
if (ncc || nsize) {
telnetmod.ic_len = ncc + nsize;
telnetmod.ic_dp = netip;
} else {
telnetmod.ic_len = 0;
telnetmod.ic_dp = NULL;
}
if (ioctl(net, I_STR, &telnetmod) < 0)
fatal(net, "ioctl TEL_IOC_ENABLE \
failed\n");
telmod_init_done = B_TRUE;
netip = netibuf;
(void) memset(netibuf, 0, netibufsize);
ncc = 0;
}
} else {
telnetmod.ic_cmd = TEL_IOC_GETBLK;
telnetmod.ic_timout = -1;
telnetmod.ic_len = 0;
telnetmod.ic_dp = NULL;
if (ioctl(net, I_STR, &telnetmod) < 0)
fatal(net, "ioctl TEL_IOC_GETBLK failed\n");
}
if ((c = select(max(net, manager) + 1, &ibits, &obits, &xbits,
(struct timeval *)0)) < 1) {
if (c == -1) {
if (errno == EINTR) {
continue;
}
}
(void) sleep(5);
continue;
}
if (FD_ISSET(net, &xbits)) {
SYNCHing = 1;
}
if (FD_ISSET(net, &ibits)) {
ncc = read(net, netibuf, netibufsize);
if (ncc < 0 && errno == EWOULDBLOCK)
ncc = 0;
else {
if (ncc <= 0) {
break;
}
netip = netibuf;
}
}
if (FD_ISSET(net, &obits) && (nfrontp - nbackp) > 0)
netflush();
if (ncc > 0)
telrcv();
if (FD_ISSET(manager, &obits) && (pfrontp - pbackp) > 0)
ptyflush();
}
cleanup(0);
}
static void
telrcv(void)
{
int c;
while (ncc > 0) {
if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
return;
c = *netip & 0377;
if (c != IAC && state == TS_DATA && init_neg_done) {
break;
}
netip++;
ncc--;
switch (state) {
case TS_CR:
state = TS_DATA;
if ((c == 0) || (c == '\n')) {
break;
}
case TS_DATA:
if (c == IAC) {
state = TS_IAC;
break;
}
if (inter > 0)
break;
if (c == '\r' && (myopts[TELOPT_BINARY] == OPT_NO)) {
state = TS_CR;
}
*pfrontp++ = c;
break;
case TS_IAC:
switch (c) {
case IP:
interrupt();
break;
case BREAK:
sendbrk();
break;
case AYT:
write_data_len("\r\n[Yes]\r\n", 9);
break;
case AO: {
struct ltchars tmpltc;
ptyflush();
if (ioctl(pty, TIOCGLTC, &tmpltc) == -1)
syslog(LOG_INFO,
"ioctl TIOCGLTC: %m\n");
if (tmpltc.t_flushc != '\377') {
*pfrontp++ = tmpltc.t_flushc;
}
netclear();
write_data("%c%c", (uchar_t)IAC,
(uchar_t)DM);
neturg = nfrontp-1;
netflush();
netflush();
break;
}
case EC:
case EL: {
struct sgttyb b;
char ch;
ptyflush();
if (ioctl(pty, TIOCGETP, &b) == -1)
syslog(LOG_INFO,
"ioctl TIOCGETP: %m\n");
ch = (c == EC) ?
b.sg_erase : b.sg_kill;
if (ch != '\377') {
*pfrontp++ = ch;
}
break;
}
case DM:
break;
case SB:
state = TS_SB;
SB_CLEAR();
continue;
case WILL:
state = TS_WILL;
continue;
case WONT:
state = TS_WONT;
continue;
case DO:
state = TS_DO;
continue;
case DONT:
state = TS_DONT;
continue;
case IAC:
*pfrontp++ = c;
break;
}
state = TS_DATA;
break;
case TS_SB:
if (c == IAC) {
state = TS_SE;
} else {
SB_ACCUM(c);
}
break;
case TS_SE:
if (c != SE) {
if (c != IAC) {
SB_ACCUM((uchar_t)IAC);
}
SB_ACCUM(c);
state = TS_SB;
} else {
SB_TERM();
suboption();
state = TS_DATA;
}
break;
case TS_WILL:
if (remopts[c] != OPT_YES)
willoption(c);
state = TS_DATA;
continue;
case TS_WONT:
if (remopts[c] != OPT_NO)
wontoption(c);
state = TS_DATA;
continue;
case TS_DO:
if (myopts[c] != OPT_YES)
dooption(c);
state = TS_DATA;
continue;
case TS_DONT:
if (myopts[c] != OPT_NO) {
dontoption(c);
}
state = TS_DATA;
continue;
default:
syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
(void) printf("telnetd: panic state=%d\n", state);
exit(EXIT_FAILURE);
}
}
}
static void
willoption(int option)
{
uchar_t *fmt;
boolean_t send_reply = B_TRUE;
switch (option) {
case TELOPT_BINARY:
mode(O_RAW, 0);
fmt = doopt;
break;
case TELOPT_ECHO:
not42 = 0;
if (myopts[TELOPT_ECHO] == OPT_YES) {
dooption(TELOPT_ECHO);
}
fmt = dont;
break;
case TELOPT_TTYPE:
settimer(ttypeopt);
goto common;
case TELOPT_NAWS:
settimer(nawsopt);
goto common;
case TELOPT_XDISPLOC:
settimer(xdisplocopt);
goto common;
case TELOPT_NEW_ENVIRON:
settimer(environopt);
goto common;
case TELOPT_AUTHENTICATION:
settimer(authopt);
if (remopts[option] == OPT_NO ||
negotiate_auth_krb5 == 0)
fmt = dont;
else
fmt = doopt;
break;
case TELOPT_OLD_ENVIRON:
settimer(oenvironopt);
goto common;
common:
if (remopts[option] == OPT_YES_BUT_ALWAYS_LOOK) {
remopts[option] = OPT_YES;
return;
}
case TELOPT_SGA:
fmt = doopt;
break;
case TELOPT_TM:
fmt = dont;
break;
case TELOPT_ENCRYPT:
settimer(encropt);
if (enc_debug)
(void) fprintf(stderr,
"RCVD IAC WILL TELOPT_ENCRYPT\n");
if (krb5_privacy_allowed()) {
fmt = doopt;
if (sent_do_encrypt)
send_reply = B_FALSE;
else
sent_do_encrypt = B_TRUE;
} else {
fmt = dont;
}
break;
default:
fmt = dont;
break;
}
if (fmt == doopt) {
remopts[option] = OPT_YES;
} else {
remopts[option] = OPT_NO;
}
if (send_reply) {
write_data((const char *)fmt, option);
netflush();
}
}
static void
wontoption(int option)
{
uchar_t *fmt;
int send_reply = 1;
switch (option) {
case TELOPT_ECHO:
not42 = 1;
break;
case TELOPT_BINARY:
mode(0, O_RAW);
break;
case TELOPT_TTYPE:
settimer(ttypeopt);
break;
case TELOPT_NAWS:
settimer(nawsopt);
break;
case TELOPT_XDISPLOC:
settimer(xdisplocopt);
break;
case TELOPT_NEW_ENVIRON:
settimer(environopt);
break;
case TELOPT_OLD_ENVIRON:
settimer(oenvironopt);
break;
case TELOPT_AUTHENTICATION:
settimer(authopt);
auth_finished(0, AUTH_REJECT);
if (auth_debug)
(void) fprintf(stderr,
"RCVD WONT TELOPT_AUTHENTICATE\n");
remopts[option] = OPT_NO;
send_reply = 0;
break;
case TELOPT_ENCRYPT:
if (enc_debug)
(void) fprintf(stderr,
"RCVD IAC WONT TELOPT_ENCRYPT\n");
settimer(encropt);
encrypt_end(TELNET_DIR_DECRYPT);
send_reply = 0;
break;
}
fmt = dont;
remopts[option] = OPT_NO;
if (send_reply) {
write_data((const char *)fmt, option);
}
}
static void
dooption(int option)
{
uchar_t *fmt;
boolean_t send_reply = B_TRUE;
switch (option) {
case TELOPT_TM:
fmt = wont;
break;
case TELOPT_ECHO:
mode(O_ECHO|O_CRMOD, 0);
fmt = will;
break;
case TELOPT_BINARY:
mode(O_RAW, 0);
fmt = will;
break;
case TELOPT_SGA:
fmt = will;
break;
case TELOPT_LOGOUT:
write_data((const char *)will, option);
netflush();
cleanup(0);
case TELOPT_ENCRYPT:
if (enc_debug)
(void) fprintf(stderr, "RCVD DO TELOPT_ENCRYPT\n");
settimer(encropt);
if (krb5_privacy_allowed() && negotiate_encrypt) {
fmt = will;
if (sent_will_encrypt)
send_reply = B_FALSE;
else
sent_will_encrypt = B_TRUE;
if (myopts[option] == OPT_YES)
return;
} else {
fmt = wont;
}
break;
case TELOPT_AUTHENTICATION:
if (auth_debug) {
(void) fprintf(stderr,
"RCVD DO TELOPT_AUTHENTICATION\n");
}
fmt = wont;
break;
default:
fmt = wont;
break;
}
if (fmt == will) {
myopts[option] = OPT_YES;
} else {
myopts[option] = OPT_NO;
}
if (send_reply) {
write_data((const char *)fmt, option);
netflush();
}
}
static void
dontoption(int option)
{
int send_reply = 1;
switch (option) {
case TELOPT_ECHO:
mode(0, O_ECHO);
break;
case TELOPT_ENCRYPT:
if (enc_debug)
(void) fprintf(stderr, "RCVD IAC DONT ENCRYPT\n");
settimer(encropt);
send_reply = 0;
break;
default:
break;
}
myopts[option] = OPT_NO;
if (send_reply) {
write_data((const char *)wont, option);
}
}
static void
suboption(void)
{
int subchar;
switch (subchar = SB_GET()) {
case TELOPT_TTYPE: {
static char terminalname[5+41] = "TERM=";
settimer(ttypesubopt);
if (SB_GET() != TELQUAL_IS) {
return;
}
terminaltype = terminalname+strlen(terminalname);
while (terminaltype < (terminalname + sizeof (terminalname) -
1) && !SB_EOF()) {
int c;
c = SB_GET();
if (isupper(c)) {
c = tolower(c);
}
*terminaltype++ = c;
}
*terminaltype = 0;
terminaltype = terminalname;
break;
}
case TELOPT_NAWS: {
struct winsize ws;
if (SB_EOF()) {
return;
}
ws.ws_col = SB_GET() << 8;
if (SB_EOF()) {
return;
}
ws.ws_col |= SB_GET();
if (SB_EOF()) {
return;
}
ws.ws_row = SB_GET() << 8;
if (SB_EOF()) {
return;
}
ws.ws_row |= SB_GET();
ws.ws_xpixel = 0; ws.ws_ypixel = 0;
(void) ioctl(pty, TIOCSWINSZ, &ws);
settimer(nawsopt);
break;
}
case TELOPT_XDISPLOC: {
if (SB_EOF() || SB_GET() != TELQUAL_IS) {
return;
}
settimer(xdisplocsubopt);
subpointer[SB_LEN()] = '\0';
if ((new_env("DISPLAY", subpointer)) == 1)
perror("malloc");
break;
}
case TELOPT_NEW_ENVIRON:
case TELOPT_OLD_ENVIRON: {
int c;
char *cp, *varp, *valp;
if (SB_EOF())
return;
c = SB_GET();
if (c == TELQUAL_IS) {
if (subchar == TELOPT_OLD_ENVIRON)
settimer(oenvironsubopt);
else
settimer(environsubopt);
} else if (c != TELQUAL_INFO) {
return;
}
if (subchar == TELOPT_NEW_ENVIRON) {
while (!SB_EOF()) {
c = SB_GET();
if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
break;
}
} else
{
while (!SB_EOF()) {
c = SB_GET();
if ((c == env_ovar) || (c == ENV_USERVAR))
break;
}
}
if (SB_EOF())
return;
cp = varp = (char *)subpointer;
valp = 0;
while (!SB_EOF()) {
c = SB_GET();
if (subchar == TELOPT_OLD_ENVIRON) {
if (c == env_ovar)
c = NEW_ENV_VAR;
else if (c == env_ovalue)
c = NEW_ENV_VALUE;
}
switch (c) {
case NEW_ENV_VALUE:
*cp = '\0';
cp = valp = (char *)subpointer;
break;
case NEW_ENV_VAR:
case ENV_USERVAR:
*cp = '\0';
if (valp) {
if ((new_env(varp, valp)) == 1) {
perror("malloc");
}
} else {
(void) del_env(varp);
}
cp = varp = (char *)subpointer;
valp = 0;
break;
case ENV_ESC:
if (SB_EOF())
break;
c = SB_GET();
default:
*cp++ = c;
break;
}
}
*cp = '\0';
if (valp) {
if ((new_env(varp, valp)) == 1) {
perror("malloc");
}
} else {
(void) del_env(varp);
}
break;
}
case TELOPT_AUTHENTICATION:
if (SB_EOF())
break;
switch (SB_GET()) {
case TELQUAL_SEND:
case TELQUAL_REPLY:
break;
case TELQUAL_IS:
if (auth_debug)
(void) fprintf(stderr,
"RCVD AUTHENTICATION IS "
"(%d bytes)\n",
SB_LEN());
if (!auth_negotiated)
auth_is((uchar_t *)subpointer, SB_LEN());
break;
case TELQUAL_NAME:
if (auth_debug)
(void) fprintf(stderr,
"RCVD AUTHENTICATION NAME "
"(%d bytes)\n",
SB_LEN());
if (!auth_negotiated)
auth_name((uchar_t *)subpointer, SB_LEN());
break;
}
break;
case TELOPT_ENCRYPT: {
int c;
if (SB_EOF())
break;
c = SB_GET();
#ifdef ENCRYPT_NAMES
if (enc_debug)
(void) fprintf(stderr, "RCVD ENCRYPT %s\n",
ENCRYPT_NAME(c));
#endif
switch (c) {
case ENCRYPT_SUPPORT:
encrypt_support(subpointer, SB_LEN());
break;
case ENCRYPT_IS:
encrypt_is((uchar_t *)subpointer, SB_LEN());
break;
case ENCRYPT_REPLY:
(void) encrypt_reply(subpointer, SB_LEN());
break;
case ENCRYPT_START:
encrypt_start();
break;
case ENCRYPT_END:
encrypt_end(TELNET_DIR_DECRYPT);
break;
case ENCRYPT_REQSTART:
encrypt_request_start();
break;
case ENCRYPT_REQEND:
encrypt_request_end();
break;
case ENCRYPT_ENC_KEYID:
encrypt_enc_keyid(subpointer, SB_LEN());
break;
case ENCRYPT_DEC_KEYID:
encrypt_dec_keyid(subpointer, SB_LEN());
break;
default:
break;
}
}
break;
default:
break;
}
}
static void
mode(int on, int off)
{
struct termios tios;
ptyflush();
if (tcgetattr(pty, &tios) < 0)
syslog(LOG_INFO, "tcgetattr: %m\n");
if (on & O_RAW) {
tios.c_cflag |= CS8;
tios.c_iflag &= ~IUCLC;
tios.c_lflag &= ~(XCASE|IEXTEN);
}
if (off & O_RAW) {
if ((tios.c_cflag & PARENB) != 0)
tios.c_cflag &= ~CS8;
tios.c_lflag |= IEXTEN;
}
if (on & O_ECHO)
tios.c_lflag |= ECHO;
if (off & O_ECHO)
tios.c_lflag &= ~ECHO;
if (on & O_CRMOD) {
tios.c_iflag |= ICRNL;
tios.c_oflag |= ONLCR;
}
if (tcsetattr(pty, TCSANOW, &tios) < 0)
syslog(LOG_INFO, "tcsetattr: %m\n");
}
static void
interrupt(void)
{
struct sgttyb b;
struct tchars tchars;
ptyflush();
if (ioctl(pty, TIOCGETP, &b) == -1)
syslog(LOG_INFO, "ioctl TIOCGETP: %m\n");
if (b.sg_flags & O_RAW) {
*pfrontp++ = '\0';
return;
}
*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
'\177' : tchars.t_intrc;
}
static void
sendbrk(void)
{
struct sgttyb b;
struct tchars tchars;
ptyflush();
(void) ioctl(pty, TIOCGETP, &b);
if (b.sg_flags & O_RAW) {
*pfrontp++ = '\0';
return;
}
*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
'\034' : tchars.t_quitc;
}
static void
ptyflush(void)
{
int n;
if ((n = pfrontp - pbackp) > 0)
n = write(manager, pbackp, n);
if (n < 0)
return;
pbackp += n;
if (pbackp == pfrontp)
pbackp = pfrontp = ptyobuf;
}
static char *
nextitem(char *current)
{
if ((*current&0xff) != IAC) {
return (current+1);
}
switch (*(current+1)&0xff) {
case DO:
case DONT:
case WILL:
case WONT:
return (current+3);
case SB:
{
char *look = current+2;
for (;;) {
if ((*look++&0xff) == IAC) {
if ((*look++&0xff) == SE) {
return (look);
}
}
}
}
default:
return (current+2);
}
}
static void
netclear(void)
{
char *thisitem, *next;
char *good;
#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
thisitem = netobuf;
while ((next = nextitem(thisitem)) <= nbackp) {
thisitem = next;
}
good = netobuf;
while (nfrontp > thisitem) {
if (wewant(thisitem)) {
int length;
next = thisitem;
do {
next = nextitem(next);
} while (wewant(next) && (nfrontp > next));
length = next-thisitem;
(void) memmove(good, thisitem, length);
good += length;
thisitem = next;
} else {
thisitem = nextitem(thisitem);
}
}
nbackp = netobuf;
nfrontp = good;
neturg = 0;
}
static void
netflush(void)
{
int n;
if ((n = nfrontp - nbackp) > 0) {
if ((neturg == 0) || (not42 == 0)) {
n = write(net, nbackp, n);
} else {
n = neturg - nbackp;
if (n > 1) {
n = write(net, nbackp, n-1);
} else {
n = send_oob(net, nbackp, n);
}
}
}
if (n < 0) {
if (errno == EWOULDBLOCK)
return;
return;
}
nbackp += n;
if (nbackp >= neturg) {
neturg = 0;
}
if (nbackp == nfrontp) {
nbackp = nfrontp = netobuf;
}
}
static void
cleanup(int signum)
{
if (!telmod_init_done) {
(void) close(net);
(void) close(manager);
}
rmut();
exit(EXIT_FAILURE);
}
static void
rmut(void)
{
pam_handle_t *pamh;
struct utmpx *up;
char user[sizeof (up->ut_user) + 1];
char ttyn[sizeof (up->ut_line) + 1];
char rhost[sizeof (up->ut_host) + 1];
(void) signal(SIGCHLD, SIG_IGN);
setutxent();
while (up = getutxent()) {
if (up->ut_pid == pid) {
if (up->ut_type == DEAD_PROCESS) {
break;
}
if (up->ut_type == USER_PROCESS) {
(void) strlcpy(user, up->ut_user,
sizeof (user));
(void) strlcpy(ttyn, up->ut_line,
sizeof (ttyn));
(void) strlcpy(rhost, up->ut_host,
sizeof (rhost));
if ((pam_start("telnet", user, NULL, &pamh)) ==
PAM_SUCCESS) {
(void) pam_set_item(pamh, PAM_TTY,
ttyn);
(void) pam_set_item(pamh, PAM_RHOST,
rhost);
(void) pam_close_session(pamh, 0);
(void) pam_end(pamh, PAM_SUCCESS);
}
}
up->ut_type = DEAD_PROCESS;
up->ut_exit.e_termination = WTERMSIG(0);
up->ut_exit.e_exit = WEXITSTATUS(0);
(void) time(&up->ut_tv.tv_sec);
if (modutx(up) == NULL) {
(void) pututxline(up);
updwtmpx("wtmpx", up);
}
break;
}
}
endutxent();
(void) signal(SIGCHLD, (void (*)())cleanup);
}
static int
readstream(int fd, char *buf, int offset)
{
struct strbuf ctlbuf, datbuf;
union T_primitives tpi;
int ret = 0;
int flags = 0;
int bytes_avail, count;
(void) memset((char *)&ctlbuf, 0, sizeof (ctlbuf));
(void) memset((char *)&datbuf, 0, sizeof (datbuf));
ctlbuf.buf = (char *)&tpi;
ctlbuf.maxlen = sizeof (tpi);
if (ioctl(fd, I_NREAD, &bytes_avail) < 0) {
syslog(LOG_ERR, "I_NREAD returned error %m");
return (-1);
}
if (bytes_avail > netibufsize - offset) {
count = netip - netibuf;
netibuf = (char *)realloc(netibuf,
(unsigned)netibufsize + bytes_avail);
if (netibuf == NULL) {
fatal(net, "netibuf realloc failed\n");
}
netibufsize += bytes_avail;
netip = netibuf + count;
buf = netibuf;
}
datbuf.buf = buf + offset;
datbuf.maxlen = netibufsize;
ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
if (ret < 0) {
syslog(LOG_ERR, "getmsg returned -1, errno %d\n",
errno);
return (-1);
}
if (ctlbuf.len <= 0) {
return (datbuf.len);
}
if (tpi.type == T_DATA_REQ) {
return (0);
}
if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
cleanup(0);
fatal(fd, "no data or protocol element recognized");
return (0);
}
static void
drainstream(int size)
{
int nbytes;
int tsize;
tsize = netip - netibuf;
if ((tsize + ncc + size) > netibufsize) {
if (!(netibuf = (char *)realloc(netibuf,
(unsigned)tsize + ncc + size)))
fatalperror(net, "netibuf realloc failed\n", errno);
netibufsize = tsize + ncc + size;
netip = netibuf + tsize;
}
if ((nbytes = read(net, (char *)netip + ncc, size)) != size)
syslog(LOG_ERR, "read %d bytes\n", nbytes);
}
static int
send_oob(int fd, char *ptr, int count)
{
struct T_exdata_req exd_req;
struct strbuf hdr, dat;
int ret;
exd_req.PRIM_type = T_EXDATA_REQ;
exd_req.MORE_flag = 0;
hdr.buf = (char *)&exd_req;
hdr.len = sizeof (exd_req);
dat.buf = ptr;
dat.len = count;
ret = putmsg(fd, &hdr, &dat, 0);
if (ret == 0) {
ret = count;
}
return (ret);
}
static int
local_setenv(const char *name, const char *value, int rewrite)
{
static int alloced;
char *c;
int l_value, offset;
if (strncmp(name, "LD_", 3) == 0 ||
strncmp(name, "NLSPATH", 7) == 0 ||
(strncmp(name, "TTYPROMPT", 9) == 0 &&
(name[9] == '\0' || name[9] == '='))) {
return (-1);
}
if (*value == '=')
++value;
l_value = strlen(value);
if ((c = __findenv(name, &offset))) {
if (!rewrite)
return (0);
if ((int)strlen(c) >= l_value) {
while (*c++ = *value++)
;
return (0);
}
} else {
int cnt;
char **p;
for (p = environ, cnt = 0; *p; ++p, ++cnt)
;
if (alloced) {
environ = (char **)realloc((char *)environ,
(size_t)(sizeof (char *) * (cnt + 2)));
if (!environ)
return (-1);
} else {
alloced = 1;
p = (char **)malloc((size_t)(sizeof (char *)*
(cnt + 2)));
if (!p)
return (-1);
(void) memcpy(p, environ, cnt * sizeof (char *));
environ = p;
}
environ[cnt + 1] = NULL;
offset = cnt;
}
for (c = (char *)name; *c && *c != '='; ++c)
;
if (!(environ[offset] =
malloc((size_t)((int)(c - name) + l_value + 2))))
return (-1);
for (c = environ[offset]; ((*c = *name++) != 0) && (*c != '='); ++c)
;
for (*c++ = '='; *c++ = *value++; )
;
return (0);
}
static void
local_unsetenv(const char *name)
{
char **p;
int offset;
while (__findenv(name, &offset))
for (p = &environ[offset]; ; ++p)
if ((*p = *(p + 1)) == 0)
break;
}
static char *
__findenv(const char *name, int *offset)
{
extern char **environ;
int len;
const char *np;
char **p, *c;
if (name == NULL || environ == NULL)
return (NULL);
for (np = name; *np && *np != '='; ++np)
continue;
len = np - name;
for (p = environ; (c = *p) != NULL; ++p)
if (strncmp(c, name, len) == 0 && c[len] == '=') {
*offset = p - environ;
return (c + len + 1);
}
return (NULL);
}
static void
showbanner(void)
{
char *cp;
char evalbuf[BUFSIZ];
if (defopen(defaultfile) == 0) {
int flags;
flags = defcntl(DC_GETFLAGS, 0);
TURNOFF(flags, DC_CASE);
(void) defcntl(DC_SETFLAGS, flags);
if (cp = defread(bannervar)) {
FILE *fp;
if (strlen(cp) + strlen("eval echo '") + strlen("'\n")
+ 1 < sizeof (evalbuf)) {
(void) strlcpy(evalbuf, "eval echo '",
sizeof (evalbuf));
(void) strlcat(evalbuf, cp, sizeof (evalbuf));
(void) strlcat(evalbuf, "'\n",
sizeof (evalbuf));
if (fp = popen(evalbuf, "r")) {
char buf[BUFSIZ];
size_t size;
if ((size = fread(buf, 1,
sizeof (buf) - 1,
fp)) != 0) {
char *p;
buf[size] = '\0';
p = strrchr(buf, '\n');
if (p != NULL)
*p = '\0';
if (strlen(buf)) {
map_banner(buf);
netflush();
}
}
(void) pclose(fp);
(void) defopen(NULL);
return;
}
}
}
(void) defopen(NULL);
}
defbanner();
netflush();
}
static void
map_banner(char *p)
{
char *q;
for (q = nfrontp; p && *p && q < nfrontp + sizeof (netobuf) - 1; )
if (*p == '\n') {
*q++ = '\r';
*q++ = '\n';
p++;
} else if (*p == '\r') {
*q++ = '\r';
*q++ = '\0';
p++;
} else
*q++ = *p++;
nfrontp += q - netobuf;
}
static void
defbanner(void)
{
struct utsname u;
if (!show_hostinfo)
return;
if (uname(&u) == -1)
return;
write_data_len((const char *) BANNER1, sizeof (BANNER1) - 1);
write_data_len(u.sysname, strlen(u.sysname));
write_data_len(" ", 1);
write_data_len(u.release, strlen(u.release));
write_data_len((const char *)BANNER2, sizeof (BANNER2) - 1);
}
static int
removemod(int f, char *modname)
{
char topmodname[BUFSIZ];
if (ioctl(f, I_LOOK, topmodname) < 0)
return (-1);
if (strcmp(modname, topmodname) != 0) {
errno = ENXIO;
return (-1);
}
if (ioctl(f, I_POP, 0) < 0)
return (-1);
return (0);
}
static void
write_data(const char *format, ...)
{
va_list args;
int len;
char argp[BUFSIZ];
va_start(args, format);
if ((len = vsnprintf(argp, sizeof (argp), format, args)) == -1)
return;
write_data_len(argp, len);
va_end(args);
}
static void
write_data_len(const char *buf, int len)
{
int remaining, copied;
remaining = BUFSIZ - (nfrontp - netobuf);
while (len > 0) {
if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
netflush();
remaining = BUFSIZ - (nfrontp - netobuf);
}
copied = remaining > len ? len : remaining;
(void) memmove(nfrontp, buf, copied);
nfrontp += copied;
len -= copied;
remaining -= copied;
buf += copied;
}
}