Home | History | Annotate | Download | only in usr.sbin
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
     29  * All Rights Reserved.
     30  */
     31 
     32 /*
     33  * University Copyright- Copyright (c) 1982, 1986, 1988
     34  * The Regents of the University of California.
     35  * All Rights Reserved.
     36  *
     37  * University Acknowledgment- Portions of this document are derived from
     38  * software developed by the University of California, Berkeley, and its
     39  * contributors.
     40  */
     41 
     42 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     43 
     44 /*
     45  * Telnet server.
     46  */
     47 #include <sys/types.h>
     48 #include <sys/param.h>
     49 #include <sys/socket.h>
     50 #include <sys/wait.h>
     51 #include <sys/file.h>
     52 #include <sys/stat.h>
     53 #include <sys/filio.h>
     54 #include <sys/time.h>
     55 #include <sys/stropts.h>
     56 #include <sys/stream.h>
     57 #include <sys/tihdr.h>
     58 #include <sys/utsname.h>
     59 #include <unistd.h>
     60 
     61 #include <netinet/in.h>
     62 
     63 #define	AUTHWHO_STR
     64 #define	AUTHTYPE_NAMES
     65 #define	AUTHHOW_NAMES
     66 #define	AUTHRSP_NAMES
     67 #define	ENCRYPT_NAMES
     68 
     69 #include <arpa/telnet.h>
     70 #include <arpa/inet.h>
     71 #include <stdio.h>
     72 #include <stdarg.h>
     73 #include <signal.h>
     74 #include <errno.h>
     75 #include <netdb.h>
     76 #include <syslog.h>
     77 #include <ctype.h>
     78 #include <fcntl.h>
     79 #include <sac.h>	/* for SC_WILDC */
     80 #include <utmpx.h>
     81 #include <sys/ttold.h>
     82 #include <malloc.h>
     83 #include <string.h>
     84 #include <security/pam_appl.h>
     85 #include <sys/tihdr.h>
     86 #include <sys/logindmux.h>
     87 #include <sys/telioctl.h>
     88 #include <deflt.h>
     89 #include <stdlib.h>
     90 #include <string.h>
     91 #include <stropts.h>
     92 #include <termios.h>
     93 
     94 #include <com_err.h>
     95 #include <krb5.h>
     96 #include <krb5_repository.h>
     97 #include <des/des.h>
     98 #include <rpc/des_crypt.h>
     99 #include <sys/cryptmod.h>
    100 #include <bsm/adt.h>
    101 
    102 #define	TELNETD_OPTS "Ss:a:dEXUhR:M:"
    103 #ifdef DEBUG
    104 #define	DEBUG_OPTS "p:e"
    105 #else
    106 #define	DEBUG_OPTS ""
    107 #endif /* DEBUG */
    108 
    109 #define	OPT_NO			0		/* won't do this option */
    110 #define	OPT_YES			1		/* will do this option */
    111 #define	OPT_YES_BUT_ALWAYS_LOOK	2
    112 #define	OPT_NO_BUT_ALWAYS_LOOK	3
    113 
    114 #define	MAXOPTLEN 256
    115 #define	MAXUSERNAMELEN 256
    116 
    117 static char	remopts[MAXOPTLEN];
    118 static char	myopts[MAXOPTLEN];
    119 static uchar_t	doopt[] = { (uchar_t)IAC, (uchar_t)DO, '%', 'c', 0 };
    120 static uchar_t	dont[] = { (uchar_t)IAC, (uchar_t)DONT, '%', 'c', 0 };
    121 static uchar_t	will[] = { (uchar_t)IAC, (uchar_t)WILL, '%', 'c', 0 };
    122 static uchar_t	wont[] = { (uchar_t)IAC, (uchar_t)WONT, '%', 'c', 0 };
    123 /*
    124  * I/O data buffers, pointers, and counters.
    125  */
    126 static char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
    127 
    128 static char	*netibuf, *netip;
    129 static int	netibufsize;
    130 
    131 #define	NIACCUM(c)	{   *netip++ = c; \
    132 			    ncc++; \
    133 			}
    134 
    135 static char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
    136 static char	*neturg = 0;		/* one past last bye of urgent data */
    137 /* the remote system seems to NOT be an old 4.2 */
    138 static int	not42 = 1;
    139 static char	defaultfile[] = "/etc/default/telnetd";
    140 static char	bannervar[] = "BANNER=";
    141 
    142 static char BANNER1[] = "\r\n\r\n";
    143 static char BANNER2[] = "\r\n\r\0\r\n\r\0";
    144 
    145 /*
    146  * buffer for sub-options - enlarged to 4096 to handle credentials
    147  * from AUTH options
    148  */
    149 static char	subbuffer[4096], *subpointer = subbuffer, *subend = subbuffer;
    150 #define	SB_CLEAR()	subpointer = subbuffer;
    151 #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
    152 #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof (subbuffer))) { \
    153 				*subpointer++ = (c); \
    154 			}
    155 #define	SB_GET()	((*subpointer++)&0xff)
    156 #define	SB_EOF()	(subpointer >= subend)
    157 #define	SB_LEN()	(subend - subpointer)
    158 
    159 #define	MAXERRSTRLEN 1024
    160 #define	MAXPRINCLEN 256
    161 
    162 extern uint_t kwarn_add_warning(char *, int);
    163 extern uint_t kwarn_del_warning(char *);
    164 
    165 static boolean_t auth_debug = 0;
    166 static boolean_t negotiate_auth_krb5 = 1;
    167 static boolean_t auth_negotiated = 0;
    168 static int auth_status = 0;
    169 static int auth_level = 0;
    170 static char	*AuthenticatingUser = NULL;
    171 static char	*krb5_name = NULL;
    172 
    173 static krb5_address rsaddr = { 0, 0, 0, NULL };
    174 static krb5_address rsport = { 0, 0, 0, NULL };
    175 
    176 static krb5_context telnet_context = 0;
    177 static krb5_auth_context auth_context = 0;
    178 
    179 /* telnetd gets session key from here */
    180 static krb5_ticket *ticket = NULL;
    181 static krb5_keyblock *session_key = NULL;
    182 static char *telnet_srvtab = NULL;
    183 
    184 typedef struct {
    185 	uchar_t AuthName;
    186 	uchar_t AuthHow;
    187 	char  *AuthString;
    188 } AuthInfo;
    189 
    190 static AuthInfo auth_list[] = {
    191 	{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL |
    192 	AUTH_ENCRYPT_ON, "KRB5 MUTUAL CRYPTO"},
    193 	{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL,
    194 	"KRB5 MUTUAL" },
    195 	{AUTHTYPE_KERBEROS_V5,	AUTH_WHO_CLIENT | AUTH_HOW_ONE_WAY,
    196 	"KRB5 1-WAY" },
    197 	{0, 0, "NONE"}
    198 };
    199 
    200 static AuthInfo NoAuth = {0, 0, NULL};
    201 
    202 static AuthInfo *authenticated = NULL;
    203 
    204 #define	PREAMBLE_SIZE		5	/* for auth_reply_str allocation */
    205 #define	POSTAMBLE_SIZE		5
    206 #define	STR_DATA_LEN(len)	((len) * 2 + PREAMBLE_SIZE + POSTAMBLE_SIZE)
    207 
    208 static void auth_name(uchar_t *, int);
    209 static void auth_is(uchar_t *, int);
    210 
    211 #define	NO_ENCRYPTION   0x00
    212 #define	SEND_ENCRYPTED  0x01
    213 #define	RECV_ENCRYPTED  0x02
    214 #define	ENCRYPT_BOTH_WAYS    (SEND_ENCRYPTED | RECV_ENCRYPTED)
    215 
    216 static telnet_enc_data_t  encr_data;
    217 static boolean_t negotiate_encrypt = B_TRUE;
    218 static boolean_t sent_encrypt_support = B_FALSE;
    219 static boolean_t sent_will_encrypt = B_FALSE;
    220 static boolean_t sent_do_encrypt = B_FALSE;
    221 static boolean_t enc_debug = 0;
    222 
    223 static void encrypt_session_key(Session_Key *key, cipher_info_t *cinfo);
    224 static int  encrypt_send_encrypt_is();
    225 
    226 extern void mit_des_fixup_key_parity(Block);
    227 extern int krb5_setenv(const char *, const char *, int);
    228 /* need to know what FD to use to talk to the crypto module */
    229 static int cryptmod_fd = -1;
    230 
    231 #define	LOGIN_PROGRAM "/bin/login"
    232 
    233 /*
    234  * State for recv fsm
    235  */
    236 #define	TS_DATA		0	/* base state */
    237 #define	TS_IAC		1	/* look for double IAC's */
    238 #define	TS_CR		2	/* CR-LF ->'s CR */
    239 #define	TS_SB		3	/* throw away begin's... */
    240 #define	TS_SE		4	/* ...end's (suboption negotiation) */
    241 #define	TS_WILL		5	/* will option negotiation */
    242 #define	TS_WONT		6	/* wont " */
    243 #define	TS_DO		7	/* do " */
    244 #define	TS_DONT		8	/* dont " */
    245 
    246 static int	ncc;
    247 static int	master;		/* master side of pty */
    248 static int	pty;		/* side of pty that gets ioctls */
    249 static int	net;
    250 static int	inter;
    251 extern char **environ;
    252 static char	*line;
    253 static int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
    254 static int	state = TS_DATA;
    255 
    256 static int env_ovar = -1;	/* XXX.sparker */
    257 static int env_ovalue = -1;	/* XXX.sparker */
    258 static char pam_svc_name[64];
    259 static boolean_t	telmod_init_done = B_FALSE;
    260 
    261 static void	doit(int, struct sockaddr_storage *);
    262 static void	willoption(int);
    263 static void	wontoption(int);
    264 static void	dooption(int);
    265 static void	dontoption(int);
    266 static void	fatal(int, char *);
    267 static void	fatalperror(int, char *, int);
    268 static void	mode(int, int);
    269 static void	interrupt(void);
    270 static void	drainstream(int);
    271 static int	readstream(int, char *, int);
    272 static int	send_oob(int fd, char *ptr, int count);
    273 static int	local_setenv(const char *name, const char *value, int rewrite);
    274 static void	local_unsetenv(const char *name);
    275 static void	suboption(void);
    276 static int	removemod(int f, char *modname);
    277 static void	willoption(int option);
    278 static void	wontoption(int option);
    279 static void	dooption(int option);
    280 static void	dontoption(int option);
    281 static void	write_data(const char *, ...);
    282 static void	write_data_len(const char *, int);
    283 static void	rmut(void);
    284 static void	cleanup(int);
    285 static void	telnet(int, int);
    286 static void	telrcv(void);
    287 static void	sendbrk(void);
    288 static void	ptyflush(void);
    289 static void	netclear(void);
    290 static void	netflush(void);
    291 static void	showbanner(void);
    292 static void	map_banner(char *);
    293 static void	defbanner(void);
    294 static void ttloop(void);
    295 
    296 /*
    297  * The env_list linked list is used to store the environment variables
    298  * until the final exec of login.  A malevolent client might try to
    299  * send an environment variable intended to affect the telnet daemon's
    300  * execution.  Right now the BANNER expansion is the only instance.
    301  * Note that it is okay to pass the environment variables to login
    302  * because login protects itself against environment variables mischief.
    303  */
    304 
    305 struct envlist {
    306 	struct envlist	*next;
    307 	char		*name;
    308 	char		*value;
    309 	int		delete;
    310 };
    311 
    312 static struct envlist *envlist_head = NULL;
    313 
    314 /*
    315  * The following are some clocks used to decide how to interpret
    316  * the relationship between various variables.
    317  */
    318 
    319 static struct {
    320 	int
    321 	system,			/* what the current time is */
    322 	echotoggle,		/* last time user entered echo character */
    323 	modenegotiated,		/* last time operating mode negotiated */
    324 	didnetreceive,		/* last time we read data from network */
    325 	ttypeopt,		/* ttype will/won't received */
    326 	ttypesubopt,		/* ttype subopt is received */
    327 	getterminal,		/* time started to get terminal information */
    328 	xdisplocopt,		/* xdisploc will/wont received */
    329 	xdisplocsubopt,		/* xdisploc suboption received */
    330 	nawsopt,		/* window size will/wont received */
    331 	nawssubopt,		/* window size received */
    332 	environopt,		/* environment option will/wont received */
    333 	oenvironopt,		/* "old" environ option will/wont received */
    334 	environsubopt,		/* environment option suboption received */
    335 	oenvironsubopt,		/* "old environ option suboption received */
    336 	gotDM;			/* when did we last see a data mark */
    337 
    338 	int getauth;
    339 	int authopt;	/* Authentication option negotiated */
    340 	int authdone;
    341 
    342 	int getencr;
    343 	int encropt;
    344 	int encr_support;
    345 } clocks;
    346 
    347 static int init_neg_done = 0;
    348 static boolean_t resolve_hostname = 0;
    349 static boolean_t show_hostinfo = 1;
    350 
    351 #define	settimer(x)	(clocks.x = ++clocks.system)
    352 #define	sequenceIs(x, y)	(clocks.x < clocks.y)
    353 
    354 static void send_will(int);
    355 static void send_wont(int);
    356 static void send_do(int);
    357 static char *__findenv(const char *name, int *offset);
    358 
    359 /* ARGSUSED */
    360 static void
    361 auth_finished(AuthInfo *ap, int result)
    362 {
    363 	if ((authenticated = ap) == NULL) {
    364 		authenticated = &NoAuth;
    365 		if (myopts[TELOPT_ENCRYPT] == OPT_YES)
    366 			send_wont(TELOPT_ENCRYPT);
    367 		myopts[TELOPT_ENCRYPT] = remopts[TELOPT_ENCRYPT] = OPT_NO;
    368 		encr_data.encrypt.autoflag = 0;
    369 	} else if (result != AUTH_REJECT &&
    370 		myopts[TELOPT_ENCRYPT] == OPT_YES &&
    371 		remopts[TELOPT_ENCRYPT] == OPT_YES) {
    372 
    373 		/*
    374 		 * Authentication successful, so we have a session key, and
    375 		 * we're willing to do ENCRYPT, so send our ENCRYPT SUPPORT.
    376 		 *
    377 		 * Can't have sent ENCRYPT SUPPORT yet!  And if we're sending it
    378 		 * now it's really only because we did the DO ENCRYPT/WILL
    379 		 * ENCRYPT dance before authentication, which is ok, but not too
    380 		 * bright since we have to do the DONT ENCRYPT/WONT ENCRYPT
    381 		 * dance if authentication fails, though clients typically just
    382 		 * don't care.
    383 		 */
    384 		write_data("%c%c%c%c%c%c%c",
    385 			(uchar_t)IAC,
    386 			(uchar_t)SB,
    387 			(uchar_t)TELOPT_ENCRYPT,
    388 			(uchar_t)ENCRYPT_SUPPORT,
    389 			(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
    390 			(uchar_t)IAC,
    391 			(uchar_t)SE);
    392 
    393 		netflush();
    394 
    395 		sent_encrypt_support = B_TRUE;
    396 
    397 		if (enc_debug)
    398 			(void) fprintf(stderr,
    399 			"SENT ENCRYPT SUPPORT\n");
    400 
    401 		(void) encrypt_send_encrypt_is();
    402 	}
    403 
    404 	auth_status = result;
    405 
    406 	settimer(authdone);
    407 }
    408 
    409 static void
    410 reply_to_client(AuthInfo *ap, int type, void *data, int len)
    411 {
    412 	uchar_t reply[BUFSIZ];
    413 	uchar_t *p = reply;
    414 	uchar_t *cd = (uchar_t *)data;
    415 
    416 	if (len == -1 && data != NULL)
    417 		len = strlen((char *)data);
    418 	else if (len > (sizeof (reply) - 9)) {
    419 		syslog(LOG_ERR,
    420 		    "krb5 auth reply length too large (%d)", len);
    421 		if (auth_debug)
    422 			(void) fprintf(stderr,
    423 				    "krb5 auth reply length too large (%d)\n",
    424 				    len);
    425 		return;
    426 	} else if (data == NULL)
    427 		len = 0;
    428 
    429 	*p++ = IAC;
    430 	*p++ = SB;
    431 	*p++ = TELOPT_AUTHENTICATION;
    432 	*p++ = AUTHTYPE_KERBEROS_V5;
    433 	*p++ = ap->AuthName;
    434 	*p++ = ap->AuthHow; /* MUTUAL, ONE-WAY, etc */
    435 	*p++ = type;	    /* RESPONSE or ACCEPT */
    436 	while (len-- > 0) {
    437 		if ((*p++ = *cd++) == IAC)
    438 			*p++ = IAC;
    439 	}
    440 	*p++ = IAC;
    441 	*p++ = SE;
    442 
    443 	/* queue the data to be sent */
    444 	write_data_len((const char *)reply, p-reply);
    445 
    446 #if defined(AUTHTYPE_NAMES) && defined(AUTHWHO_STR) &&\
    447 defined(AUTHHOW_NAMES) && defined(AUTHRSP_NAMES)
    448 	if (auth_debug) {
    449 		(void) fprintf(stderr, "SENT TELOPT_AUTHENTICATION REPLY "
    450 			    "%s %s|%s %s\n",
    451 			    AUTHTYPE_NAME(ap->AuthName),
    452 			    AUTHWHO_NAME(ap->AuthHow & AUTH_WHO_MASK),
    453 			    AUTHHOW_NAME(ap->AuthHow & AUTH_HOW_MASK),
    454 			    AUTHRSP_NAME(type));
    455 	}
    456 #endif /* AUTHTYPE_NAMES && AUTHWHO_NAMES && AUTHHOW_NAMES && AUTHRSP_NAMES */
    457 
    458 	netflush();
    459 }
    460 
    461 /* Decode, decrypt and store the forwarded creds in the local ccache. */
    462 static krb5_error_code
    463 rd_and_store_forwarded_creds(krb5_context context,
    464 			    krb5_auth_context auth_context,
    465 			    krb5_data *inbuf, krb5_ticket *ticket,
    466 			    char *username)
    467 {
    468 	krb5_creds **creds;
    469 	krb5_error_code retval;
    470 	char ccname[MAXPATHLEN];
    471 	krb5_ccache ccache = NULL;
    472 	char *client_name = NULL;
    473 
    474 	if (retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))
    475 		return (retval);
    476 
    477 	(void) sprintf(ccname, "FILE:/tmp/krb5cc_p%ld", getpid());
    478 	(void) krb5_setenv("KRB5CCNAME", ccname, 1);
    479 
    480 	if ((retval = krb5_cc_default(context, &ccache)))
    481 		goto cleanup;
    482 
    483 	if ((retval = krb5_cc_initialize(context, ccache,
    484 					ticket->enc_part2->client)) != 0)
    485 		goto cleanup;
    486 
    487 	if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0)
    488 		goto cleanup;
    489 
    490 	if ((retval = krb5_cc_close(context, ccache)) != 0)
    491 		goto cleanup;
    492 
    493 	/* Register with ktkt_warnd(1M) */
    494 	if ((retval = krb5_unparse_name(context, (*creds)->client,
    495 					&client_name)) != 0)
    496 		goto cleanup;
    497 	(void) kwarn_del_warning(client_name);
    498 	if (kwarn_add_warning(client_name, (*creds)->times.endtime) != 0) {
    499 		syslog(LOG_AUTH|LOG_NOTICE,
    500 		    "rd_and_store_forwarded_creds: kwarn_add_warning"
    501 		    " failed: ktkt_warnd(1M) down? ");
    502 		if (auth_debug)
    503 			(void) fprintf(stderr,
    504 				    "kwarn_add_warning failed:"
    505 				    " ktkt_warnd(1M) down?\n");
    506 	}
    507 	free(client_name);
    508 	client_name = NULL;
    509 
    510 	if (username != NULL) {
    511 		/*
    512 		 * This verifies that the user is valid on the local system,
    513 		 * maps the username from KerberosV5 to unix,
    514 		 * and moves the KRB5CCNAME file to the correct place
    515 		 *  /tmp/krb5cc_[uid] with correct ownership (0600 uid gid).
    516 		 *
    517 		 * NOTE: the user must be in the gsscred table in order to map
    518 		 * from KRB5 to Unix.
    519 		 */
    520 		(void) krb5_kuserok(context, ticket->enc_part2->client,
    521 				username);
    522 	}
    523 	if (auth_debug)
    524 		(void) fprintf(stderr,
    525 			    "Successfully stored forwarded creds\n");
    526 
    527 cleanup:
    528 	krb5_free_creds(context, *creds);
    529 	return (retval);
    530 }
    531 
    532 static void
    533 kerberos5_is(AuthInfo *ap, uchar_t *data, int cnt)
    534 {
    535 	krb5_error_code err = 0;
    536 	krb5_principal server;
    537 	krb5_keyblock *newkey = NULL;
    538 	krb5_keytab keytabid = 0;
    539 	krb5_data outbuf;
    540 	krb5_data inbuf;
    541 	krb5_authenticator *authenticator;
    542 	char errbuf[MAXERRSTRLEN];
    543 	char *name;
    544 	krb5_data auth;
    545 
    546 	Session_Key skey;
    547 
    548 	if (cnt-- < 1)
    549 		return;
    550 	switch (*data++) {
    551 	case KRB_AUTH:
    552 		auth.data = (char *)data;
    553 		auth.length = cnt;
    554 
    555 		if (auth_context == NULL) {
    556 			err = krb5_auth_con_init(telnet_context, &auth_context);
    557 			if (err)
    558 				syslog(LOG_ERR,
    559 				    "Error getting krb5 auth "
    560 				    "context: %s", error_message(err));
    561 		}
    562 		if (!err) {
    563 			krb5_rcache rcache;
    564 
    565 			err = krb5_auth_con_getrcache(telnet_context,
    566 						    auth_context,
    567 						    &rcache);
    568 			if (!err && !rcache) {
    569 				err = krb5_sname_to_principal(telnet_context,
    570 							    0, 0,
    571 							    KRB5_NT_SRV_HST,
    572 							    &server);
    573 				if (!err) {
    574 					err = krb5_get_server_rcache(
    575 						telnet_context,
    576 						krb5_princ_component(
    577 							telnet_context,
    578 							server, 0),
    579 						&rcache);
    580 
    581 					krb5_free_principal(telnet_context,
    582 							    server);
    583 				}
    584 			}
    585 			if (err)
    586 				syslog(LOG_ERR,
    587 				    "Error allocating krb5 replay cache: %s",
    588 				    error_message(err));
    589 			else {
    590 				err = krb5_auth_con_setrcache(telnet_context,
    591 							    auth_context,
    592 							    rcache);
    593 				if (err)
    594 					syslog(LOG_ERR,
    595 					    "Error creating krb5 "
    596 					    "replay cache: %s",
    597 					    error_message(err));
    598 			}
    599 		}
    600 		if (!err && telnet_srvtab != NULL)
    601 			err = krb5_kt_resolve(telnet_context,
    602 					    telnet_srvtab, &keytabid);
    603 		if (!err)
    604 			err = krb5_rd_req(telnet_context, &auth_context, &auth,
    605 					NULL, keytabid, NULL, &ticket);
    606 		if (err) {
    607 			(void) snprintf(errbuf, sizeof (errbuf),
    608 				"Error reading krb5 auth information:"
    609 				" %s", error_message(err));
    610 			goto errout;
    611 		}
    612 
    613 		/*
    614 		 * Verify that the correct principal was used
    615 		 */
    616 		if (krb5_princ_component(telnet_context,
    617 				ticket->server, 0)->length < MAXPRINCLEN) {
    618 			char princ[MAXPRINCLEN];
    619 			(void) strncpy(princ,
    620 				    krb5_princ_component(telnet_context,
    621 						ticket->server, 0)->data,
    622 				    krb5_princ_component(telnet_context,
    623 					    ticket->server, 0)->length);
    624 			princ[krb5_princ_component(telnet_context,
    625 					ticket->server, 0)->length] = '\0';
    626 			if (strcmp("host", princ)) {
    627 				if (strlen(princ) < sizeof (errbuf) - 39) {
    628 				    (void) snprintf(errbuf, sizeof (errbuf),
    629 						"incorrect service "
    630 						    "name: \"%s\" != "
    631 						    "\"host\"",
    632 						    princ);
    633 			    } else {
    634 				    (void) strncpy(errbuf,
    635 						"incorrect service "
    636 						"name: principal != "
    637 						"\"host\"",
    638 						sizeof (errbuf));
    639 			    }
    640 			    goto errout;
    641 			}
    642 		} else {
    643 			(void) strlcpy(errbuf, "service name too long",
    644 					sizeof (errbuf));
    645 			goto errout;
    646 		}
    647 
    648 		err = krb5_auth_con_getauthenticator(telnet_context,
    649 						auth_context,
    650 						&authenticator);
    651 		if (err) {
    652 			(void) snprintf(errbuf, sizeof (errbuf),
    653 				"Failed to get authenticator: %s",
    654 				error_message(err));
    655 			goto errout;
    656 		}
    657 		if ((ap->AuthHow & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
    658 			!authenticator->checksum) {
    659 			(void) strlcpy(errbuf,
    660 				    "authenticator is missing checksum",
    661 				    sizeof (errbuf));
    662 			goto errout;
    663 		}
    664 		if (authenticator->checksum) {
    665 			char type_check[2];
    666 			krb5_checksum *cksum = authenticator->checksum;
    667 			krb5_keyblock *key;
    668 			krb5_data input;
    669 			krb5_boolean valid;
    670 
    671 			type_check[0] = ap->AuthName;
    672 			type_check[1] = ap->AuthHow;
    673 
    674 			err = krb5_auth_con_getkey(telnet_context,
    675 						auth_context, &key);
    676 			if (err) {
    677 				(void) snprintf(errbuf, sizeof (errbuf),
    678 					"Failed to get key from "
    679 					"authenticator: %s",
    680 					error_message(err));
    681 				goto errout;
    682 			}
    683 
    684 			input.data = type_check;
    685 			input.length = 2;
    686 			err = krb5_c_verify_checksum(telnet_context,
    687 							key, 0,
    688 							&input,
    689 							cksum,
    690 							&valid);
    691 			if (!err && !valid)
    692 				err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
    693 
    694 			if (err) {
    695 				(void) snprintf(errbuf, sizeof (errbuf),
    696 						"Kerberos checksum "
    697 						"verification failed: "
    698 						"%s",
    699 						error_message(err));
    700 				goto errout;
    701 			}
    702 			krb5_free_keyblock(telnet_context, key);
    703 		}
    704 
    705 		krb5_free_authenticator(telnet_context, authenticator);
    706 		if ((ap->AuthHow & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
    707 			/* do ap_rep stuff here */
    708 			if ((err = krb5_mk_rep(telnet_context, auth_context,
    709 					    &outbuf))) {
    710 				(void) snprintf(errbuf, sizeof (errbuf),
    711 						"Failed to make "
    712 						"Kerberos auth reply: "
    713 						"%s",
    714 						error_message(err));
    715 				goto errout;
    716 			}
    717 			reply_to_client(ap, KRB_RESPONSE, outbuf.data,
    718 					outbuf.length);
    719 		}
    720 		if (krb5_unparse_name(telnet_context,
    721 				    ticket->enc_part2->client,
    722 				    &name))
    723 			name = 0;
    724 		reply_to_client(ap, KRB_ACCEPT, name, name ? -1 : 0);
    725 		if (auth_debug) {
    726 			syslog(LOG_NOTICE,
    727 			    "\tKerberos5 identifies user as ``%s''\r\n",
    728 			    name ? name : "");
    729 		}
    730 		if (name != NULL) {
    731 			krb5_name = (char *)strdup(name);
    732 		}
    733 		auth_finished(ap, AUTH_USER);
    734 
    735 		if (name != NULL)
    736 			free(name);
    737 		krb5_auth_con_getremotesubkey(telnet_context, auth_context,
    738 					    &newkey);
    739 		if (session_key != NULL) {
    740 			krb5_free_keyblock(telnet_context, session_key);
    741 			session_key = 0;
    742 		}
    743 		if (newkey != NULL) {
    744 			krb5_copy_keyblock(telnet_context,
    745 					newkey, &session_key);
    746 			krb5_free_keyblock(telnet_context, newkey);
    747 		} else {
    748 			krb5_copy_keyblock(telnet_context,
    749 				ticket->enc_part2->session,
    750 				&session_key);
    751 		}
    752 
    753 		/*
    754 		 * Initialize encryption stuff.  Currently, we are only
    755 		 * supporting 8 byte keys and blocks. Check for this later.
    756 		 */
    757 		skey.type = SK_DES;
    758 		skey.length = DES_BLOCKSIZE;
    759 		skey.data = session_key->contents;
    760 		encrypt_session_key(&skey, &encr_data.encrypt);
    761 		encrypt_session_key(&skey, &encr_data.decrypt);
    762 		break;
    763 	case KRB_FORWARD:
    764 		inbuf.length = cnt;
    765 		inbuf.data = (char *)data;
    766 		if (auth_debug)
    767 			(void) fprintf(stderr,
    768 				    "RCVD KRB_FORWARD data (%d bytes)\n", cnt);
    769 
    770 		if (auth_context != NULL) {
    771 			krb5_rcache rcache;
    772 
    773 			err = krb5_auth_con_getrcache(telnet_context,
    774 						    auth_context, &rcache);
    775 			if (!err && !rcache) {
    776 				err = krb5_sname_to_principal(telnet_context,
    777 					0, 0, KRB5_NT_SRV_HST, &server);
    778 				if (!err) {
    779 					err = krb5_get_server_rcache(
    780 						telnet_context,
    781 						krb5_princ_component(
    782 							telnet_context,
    783 							server, 0),
    784 						&rcache);
    785 					krb5_free_principal(telnet_context,
    786 								server);
    787 				}
    788 			}
    789 			if (err) {
    790 				syslog(LOG_ERR,
    791 				    "Error allocating krb5 replay cache: %s",
    792 				    error_message(err));
    793 			} else {
    794 				err = krb5_auth_con_setrcache(telnet_context,
    795 					auth_context, rcache);
    796 				if (err)
    797 					syslog(LOG_ERR,
    798 					    "Error creating krb5 replay cache:"
    799 					    " %s",
    800 					    error_message(err));
    801 			}
    802 		}
    803 		/*
    804 		 * Use the 'rsaddr' and 'rsport' (remote service addr/port)
    805 		 * from the original connection.  This data is used to
    806 		 * verify the forwarded credentials.
    807 		 */
    808 		if (!(err = krb5_auth_con_setaddrs(telnet_context, auth_context,
    809 					    NULL, &rsaddr)))
    810 			err = krb5_auth_con_setports(telnet_context,
    811 						auth_context, NULL, &rsport);
    812 
    813 		if (err == 0)
    814 			/*
    815 			 * If all is well, store the forwarded creds in
    816 			 * the users local credential cache.
    817 			 */
    818 			err = rd_and_store_forwarded_creds(telnet_context,
    819 							auth_context, &inbuf,
    820 							ticket,
    821 							AuthenticatingUser);
    822 		if (err) {
    823 			(void) snprintf(errbuf, sizeof (errbuf),
    824 					"Read forwarded creds failed: %s",
    825 					error_message(err));
    826 			syslog(LOG_ERR, "%s", errbuf);
    827 
    828 			reply_to_client(ap, KRB_FORWARD_REJECT, errbuf, -1);
    829 			if (auth_debug)
    830 				(void) fprintf(stderr,
    831 					    "\tCould not read "
    832 					    "forwarded credentials\r\n");
    833 		} else
    834 			reply_to_client(ap, KRB_FORWARD_ACCEPT, (void *) 0, 0);
    835 
    836 		if (rsaddr.contents != NULL)
    837 			free(rsaddr.contents);
    838 
    839 		if (rsport.contents != NULL)
    840 			free(rsport.contents);
    841 
    842 		if (auth_debug)
    843 			(void) fprintf(stderr, "\tForwarded "
    844 						"credentials obtained\r\n");
    845 		break;
    846 	default:
    847 		if (auth_debug)
    848 			(void) fprintf(stderr,
    849 				    "\tUnknown Kerberos option %d\r\n",
    850 				    data[-1]);
    851 		reply_to_client(ap, KRB_REJECT, (void *) 0, 0);
    852 		break;
    853 	}
    854 	return;
    855 
    856 errout:
    857 	reply_to_client(ap, KRB_REJECT, errbuf, -1);
    858 
    859 	if (auth_debug)
    860 		(void) fprintf(stderr, "\tKerberos V5 error: %s\r\n", errbuf);
    861 
    862 	syslog(LOG_ERR, "%s", errbuf);
    863 
    864 	if (auth_context != NULL) {
    865 		krb5_auth_con_free(telnet_context, auth_context);
    866 		auth_context = 0;
    867 	}
    868 }
    869 
    870 static int
    871 krb5_init()
    872 {
    873 	int code = 0;
    874 
    875 	if (telnet_context == NULL) {
    876 		code = krb5_init_context(&telnet_context);
    877 		if (code != 0 && auth_debug)
    878 			syslog(LOG_NOTICE,
    879 			    "Cannot initialize Kerberos V5: %s",
    880 			    error_message(code));
    881 	}
    882 
    883 	return (code);
    884 }
    885 
    886 static void
    887 auth_name(uchar_t *data, int cnt)
    888 {
    889 	char namebuf[MAXPRINCLEN];
    890 
    891 	if (cnt < 1) {
    892 		if (auth_debug)
    893 			(void) fprintf(stderr,
    894 				    "\t(auth_name) Empty NAME in auth "
    895 				    "reply\n");
    896 		return;
    897 	}
    898 	if (cnt > sizeof (namebuf)-1) {
    899 		if (auth_debug)
    900 			(void) fprintf(stderr,
    901 				    "\t(auth_name) NAME exceeds %d bytes\n",
    902 				sizeof (namebuf)-1);
    903 		return;
    904 	}
    905 	(void) memcpy((void *)namebuf, (void *)data, cnt);
    906 	namebuf[cnt] = 0;
    907 	if (auth_debug)
    908 		(void) fprintf(stderr, "\t(auth_name) name [%s]\n", namebuf);
    909 	AuthenticatingUser = (char *)strdup(namebuf);
    910 }
    911 
    912 static void
    913 auth_is(uchar_t *data, int cnt)
    914 {
    915 	AuthInfo *aptr = auth_list;
    916 
    917 	if (cnt < 2)
    918 		return;
    919 
    920 	/*
    921 	 * We failed to negoiate secure authentication
    922 	 */
    923 	if (data[0] == AUTHTYPE_NULL) {
    924 		auth_finished(0, AUTH_REJECT);
    925 		return;
    926 	}
    927 
    928 	while (aptr->AuthName != NULL &&
    929 	    (aptr->AuthName != data[0] || aptr->AuthHow != data[1]))
    930 		aptr++;
    931 
    932 	if (aptr != NULL) {
    933 		if (auth_debug)
    934 			(void) fprintf(stderr, "\t(auth_is) auth type is %s "
    935 				"(%d bytes)\n",	aptr->AuthString, cnt);
    936 
    937 		if (aptr->AuthName == AUTHTYPE_KERBEROS_V5)
    938 			kerberos5_is(aptr, data+2, cnt-2);
    939 	}
    940 }
    941 
    942 static int
    943 krb5_user_status(char *name, int namelen, int level)
    944 {
    945 	int retval = AUTH_USER;
    946 
    947 	if (auth_debug)
    948 		(void) fprintf(stderr, "\t(krb5_user_status) level = %d "
    949 			"auth_level = %d  user = %s\n",
    950 			level, auth_level,
    951 			(AuthenticatingUser != NULL ? AuthenticatingUser : ""));
    952 
    953 	if (level < AUTH_USER)
    954 		return (level);
    955 
    956 	if (AuthenticatingUser != NULL &&
    957 	    (retval = krb5_kuserok(telnet_context, ticket->enc_part2->client,
    958 			    AuthenticatingUser))) {
    959 		(void) strncpy(name, AuthenticatingUser, namelen);
    960 		return (AUTH_VALID);
    961 	} else {
    962 		if (!retval)
    963 			syslog(LOG_ERR,
    964 			    "Krb5 principal lacks permission to "
    965 			    "access local account for %s",
    966 			    AuthenticatingUser);
    967 		return (AUTH_USER);
    968 	}
    969 }
    970 
    971 /*
    972  * Wrapper around /dev/urandom
    973  */
    974 static int
    975 getrandom(char *buf, int buflen)
    976 {
    977 	static int devrandom = -1;
    978 
    979 	if (devrandom == -1 &&
    980 	    (devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
    981 		fatalperror(net, "Unable to open /dev/urandom: ",
    982 			    errno);
    983 		return (-1);
    984 	}
    985 
    986 	if (read(devrandom, buf, buflen) == -1) {
    987 		fatalperror(net, "Unable to read from /dev/urandom: ",
    988 			    errno);
    989 		return (-1);
    990 	}
    991 
    992 	return (0);
    993 }
    994 
    995 /*
    996  * encrypt_init
    997  *
    998  * Initialize the encryption data structures
    999  */
   1000 static void
   1001 encrypt_init()
   1002 {
   1003 	(void) memset(&encr_data.encrypt, 0, sizeof (cipher_info_t));
   1004 	(void) memset(&encr_data.decrypt, 0, sizeof (cipher_info_t));
   1005 
   1006 	encr_data.encrypt.state = ENCR_STATE_NOT_READY;
   1007 	encr_data.decrypt.state = ENCR_STATE_NOT_READY;
   1008 }
<