Home | History | Annotate | Download | only in usr.sbin
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*	Copyright(c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
      7 /*	  All Rights Reserved  	*/
      8 
      9 /*
     10  * Copyright (c) 1983 The Regents of the University of California.
     11  * All rights reserved.
     12  *
     13  * Redistribution and use in source and binary forms are permitted
     14  * provided that the above copyright notice and this paragraph are
     15  * duplicated in all such forms and that any documentation,
     16  * advertising materials, and other materials related to such
     17  * distribution and use acknowledge that the software was developed
     18  * by the University of California, Berkeley.  The name of the
     19  * University may not be used to endorse or promote products derived
     20  * from this software without specific prior written permission.
     21  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     23  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * remote login server:
     30  *	remuser\0
     31  *	locuser\0
     32  *	terminal info\0
     33  *	data
     34  */
     35 
     36 #include <time.h>
     37 #include <sys/types.h>
     38 #include <sys/stat.h>
     39 #include <sys/socket.h>
     40 #include <sys/wait.h>
     41 
     42 #include <netinet/in.h>
     43 
     44 #include <errno.h>
     45 #include <signal.h>
     46 #include <fcntl.h>
     47 #include <stdio.h>
     48 #include <netdb.h>
     49 #include <syslog.h>
     50 #include <string.h>
     51 #include <unistd.h>
     52 #include <stdlib.h>
     53 #include <alloca.h>
     54 #include <stropts.h>
     55 #include <sac.h>	/* for SC_WILDC */
     56 #include <utmpx.h>
     57 #include <sys/filio.h>
     58 #include <sys/logindmux.h>
     59 #include <sys/rlioctl.h>
     60 #include <sys/termios.h>
     61 #include <sys/tihdr.h>
     62 #include <arpa/inet.h>
     63 #include <security/pam_appl.h>
     64 #include <strings.h>
     65 #include <com_err.h>
     66 #include <k5-int.h>
     67 #include <kcmd.h>
     68 #include <krb5_repository.h>
     69 #include <sys/cryptmod.h>
     70 #include <bsm/adt.h>
     71 #include <addr_match.h>
     72 #include <store_forw_creds.h>
     73 
     74 #define	KRB5_RECVAUTH_V5 5
     75 #define	UT_NAMESIZE	sizeof (((struct utmpx *)0)->ut_name)
     76 
     77 static char lusername[UT_NAMESIZE+1];
     78 static char rusername[UT_NAMESIZE+1];
     79 static char *krusername = NULL;
     80 static char term[64];
     81 
     82 static krb5_ccache ccache = NULL;
     83 static krb5_keyblock *session_key = NULL;
     84 static int chksum_flag = 0;
     85 static int use_auth = 0;
     86 static enum kcmd_proto kcmd_protocol;
     87 #ifdef ALLOW_KCMD_V2
     88 static krb5_data encr_iv = { NULL, 0 };
     89 static krb5_data decr_iv = { NULL, 0 };
     90 #endif /* ALLOW_KCMD_V2 */
     91 
     92 #define	CHKSUM_REQUIRED 0x01
     93 #define	CHKSUM_IGNORED  0x02
     94 #define	VALID_CHKSUM(x) ((x) == 0 || (x) == CHKSUM_REQUIRED ||\
     95 			(x) == CHKSUM_IGNORED)
     96 
     97 #define	PWD_IF_FAIL  0x01
     98 #define	PWD_REQUIRED 0x02
     99 
    100 #define	AUTH_NONE 0x00
    101 
    102 #define	ARGSTR "k5exEXciM:s:S:D:"
    103 #define	DEFAULT_TOS 16
    104 
    105 #define	KRB5_PROG_NAME "krlogin"
    106 
    107 #define	SECURE_MSG "This rlogin session is using encryption " \
    108 	"for all data transmissions.\r\n"
    109 
    110 #define	KRB_V5_SENDAUTH_VERS	"KRB5_SENDAUTH_V1.0"
    111 #define	KRB5_RECVAUTH_V5	5
    112 
    113 static krb5_error_code krb5_compat_recvauth(krb5_context context,
    114 					    krb5_auth_context *auth_context,
    115 					    krb5_pointer fdp,
    116 					    krb5_principal server,
    117 					    krb5_int32 flags,
    118 					    krb5_keytab keytab,
    119 					    krb5_ticket **ticket,
    120 					    krb5_int32 *auth_sys,
    121 					    krb5_data *version);
    122 
    123 static void do_krb_login(int, char *, char *, krb5_context, int, krb5_keytab);
    124 static int configure_stream(int, krb5_keyblock *, int, krb5_data *, uint_t);
    125 
    126 extern krb5_error_code krb5_read_message(krb5_context, krb5_pointer,
    127 					krb5_data *);
    128 extern krb5_error_code krb5_net_read(krb5_context, int, char *, int);
    129 
    130 #define	LOGIN_PROGRAM "/bin/login"
    131 
    132 #define	DEFAULT_PROG_NAME	"rlogin"
    133 
    134 static const char *pam_prog_name = DEFAULT_PROG_NAME;
    135 static void	rmut(void);
    136 static void	doit(int,  struct sockaddr_storage *, krb5_context, int,
    137 		    krb5_keytab);
    138 static void	protocol(int, int, int);
    139 
    140 static int	readstream(int, char *, int);
    141 static void	fatal(int, const char *);
    142 static void	fatalperror(int, const char *);
    143 static int	send_oob(int fd, void *ptr, size_t count);
    144 static int	removemod(int f, char *modname);
    145 
    146 static int
    147 issock(int fd)
    148 {
    149 	struct stat stats;
    150 
    151 	if (fstat(fd, &stats) == -1)
    152 		return (0);
    153 	return (S_ISSOCK(stats.st_mode));
    154 }
    155 
    156 /*
    157  * audit_rlogin_settid stores the terminal id while it is still
    158  * available.  Subsequent calls to adt_load_hostname() return
    159  * the id which is stored here.
    160  */
    161 static int
    162 audit_rlogin_settid(int fd) {
    163 	adt_session_data_t	*ah;
    164 	adt_termid_t		*termid;
    165 	int			rc;
    166 
    167 	if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
    168 		if ((rc = adt_load_termid(fd, &termid)) == 0) {
    169 			if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
    170 			    ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
    171 			    termid, ADT_SETTID)) == 0)
    172 				(void) adt_set_proc(ah);
    173 			free(termid);
    174 		}
    175 		(void) adt_end_session(ah);
    176 	}
    177 	return (rc);
    178 }
    179 
    180 
    181 /* ARGSUSED */
    182 int
    183 main(int argc, char *argv[])
    184 {
    185 	int on = 1;
    186 	socklen_t fromlen;
    187 	struct sockaddr_storage from;
    188 	int fd = -1;
    189 
    190 	extern char *optarg;
    191 	char c;
    192 	int tos = -1;
    193 	krb5_context krb_context;
    194 	krb5_keytab keytab = NULL;
    195 	krb5_error_code status;
    196 	char *realm = NULL;
    197 	char *keytab_file = NULL;
    198 	int encr_flag = 0;
    199 	struct sockaddr_storage ouraddr;
    200 	socklen_t ourlen;
    201 #ifdef DEBUG
    202 	int debug_port = 0;
    203 #endif /* DEBUG */
    204 	openlog("rlogind", LOG_PID | LOG_ODELAY, LOG_DAEMON);
    205 
    206 	while ((c = getopt(argc, argv, ARGSTR)) != -1) {
    207 		switch (c) {
    208 		case 'k':
    209 		case '5':
    210 			use_auth = KRB5_RECVAUTH_V5;
    211 			break;
    212 		case 'e':
    213 		case 'E':
    214 		case 'x':
    215 		case 'X':
    216 			encr_flag = 1;
    217 			break;
    218 		case 'M':
    219 			realm = (char *)strdup(optarg);
    220 			break;
    221 		case 'S':
    222 			keytab_file = (char *)strdup(optarg);
    223 			break;
    224 		case 'c':
    225 			chksum_flag |= CHKSUM_REQUIRED;
    226 			break;
    227 		case 'i':
    228 			chksum_flag |= CHKSUM_IGNORED;
    229 			break;
    230 		case 's':
    231 			if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
    232 			    tos > 255) {
    233 				syslog(LOG_ERR, "%s: illegal tos value: "
    234 				    "%s\n", argv[0], optarg);
    235 			} else {
    236 				if (tos < 0)
    237 					tos = DEFAULT_TOS;
    238 			}
    239 			break;
    240 #ifdef DEBUG
    241 		case 'D':
    242 			debug_port = atoi(optarg);
    243 			break;
    244 #endif /* DEBUG */
    245 		default:
    246 			syslog(LOG_ERR, "Unrecognized command line option "
    247 			    "(%s), exiting", argv[optind]);
    248 			exit(EXIT_FAILURE);
    249 		}
    250 	}
    251 	if (use_auth == KRB5_RECVAUTH_V5) {
    252 		status = krb5_init_context(&krb_context);
    253 		if (status) {
    254 			syslog(LOG_ERR, "Error initializing krb5: %s",
    255 			    error_message(status));
    256 			exit(EXIT_FAILURE);
    257 		}
    258 		if (realm != NULL)
    259 			krb5_set_default_realm(krb_context, realm);
    260 		if (keytab_file != NULL) {
    261 			if ((status = krb5_kt_resolve(krb_context,
    262 						    keytab_file,
    263 						    &keytab))) {
    264 				com_err(argv[0],
    265 					status,
    266 					"while resolving srvtab file %s",
    267 					keytab_file);
    268 				exit(EXIT_FAILURE);
    269 			}
    270 		}
    271 	}
    272 
    273 #ifdef DEBUG
    274 	if (debug_port) {
    275 		int s;
    276 		struct sockaddr_in sin;
    277 
    278 		if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
    279 			fatalperror(STDERR_FILENO, "Error in socket");
    280 		}
    281 
    282 		(void) memset((char *)&sin, 0, sizeof (sin));
    283 		sin.sin_family = AF_INET;
    284 		sin.sin_port = htons(debug_port);
    285 		sin.sin_addr.s_addr = INADDR_ANY;
    286 
    287 		(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
    288 					(char *)&on, sizeof (on));
    289 
    290 		if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
    291 			fatalperror(STDERR_FILENO, "bind error");
    292 		}
    293 
    294 		if ((listen(s, 5)) < 0) {
    295 			fatalperror(STDERR_FILENO, "listen error");
    296 		}
    297 
    298 		fromlen = sizeof (from);
    299 		if ((fd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) {
    300 			fatalperror(STDERR_FILENO, "accept error");
    301 		}
    302 
    303 		(void) close(s);
    304 	} else
    305 #endif /* DEBUG */
    306 	{
    307 		if (!issock(STDIN_FILENO))
    308 			fatal(STDIN_FILENO,
    309 				"stdin is not a socket file descriptor");
    310 		fd = STDIN_FILENO;
    311 	}
    312 
    313 	fromlen = sizeof (from);
    314 	if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0)
    315 		fatalperror(STDERR_FILENO, "getpeername");
    316 
    317 	if (audit_rlogin_settid(fd))	/* set terminal ID */
    318 		fatalperror(STDERR_FILENO, "audit");
    319 
    320 	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
    321 	    sizeof (on)) < 0)
    322 		syslog(LOG_WARNING, "setsockopt(SO_KEEPALIVE): %m");
    323 
    324 	if (!VALID_CHKSUM(chksum_flag)) {
    325 		syslog(LOG_ERR, "Configuration error: mutually exclusive "
    326 		    "options specified (-c and -i)");
    327 		fatal(fd, "Checksums are required and ignored (-c and -i);"
    328 		    "these options are mutually exclusive - check "
    329 		    "the documentation.");
    330 	}
    331 	ourlen = sizeof (ouraddr);
    332 	if (getsockname(fd, (struct sockaddr *)&ouraddr, &ourlen) == -1) {
    333 		syslog(LOG_ERR, "getsockname error: %m");
    334 		exit(EXIT_FAILURE);
    335 	}
    336 
    337 	if (tos != -1 &&
    338 	    ouraddr.ss_family != AF_INET6 &&
    339 	    setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
    340 					sizeof (tos)) < 0 &&
    341 					errno != ENOPROTOOPT) {
    342 		syslog(LOG_ERR, "setsockopt(IP_TOS %d): %m", tos);
    343 	}
    344 	doit(fd, &from, krb_context, encr_flag, keytab);
    345 	return (0);
    346 }
    347 
    348 static void	cleanup(int);
    349 static int	nsize = 0;	/* bytes read prior to pushing rlmod */
    350 static char	*rlbuf;		/* buffer where nbytes are read to */
    351 static char	*line;
    352 
    353 static struct winsize win = { 0, 0, 0, 0 };
    354 static pid_t pid;
    355 static char hostname[MAXHOSTNAMELEN + 1];
    356 
    357 static void
    358 getstr(int f, char *buf, int cnt, char *err)
    359 {
    360 	char c;
    361 	do {
    362 		if (read(f, &c, 1) != 1 || (--cnt < 0)) {
    363 			syslog(LOG_ERR, "Error reading \'%s\' field", err);
    364 			exit(EXIT_FAILURE);
    365 		}
    366 		*buf++ = c;
    367 	} while (c != '\0');
    368 }
    369 
    370 static krb5_error_code
    371 recvauth(int f,
    372 	krb5_context krb_context,
    373 	unsigned int *valid_checksum,
    374 	krb5_ticket **ticket,
    375 	int *auth_type,
    376 	krb5_principal *client,
    377 	int encr_flag,
    378 	krb5_keytab keytab)
    379 {
    380 	krb5_error_code status = 0;
    381 	krb5_auth_context auth_context = NULL;
    382 	krb5_rcache rcache;
    383 	krb5_authenticator *authenticator;
    384 	krb5_data inbuf;
    385 	krb5_data auth_version;
    386 
    387 	*valid_checksum = 0;
    388 
    389 	if ((status = krb5_auth_con_init(krb_context, &auth_context)))
    390 		return (status);
    391 
    392 	/* Only need remote address for rd_cred() to verify client */
    393 	if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f,
    394 			KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
    395 		return (status);
    396 
    397 	status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache);
    398 	if (status)
    399 		return (status);
    400 
    401 	if (!rcache) {
    402 		krb5_principal server;
    403 
    404 		status = krb5_sname_to_principal(krb_context, 0, 0,
    405 						KRB5_NT_SRV_HST, &server);
    406 		if (status)
    407 			return (status);
    408 
    409 		status = krb5_get_server_rcache(krb_context,
    410 				krb5_princ_component(krb_context, server, 0),
    411 				&rcache);
    412 		krb5_free_principal(krb_context, server);
    413 		if (status)
    414 			return (status);
    415 
    416 		status = krb5_auth_con_setrcache(krb_context, auth_context,
    417 						rcache);
    418 		if (status)
    419 			return (status);
    420 	}
    421 	if ((status = krb5_compat_recvauth(krb_context,
    422 					&auth_context,
    423 					&f,
    424 					NULL,	/* Specify daemon principal */
    425 					0,	/* no flags */
    426 					keytab,	/* NULL to use v5srvtab */
    427 					ticket,	/* return ticket */
    428 					auth_type, /* authentication system */
    429 					&auth_version))) {
    430 		if (*auth_type == KRB5_RECVAUTH_V5) {
    431 			/*
    432 			 * clean up before exiting
    433 			 */
    434 			getstr(f, rusername, sizeof (rusername), "remuser");
    435 			getstr(f, lusername, sizeof (lusername), "locuser");
    436 			getstr(f, term, sizeof (term), "Terminal type");
    437 		}
    438 		return (status);
    439 	}
    440 
    441 	getstr(f, lusername, sizeof (lusername), "locuser");
    442 	getstr(f, term, sizeof (term), "Terminal type");
    443 
    444 	kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
    445 	if (auth_version.length != 9 || auth_version.data == NULL) {
    446 		syslog(LOG_ERR, "Bad application protocol version length in "
    447 		    "KRB5 exchange, exiting");
    448 		fatal(f, "Bad application version length, exiting.");
    449 	}
    450 	/*
    451 	 * Determine which Kerberos CMD protocol was used.
    452 	 */
    453 	if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) {
    454 		kcmd_protocol = KCMD_OLD_PROTOCOL;
    455 	} else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) {
    456 		kcmd_protocol = KCMD_NEW_PROTOCOL;
    457 	} else {
    458 		syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting",
    459 			(char *)auth_version.data);
    460 		fatal(f, "Unrecognized KCMD protocol, exiting");
    461 	}
    462 
    463 	if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag &&
    464 		kcmd_protocol == KCMD_OLD_PROTOCOL) {
    465 		if ((status = krb5_auth_con_getauthenticator(krb_context,
    466 							    auth_context,
    467 							    &authenticator)))
    468 			return (status);
    469 		if (authenticator->checksum) {
    470 			struct sockaddr_storage adr;
    471 			int adr_length = sizeof (adr);
    472 			int buflen;
    473 			krb5_data input;
    474 			krb5_keyblock key;
    475 			char *chksumbuf;
    476 
    477 			/*
    478 			 * Define the lenght of the chksum buffer.
    479 			 * chksum string = "[portnum]:termstr:username"
    480 			 * The extra 32 is to hold a integer string for
    481 			 * the portnumber.
    482 			 */
    483 			buflen = strlen(term) + strlen(lusername) + 32;
    484 			chksumbuf = (char *)malloc(buflen);
    485 			if (chksumbuf == 0) {
    486 				krb5_free_authenticator(krb_context,
    487 							authenticator);
    488 				fatal(f, "Out of memory error");
    489 			}
    490 
    491 			if (getsockname(f, (struct sockaddr *)&adr,
    492 							&adr_length) != 0) {
    493 				krb5_free_authenticator(krb_context,
    494 							authenticator);
    495 				fatal(f, "getsockname error");
    496 			}
    497 
    498 			(void) snprintf(chksumbuf, buflen,
    499 					"%u:%s%s",
    500 					ntohs(SOCK_PORT(adr)),
    501 					term, lusername);
    502 
    503 			input.data = chksumbuf;
    504 			input.length = strlen(chksumbuf);
    505 			key.contents = (*ticket)->enc_part2->session->contents;
    506 			key.length = (*ticket)->enc_part2->session->length;
    507 			status = krb5_c_verify_checksum(krb_context,
    508 						&key, 0,
    509 						&input,
    510 						authenticator->checksum,
    511 						valid_checksum);
    512 
    513 			if (status == 0 && *valid_checksum == 0)
    514 				status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
    515 
    516 			if (chksumbuf)
    517 				krb5_xfree(chksumbuf);
    518 			if (status) {
    519 				krb5_free_authenticator(krb_context,
    520 							authenticator);
    521 				return (status);
    522 			}
    523 		}
    524 		krb5_free_authenticator(krb_context, authenticator);
    525 	}
    526 
    527 	if ((status = krb5_copy_principal(krb_context,
    528 					(*ticket)->enc_part2->client,
    529 					client)))
    530 		return (status);
    531 
    532 	/* Get the Unix username of the remote user */
    533 	getstr(f, rusername, sizeof (rusername), "remuser");
    534 
    535 	/* Get the Kerberos principal name string of the remote user */
    536 	if ((status = krb5_unparse_name(krb_context, *client, &krusername)))
    537 		return (status);
    538 
    539 #ifdef DEBUG
    540 	syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s",
    541 	    (krusername != NULL ? krusername : "<unknown>"));
    542 #endif
    543 
    544 	if (encr_flag) {
    545 		status = krb5_auth_con_getremotesubkey(krb_context,
    546 						    auth_context,
    547 						    &session_key);
    548 		if (status) {
    549 			syslog(LOG_ERR, "Error getting KRB5 session "
    550 			    "subkey, exiting");
    551 			fatal(f, "Error getting KRB5 session subkey, exiting");
    552 		}
    553 		/*
    554 		 * The "new" protocol requires that a subkey be sent.
    555 		 */
    556 		if (session_key == NULL &&
    557 		    kcmd_protocol == KCMD_NEW_PROTOCOL) {
    558 			syslog(LOG_ERR, "No KRB5 session subkey sent, exiting");
    559 			fatal(f, "No KRB5 session subkey sent, exiting");
    560 		}
    561 		/*
    562 		 * The "old" protocol does not permit an authenticator subkey.
    563 		 * The key is taken from the ticket instead (see below).
    564 		 */
    565 		if (session_key != NULL &&
    566 		    kcmd_protocol == KCMD_OLD_PROTOCOL) {
    567 			syslog(LOG_ERR, "KRB5 session subkey not permitted "
    568 			    "with old KCMD protocol, exiting");
    569 
    570 			fatal(f, "KRB5 session subkey not permitted "
    571 			    "with old KCMD protocol, exiting");
    572 		}
    573 		/*
    574 		 * If no key at this point, use the session key from
    575 		 * the ticket.
    576 		 */
    577 		if (session_key == NULL) {
    578 			/*
    579 			 * Save the session key so we can configure the crypto
    580 			 * module later.
    581 			 */
    582 			status = krb5_copy_keyblock(krb_context,
    583 					    (*ticket)->enc_part2->session,
    584 					    &session_key);
    585 			if (status) {
    586 				syslog(LOG_ERR, "krb5_copy_keyblock failed");
    587 				fatal(f, "krb5_copy_keyblock failed");
    588 			}
    589 		}
    590 		/*
    591 		 * If session key still cannot be found, we must
    592 		 * exit because encryption is required here
    593 		 * when encr_flag (-x) is set.
    594 		 */
    595 		if (session_key == NULL) {
    596 			syslog(LOG_ERR, "Could not find an encryption key,"
    597 				    "exiting");
    598 			fatal(f, "Encryption required but key not found, "
    599 			    "exiting");
    600 		}
    601 	}
    602 	/*
    603 	 * Use krb5_read_message to read the principal stuff.
    604 	 */
    605 	if ((status = krb5_read_message(krb_context, (krb5_pointer)&f,
    606 					&inbuf)))
    607 		fatal(f, "Error reading krb5 message");
    608 
    609 	if (inbuf.length) { /* Forwarding being done, read creds */
    610 		krb5_creds **creds = NULL;
    611 
    612 		if (status = krb5_rd_cred(krb_context, auth_context, &inbuf,
    613 					    &creds, NULL)) {
    614 			if (rcache)
    615 				(void) krb5_rc_close(krb_context, rcache);
    616 			krb5_free_creds(krb_context, *creds);
    617 			fatal(f, "Can't get forwarded credentials");
    618 		}
    619 
    620 		/* Store the forwarded creds in the ccache */
    621 		if (status = store_forw_creds(krb_context,
    622 					    creds, *ticket, lusername,
    623 					    &ccache)) {
    624 			if (rcache)
    625 				(void) krb5_rc_close(krb_context, rcache);
    626 			krb5_free_creds(krb_context, *creds);
    627 			fatal(f, "Can't store forwarded credentials");
    628 		}
    629 		krb5_free_creds(krb_context, *creds);
    630 	}
    631 
    632 	if (rcache)
    633 		(void) krb5_rc_close(krb_context, rcache);
    634 
    635 	return (status);
    636 }
    637 
    638 static void
    639 do_krb_login(int f, char *host_addr, char *hostname,
    640 	    krb5_context krb_context, int encr_flag,
    641 	    krb5_keytab keytab)
    642 {
    643 	krb5_error_code status;
    644 	uint_t valid_checksum;
    645 	krb5_ticket	*ticket = NULL;
    646 	int auth_sys = 0;
    647 	int auth_sent = 0;
    648 	krb5_principal client = NULL;
    649 
    650 	if (getuid())
    651 		fatal(f, "Error authorizing KRB5 connection, "
    652 			"server lacks privilege");
    653 
    654 	status = recvauth(f, krb_context, &valid_checksum, &ticket,
    655 			&auth_sys, &client, encr_flag, keytab);
    656 	if (status) {
    657 		if (ticket)
    658 			krb5_free_ticket(krb_context, ticket);
    659 		if (status != 255)
    660 			syslog(LOG_ERR,
    661 			    "Authentication failed from %s(%s): %s\n",
    662 			    host_addr, hostname, error_message(status));
    663 		fatal(f, "Kerberos authentication failed, exiting");
    664 	}
    665 
    666 	if (auth_sys != KRB5_RECVAUTH_V5) {
    667 		fatal(f, "This server only supports Kerberos V5");
    668 	} else {
    669 		/*
    670 		 * Authenticated OK, now check authorization.
    671 		 */
    672 		if (client && krb5_kuserok(krb_context, client, lusername))
    673 		    auth_sent = KRB5_RECVAUTH_V5;
    674 	}
    675 
    676 	if (auth_sent == KRB5_RECVAUTH_V5 &&
    677 	    kcmd_protocol == KCMD_OLD_PROTOCOL &&
    678 	    chksum_flag == CHKSUM_REQUIRED && !valid_checksum) {
    679 		syslog(LOG_ERR, "Client did not supply required checksum, "
    680 		    "connection rejected.");
    681 		fatal(f, "Client did not supply required checksum, "
    682 		    "connection rejected.");
    683 	}
    684 
    685 	if (auth_sys != auth_sent) {
    686 		char *msg_fail = NULL;
    687 		int msgsize = 0;
    688 
    689 		if (ticket)
    690 			krb5_free_ticket(krb_context, ticket);
    691 
    692 		if (krusername != NULL) {
    693 			/*
    694 			 * msgsize must be enough to hold
    695 			 * krusername, lusername and a brief
    696 			 * message describing the failure.
    697 			 */
    698 			msgsize = strlen(krusername) +
    699 				strlen(lusername) + 80;
    700 			msg_fail = (char *)malloc(msgsize);
    701 		}
    702 		if (msg_fail == NULL) {
    703 			syslog(LOG_ERR, "User is not authorized to login to "
    704 			    "specified account");
    705 
    706 			fatal(f, "User is not authorized to login to "
    707 			    "specified account");
    708 		}
    709 		if (auth_sent != 0)
    710 			(void) snprintf(msg_fail, msgsize,
    711 					"Access denied because of improper "
    712 					"KRB5 credentials");
    713 		else
    714 			(void) snprintf(msg_fail, msgsize,
    715 					"User %s is not authorized to login "
    716 					"to account %s",
    717 					krusername, lusername);
    718 		syslog(LOG_ERR, "%s", msg_fail);
    719 		fatal(f, msg_fail);
    720 	}
    721 }
    722 
    723 /*
    724  * stop_stream
    725  *
    726  * Utility routine to send a CRYPTIOCSTOP ioctl to the
    727  * crypto module(cryptmod).
    728  */
    729 static void
    730 stop_stream(int fd, int dir)
    731 {
    732 	struct strioctl  crioc;
    733 	uint32_t stopdir = dir;
    734 
    735 	crioc.ic_cmd = CRYPTIOCSTOP;
    736 	crioc.ic_timout = -1;
    737 	crioc.ic_len = sizeof (stopdir);
    738 	crioc.ic_dp = (char *)&stopdir;
    739 
    740 	if (ioctl(fd, I_STR, &crioc))
    741 		syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
    742 }
    743 
    744 /*
    745  * start_stream
    746  *
    747  * Utility routine to send a CRYPTIOCSTART ioctl to the
    748  * crypto module(cryptmod).  This routine may contain optional
    749  * payload data that the cryptmod will interpret as bytes that
    750  * need to be decrypted and sent back up to the application
    751  * via the data stream.
    752  */
    753 static void
    754 start_stream(int fd, int dir)
    755 {
    756 	struct strioctl crioc;
    757 	uint32_t iocval;
    758 	size_t datalen = 0;
    759 	char *data = NULL;
    760 
    761 	if (dir == CRYPT_DECRYPT) {
    762 		iocval = CRYPTIOCSTARTDEC;
    763 
    764 		/* Look for data not yet processed */
    765 		if (ioctl(fd, I_NREAD, &datalen) < 0) {
    766 			syslog(LOG_ERR, "I_NREAD returned error %m");
    767 			datalen = 0;
    768 		} else {
    769 			if (datalen > 0) {
    770 				data = malloc(datalen);
    771 				if (data != NULL) {
    772 					int nbytes = read(fd, data, datalen);
    773 					datalen = nbytes;
    774 				} else {
    775 					syslog(LOG_ERR,
    776 						"malloc error (%d bytes)",
    777 						datalen);
    778 					datalen = 0;
    779 				}
    780 			} else {
    781 				datalen = 0;
    782 			}
    783 		}
    784 	} else {
    785 		iocval = CRYPTIOCSTARTENC;
    786 	}
    787 
    788 	crioc.ic_cmd = iocval;
    789 	crioc.ic_timout = -1;
    790 	crioc.ic_len = datalen;
    791 	crioc.ic_dp = data;
    792 
    793 	if (ioctl(fd, I_STR, &crioc))
    794 		syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
    795 
    796 	if (data != NULL)
    797 		free(data);
    798 }
    799 
    800 static int
    801 configure_stream(int fd, krb5_keyblock *skey, int dir, krb5_data *ivec,
    802 		uint_t iv_usage)
    803 {
    804 	struct cr_info_t setup_info;
    805 	struct strioctl crioc;
    806 	int retval = 0;
    807 
    808 	switch (skey->enctype) {
    809 	case ENCTYPE_DES_CBC_CRC:
    810 		setup_info.crypto_method = CRYPT_METHOD_DES_CBC_CRC;
    811 		break;
    812 	case ENCTYPE_DES_CBC_MD5:
    813 		setup_info.crypto_method = CRYPT_METHOD_DES_CBC_MD5;
    814 		break;
    815 	case ENCTYPE_DES_CBC_RAW:
    816 		setup_info.crypto_method = CRYPT_METHOD_DES_CBC_NULL;
    817 		break;
    818 	case ENCTYPE_DES3_CBC_SHA1:
    819 		setup_info.crypto_method = CRYPT_METHOD_DES3_CBC_SHA1;
    820 		break;
    821 	case ENCTYPE_ARCFOUR_HMAC:
    822 		setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5;
    823 		break;
    824 	case ENCTYPE_ARCFOUR_HMAC_EXP:
    825 		setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5_EXP;
    826 		break;
    827 	case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
    828 		setup_info.crypto_method = CRYPT_METHOD_AES128;
    829 		break;
    830 	case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
    831 		setup_info.crypto_method = CRYPT_METHOD_AES256;
    832 		break;
    833 	default:
    834 		syslog(LOG_ERR, "Enctype in kerberos session key "
    835 		    "is not supported by crypto module(%d)",
    836 		    skey->enctype);
    837 		return (-1);
    838 	}
    839 	if (ivec == NULL || ivec->length == 0) {
    840 		(void) memset(&setup_info.ivec, 0, sizeof (setup_info.ivec));
    841 
    842 		if (skey->enctype != ENCTYPE_ARCFOUR_HMAC &&
    843 		    skey->enctype != ENCTYPE_ARCFOUR_HMAC_EXP)
    844 			/* Kerberos IVs are 8 bytes long for DES keys */
    845 			setup_info.iveclen = KRB5_MIT_DES_KEYSIZE;
    846 		else
    847 			setup_info.iveclen = 0;
    848 	} else {
    849 		(void) memcpy(&setup_info.ivec, ivec->data, ivec->length);
    850 		setup_info.iveclen = ivec->length;
    851 	}
    852 
    853 	setup_info.ivec_usage = iv_usage;
    854 	(void) memcpy(&setup_info.key, skey->contents, skey->length);
    855 
    856 	setup_info.keylen = skey->length;
    857 	setup_info.direction_mask = dir;
    858 	/*
    859 	 * R* commands get special handling by crypto module -
    860 	 * 4 byte length field is used before each encrypted block
    861 	 * of data.
    862 	 */
    863 	setup_info.option_mask = (kcmd_protocol == KCMD_OLD_PROTOCOL ?
    864 				CRYPTOPT_RCMD_MODE_V1 :
    865 				CRYPTOPT_RCMD_MODE_V2);
    866 
    867 	crioc.ic_cmd = CRYPTIOCSETUP;
    868 	crioc.ic_timout = -1;
    869 	crioc.ic_len = sizeof (setup_info);
    870 	crioc.ic_dp = (char *)&setup_info;
    871 
    872 	if (ioctl(fd, I_STR, &crioc)) {
    873 		syslog(LOG_ERR, "Error sending CRYPTIOCSETUP ioctl: %m");
    874 		retval = -1;
    875 	}
    876 	return (retval);
    877 }
    878 
    879 static krb5_error_code
    880 krb5_compat_recvauth(krb5_context context,
    881 		    krb5_auth_context *auth_context,
    882 		    krb5_pointer fdp,	/* IN */
    883 		    krb5_principal server,	/* IN */
    884 		    krb5_int32 flags,	/* IN */
    885 		    krb5_keytab keytab,	/* IN */
    886 		    krb5_ticket **ticket, /* OUT */
    887 		    krb5_int32 *auth_sys, /* OUT */
    888 		    krb5_data *version)   /* OUT */
    889 {
    890 	krb5_int32 vlen;
    891 	char	*buf;
    892 	int	len, length;
    893 	krb5_int32	retval;
    894 	int		fd = *((int *)fdp);
    895 
    896 	if ((retval = krb5_net_read(context, fd, (char *)&vlen, 4)) != 4)
    897 		return ((retval < 0) ? errno : ECONNABORTED);
    898 
    899 	/*
    900 	 * Assume that we're talking to a V5 recvauth; read in the
    901 	 * the version string, and make sure it matches.
    902 	 */
    903 	len = (int)ntohl(vlen);
    904 
    905 	if (len < 0 || len > 255)
    906 		return (KRB5_SENDAUTH_BADAUTHVERS);
    907 
    908 	buf = malloc(len);
    909 	if (buf == NULL)
    910 		return (ENOMEM);
    911 
    912 	length = krb5_net_read(context, fd, buf, len);
    913 	if (len != length) {
    914 		krb5_xfree(buf);
    915 		return ((len < 0) ? errno : ECONNABORTED);
    916 	}
    917 
    918 	if (strcmp(buf, KRB_V5_SENDAUTH_VERS) != 0) {
    919 		krb5_xfree(buf);
    920 		return (KRB5_SENDAUTH_BADAUTHVERS);
    921 	}
    922 	krb5_xfree(buf);
    923 
    924 	*auth_sys = KRB5_RECVAUTH_V5;
    925 
    926 	retval = krb5_recvauth_version(context, auth_context, fdp,
    927 				    server, flags | KRB5_RECVAUTH_SKIP_VERSION,
    928 				    keytab, ticket, version);
    929 
    930 	return (retval);
    931 }
    932 
    933 
    934 static void
    935 doit(int f,
    936 	struct sockaddr_storage *fromp,
    937 	krb5_context krb_context,
    938 	int encr_flag,
    939 	krb5_keytab keytab)
    940 {
    941 	int p, t, on = 1;
    942 	char c;
    943 	char abuf[INET6_ADDRSTRLEN];
    944 	struct sockaddr_in *sin;
    945 	struct sockaddr_in6 *sin6;
    946 	int fromplen;
    947 	in_port_t port;
    948 	struct termios tp;
    949 	boolean_t bad_port;
    950 	boolean_t no_name;
    951 	char rhost_addra[INET6_ADDRSTRLEN];
    952 
    953 	if (!(rlbuf = malloc(BUFSIZ))) {
    954 		syslog(LOG_ERR, "rlbuf malloc failed\n");
    955 		exit(EXIT_FAILURE);
    956 	}
    957 	(void) alarm(60);
    958 	if (read(f, &c, 1) != 1 || c != 0) {
    959 		syslog(LOG_ERR, "failed to receive protocol zero byte\n");
    960 		exit(EXIT_FAILURE);
    961 	}
    962 	(void) alarm(0);
    963 	if (fromp->ss_family == AF_INET) {
    964 		sin = (struct sockaddr_in *)fromp;
    965 		port = sin->sin_port = ntohs((ushort_t)sin->sin_port);
    966 		fromplen = sizeof (struct sockaddr_in);
    967 
    968 		if (!inet_ntop(AF_INET, &sin->sin_addr,
    969 			    rhost_addra, sizeof (rhost_addra)))
    970 			goto badconversion;
    971 	} else if (fromp->ss_family == AF_INET6) {
    972 		sin6 = (struct sockaddr_in6 *)fromp;
    973 		port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port);
    974 		fromplen = sizeof (struct sockaddr_in6);
    975 
    976 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    977 			struct in_addr ipv4_addr;
    978 
    979 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
    980 					    &ipv4_addr);
    981 			if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra,
    982 				    sizeof (rhost_addra)))
    983 				goto badconversion;
    984 		} else {
    985 			if (!inet_ntop(AF_INET6, &sin6->sin6_addr,
    986 				    rhost_addra, sizeof (rhost_addra)))
    987 				goto badconversion;
    988 		}
    989 	} else {
    990 		syslog(LOG_ERR, "unknown address family %d\n",
    991 		    fromp->ss_family);
    992 		fatal(f, "Permission denied");
    993 	}
    994 
    995 	/*
    996 	 * Allow connections only from the "ephemeral" reserved
    997 	 * ports(ports 512 - 1023) by checking the remote port
    998 	 * because other utilities(e.g. in.ftpd) can be used to
    999 	 * allow a unprivileged user to originate a connection
   1000 	 * from a privileged port and provide untrustworthy
   1001 	 * authentication.
   1002 	 */
   1003 	bad_port = (use_auth != KRB5_RECVAUTH_V5 &&
   1004 		    (port >= (in_port_t)IPPORT_RESERVED) ||
   1005 		    (port < (in_port_t)(IPPORT_RESERVED/2)));
   1006 	no_name = getnameinfo((const struct sockaddr *) fromp,
   1007 			    fromplen, hostname, sizeof (hostname),
   1008 			    NULL, 0, 0) != 0;
   1009 
   1010 	if (no_name || bad_port) {
   1011 		(void) strlcpy(abuf, rhost_addra, sizeof (abuf));
   1012 		/* If no host name, use IP address for name later on. */
   1013 		if (no_name)
   1014 			(void) strlcpy(hostname, abuf, sizeof (hostname));
   1015 	}
   1016 
   1017 	if (!no_name) {
   1018 		/*
   1019 		 * Even if getnameinfo() succeeded, we still have to check
   1020 		 * for spoofing.
   1021 		 */
   1022 		check_address("rlogind", fromp, sin, sin6, rhost_addra,
   1023 		    hostname, sizeof (hostname));
   1024 	}
   1025 
   1026 	if (bad_port) {
   1027 		if (no_name)
   1028 			syslog(LOG_NOTICE,
   1029 			    "connection from %s - bad port\n",
   1030 			    abuf);
   1031 		else
   1032 			syslog(LOG_NOTICE,
   1033 			    "connection from %s(%s) - bad port\n",
   1034 			    hostname, abuf);
   1035 		fatal(f, "Permission denied");
   1036 	}
   1037 
   1038 	if (use_auth == KRB5_RECVAUTH_V5) {
   1039 		do_krb_login(f, rhost_addra, hostname,
   1040 			    krb_context, encr_flag, keytab);
   1041 		if (krusername != NULL && strlen(krusername)) {
   1042 			/*
   1043 			 * Kerberos Authentication succeeded,
   1044 			 * so set the proper program name to use
   1045 			 * with pam (important during 'cleanup'
   1046 			 * routine later).
   1047 			 */
   1048 			pam_prog_name = KRB5_PROG_NAME;
   1049 		}
   1050 	}