xref: /titanic_41/usr/src/cmd/cmd-inet/usr.sbin/in.rlogind.c (revision c94146ce6d2a316eca6450a8cd77d8dee993f233)
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 	}
1051 
1052 	if (write(f, "", 1) != 1) {
1053 		syslog(LOG_NOTICE,
1054 		    "send of the zero byte(to %s) failed:"
1055 		    " cannot start data transfer mode\n",
1056 		    (no_name ? abuf : hostname));
1057 		exit(EXIT_FAILURE);
1058 	}
1059 	if ((p = open("/dev/ptmx", O_RDWR)) == -1)
1060 		fatalperror(f, "cannot open /dev/ptmx");
1061 	if (grantpt(p) == -1)
1062 		fatal(f, "could not grant slave pty");
1063 	if (unlockpt(p) == -1)
1064 		fatal(f, "could not unlock slave pty");
1065 	if ((line = ptsname(p)) == NULL)
1066 		fatal(f, "could not enable slave pty");
1067 	if ((t = open(line, O_RDWR)) == -1)
1068 		fatal(f, "could not open slave pty");
1069 	if (ioctl(t, I_PUSH, "ptem") == -1)
1070 		fatalperror(f, "ioctl I_PUSH ptem");
1071 	if (ioctl(t, I_PUSH, "ldterm") == -1)
1072 		fatalperror(f, "ioctl I_PUSH ldterm");
1073 	if (ioctl(t, I_PUSH, "ttcompat") == -1)
1074 		fatalperror(f, "ioctl I_PUSH ttcompat");
1075 	/*
1076 	 * POP the sockmod and push the rlmod module.
1077 	 *
1078 	 * Note that sockmod has to be removed since readstream assumes
1079 	 * a "raw" TPI endpoint(e.g. it uses getmsg).
1080 	 */
1081 	if (removemod(f, "sockmod") < 0)
1082 		fatalperror(f, "couldn't remove sockmod");
1083 
1084 	if (encr_flag) {
1085 		if (ioctl(f, I_PUSH, "cryptmod") < 0)
1086 		    fatalperror(f, "ioctl I_PUSH rlmod");
1087 
1088 	}
1089 
1090 	if (ioctl(f, I_PUSH, "rlmod") < 0)
1091 		fatalperror(f, "ioctl I_PUSH rlmod");
1092 
1093 	if (encr_flag) {
1094 		/*
1095 		 * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
1096 		 */
1097 		uchar_t passthru = 1;
1098 		struct strioctl rlmodctl;
1099 
1100 		rlmodctl.ic_cmd = CRYPTPASSTHRU;
1101 		rlmodctl.ic_timout = -1;
1102 		rlmodctl.ic_len = sizeof (uchar_t);
1103 		rlmodctl.ic_dp = (char *)&passthru;
1104 
1105 		if (ioctl(f, I_STR, &rlmodctl) < 0)
1106 			fatal(f, "ioctl CRYPTPASSTHRU failed\n");
1107 	}
1108 
1109 	/*
1110 	 * readstream will do a getmsg till it receives
1111 	 * M_PROTO type T_DATA_REQ from rloginmodopen()
1112 	 * indicating all data on the stream prior to pushing rlmod has
1113 	 * been drained at the stream head.
1114 	 */
1115 	if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0)
1116 		fatalperror(f, "readstream failed");
1117 	/*
1118 	 * Make sure the pty doesn't modify the strings passed
1119 	 * to login as part of the "rlogin protocol."  The login
1120 	 * program should set these flags to apropriate values
1121 	 * after it has read the strings.
1122 	 */
1123 	if (ioctl(t, TCGETS, &tp) == -1)
1124 		fatalperror(f, "ioctl TCGETS");
1125 	tp.c_lflag &= ~(ECHO|ICANON);
1126 	tp.c_oflag &= ~(XTABS|OCRNL);
1127 	tp.c_iflag &= ~(IGNPAR|ICRNL);
1128 	if (ioctl(t, TCSETS, &tp) == -1)
1129 		fatalperror(f, "ioctl TCSETS");
1130 
1131 	/*
1132 	 * System V ptys allow the TIOC{SG}WINSZ ioctl to be
1133 	 * issued on the master side of the pty.  Luckily, that's
1134 	 * the only tty ioctl we need to do do, so we can close the
1135 	 * slave side in the parent process after the fork.
1136 	 */
1137 	(void) ioctl(p, TIOCSWINSZ, &win);
1138 
1139 	pid = fork();
1140 	if (pid < 0)
1141 		fatalperror(f, "fork");
1142 	if (pid == 0) {
1143 		int tt;
1144 		struct utmpx ut;
1145 
1146 		/* System V login expects a utmp entry to already be there */
1147 		(void) memset(&ut, 0, sizeof (ut));
1148 		(void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user));
1149 		(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
1150 		ut.ut_pid = getpid();
1151 		ut.ut_id[0] = 'r';
1152 		ut.ut_id[1] = (char)SC_WILDC;
1153 		ut.ut_id[2] = (char)SC_WILDC;
1154 		ut.ut_id[3] = (char)SC_WILDC;
1155 		ut.ut_type = LOGIN_PROCESS;
1156 		ut.ut_exit.e_termination = 0;
1157 		ut.ut_exit.e_exit = 0;
1158 		(void) time(&ut.ut_tv.tv_sec);
1159 		if (makeutx(&ut) == NULL)
1160 			syslog(LOG_INFO, "in.rlogind:\tmakeutx failed");
1161 
1162 		/* controlling tty */
1163 		if (setsid() == -1)
1164 			fatalperror(f, "setsid");
1165 		if ((tt = open(line, O_RDWR)) == -1)
1166 			fatalperror(f, "could not re-open slave pty");
1167 
1168 		if (close(p) == -1)
1169 			fatalperror(f, "error closing pty master");
1170 		if (close(t) == -1)
1171 			fatalperror(f, "error closing pty slave"
1172 				    " opened before session established");
1173 		/*
1174 		 * If this fails we may or may not be able to output an
1175 		 * error message.
1176 		 */
1177 		if (close(f) == -1)
1178 			fatalperror(f, "error closing deamon stdout");
1179 		if (dup2(tt, STDIN_FILENO) == -1 ||
1180 		    dup2(tt, STDOUT_FILENO) == -1 ||
1181 		    dup2(tt, STDERR_FILENO) == -1)
1182 			exit(EXIT_FAILURE);	/* Disaster! No stderr! */
1183 
1184 		(void) close(tt);
1185 
1186 		if (use_auth == KRB5_RECVAUTH_V5 &&
1187 		    krusername != NULL && strlen(krusername)) {
1188 			(void) execl(LOGIN_PROGRAM, "login",
1189 				    "-d", line,
1190 				    "-r", hostname,
1191 				    "-u", krusername, /* KRB5 principal name */
1192 				    "-s", pam_prog_name,
1193 				    "-t", term,	/* Remote Terminal */
1194 				    "-U", rusername,	/* Remote User */
1195 				    "-R", KRB5_REPOSITORY_NAME,
1196 				    lusername,  /* local user */
1197 				    NULL);
1198 		} else {
1199 			(void) execl(LOGIN_PROGRAM, "login",
1200 				"-d", line,
1201 				"-r", hostname,
1202 				NULL);
1203 		}
1204 
1205 		fatalperror(STDERR_FILENO, "/bin/login");
1206 		/*NOTREACHED*/
1207 	}
1208 	(void) close(t);
1209 	(void) ioctl(f, FIONBIO, &on);
1210 	(void) ioctl(p, FIONBIO, &on);
1211 
1212 	/*
1213 	 * Must ignore SIGTTOU, otherwise we'll stop
1214 	 * when we try and set slave pty's window shape
1215 	 * (our controlling tty is the master pty).
1216 	 * Likewise, we don't want any of the tty-generated
1217 	 * signals from chars passing through.
1218 	 */
1219 	(void) sigset(SIGTSTP, SIG_IGN);
1220 	(void) sigset(SIGINT, SIG_IGN);
1221 	(void) sigset(SIGQUIT, SIG_IGN);
1222 	(void) sigset(SIGTTOU, SIG_IGN);
1223 	(void) sigset(SIGTTIN, SIG_IGN);
1224 	(void) sigset(SIGCHLD, cleanup);
1225 	(void) setpgrp();
1226 
1227 	if (encr_flag) {
1228 		krb5_data ivec, *ivptr;
1229 		uint_t ivec_usage;
1230 		stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT);
1231 
1232 		/*
1233 		 * Configure the STREAMS crypto module.  For now,
1234 		 * don't use any IV parameter.  KCMDV0.2 support
1235 		 * will require the use of Initialization Vectors
1236 		 * for both encrypt and decrypt modes.
1237 		 */
1238 		if (kcmd_protocol == KCMD_OLD_PROTOCOL) {
1239 			if (session_key->enctype == ENCTYPE_DES_CBC_CRC) {
1240 				/*
1241 				 * This is gross but necessary for MIT compat.
1242 				 */
1243 				ivec.length = session_key->length;
1244 				ivec.data = (char *)session_key->contents;
1245 				ivec_usage = IVEC_REUSE;
1246 				ivptr = &ivec;
1247 			} else {
1248 				ivptr = NULL; /* defaults to all 0's */
1249 				ivec_usage = IVEC_NEVER;
1250 			}
1251 			/*
1252 			 * configure both sides of stream together
1253 			 * since they share the same IV.
1254 			 * This is what makes the OLD KCMD protocol
1255 			 * less secure than the newer one - Bad ivecs.
1256 			 */
1257 			if (configure_stream(f, session_key,
1258 				CRYPT_ENCRYPT|CRYPT_DECRYPT,
1259 				ivptr, ivec_usage) != 0)
1260 				fatal(f, "Cannot initialize encryption -"
1261 					" exiting.\n");
1262 		} else {
1263 			size_t blocksize;
1264 			if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC ||
1265 			    session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
1266 				if (configure_stream(f, session_key,
1267 					CRYPT_ENCRYPT|CRYPT_DECRYPT,
1268 					NULL, IVEC_NEVER) != 0)
1269 					fatal(f,
1270 					"Cannot initialize encryption -"
1271 					" exiting.\n");
1272 				goto startcrypto;
1273 			}
1274 			if (krb5_c_block_size(krb_context,
1275 					    session_key->enctype,
1276 					    &blocksize)) {
1277 				syslog(LOG_ERR, "Cannot determine blocksize "
1278 				    "for encryption type %d",
1279 				    session_key->enctype);
1280 				fatal(f, "Cannot determine blocksize "
1281 				    "for encryption - exiting.\n");
1282 			}
1283 			ivec.data = (char *)malloc(blocksize);
1284 			ivec.length = blocksize;
1285 			if (ivec.data == NULL)
1286 				fatal(f, "memory error - exiting\n");
1287 			/*
1288 			 * Following MIT convention -
1289 			 *   encrypt IV = 0x01 x blocksize
1290 			 *   decrypt IV = 0x00 x blocksize
1291 			 *   ivec_usage = IVEC_ONETIME
1292 			 *
1293 			 * configure_stream separately for encrypt and
1294 			 * decrypt because there are 2 different IVs.
1295 			 *
1296 			 * AES uses 0's for IV.
1297 			 */
1298 			if (session_key->enctype ==
1299 				ENCTYPE_AES128_CTS_HMAC_SHA1_96 ||
1300 			    session_key->enctype ==
1301 				ENCTYPE_AES256_CTS_HMAC_SHA1_96)
1302 				(void) memset(ivec.data, 0x00, blocksize);
1303 			else
1304 				(void) memset(ivec.data, 0x01, blocksize);
1305 			if (configure_stream(f, session_key, CRYPT_ENCRYPT,
1306 				&ivec, IVEC_ONETIME) != 0)
1307 				fatal(f, "Cannot initialize encryption -"
1308 					" exiting.\n");
1309 			(void) memset(ivec.data, 0x00, blocksize);
1310 			if (configure_stream(f, session_key, CRYPT_DECRYPT,
1311 				&ivec, IVEC_ONETIME) != 0)
1312 				fatal(f, "Cannot initialize encryption -"
1313 					" exiting.\n");
1314 
1315 			(void) free(ivec.data);
1316 		}
1317 startcrypto:
1318 		start_stream(f, CRYPT_ENCRYPT);
1319 		start_stream(f, CRYPT_DECRYPT);
1320 	}
1321 	protocol(f, p, encr_flag);
1322 	cleanup(0);
1323 	/*NOTREACHED*/
1324 
1325 badconversion:
1326 	fatalperror(f, "address conversion");
1327 	/*NOTREACHED*/
1328 }
1329 
1330 /*
1331  * rlogin "protocol" machine.
1332  */
1333 static void
1334 protocol(int f, int p, int encr_flag)
1335 {
1336 	struct	stat	buf;
1337 	struct 	protocol_arg	rloginp;
1338 	struct	strioctl	rloginmod;
1339 	int	ptmfd;	/* fd of logindmux coneected to ptmx */
1340 	int	netfd;	/* fd of logindmux connected to netf */
1341 	static uchar_t	oobdata[] = {TIOCPKT_WINDOW};
1342 
1343 	/* indicate new rlogin */
1344 	if (send_oob(f, oobdata, 1) < 0)
1345 		fatalperror(f, "send_oob");
1346 	/*
1347 	 * We cannot send the SECURE_MSG until after the
1348 	 * client has been signaled with the oobdata (above).
1349 	 */
1350 	if (encr_flag) {
1351 		if (write(f, SECURE_MSG, strlen(SECURE_MSG)) < 0)
1352 			fatalperror(f, "Error writing SECURE message");
1353 	}
1354 
1355 	/*
1356 	 * Open logindmux driver and link netf and ptmx
1357 	 * underneath logindmux.
1358 	 */
1359 	if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1)
1360 		fatalperror(f, "open /dev/logindmux");
1361 
1362 	if ((netfd = open("/dev/logindmux", O_RDWR)) == -1)
1363 		fatalperror(f, "open /dev/logindmux");
1364 
1365 	if (ioctl(ptmfd, I_LINK, p) < 0)
1366 		fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
1367 
1368 	if (ioctl(netfd, I_LINK, f) < 0)
1369 		fatal(f, "ioctl I_LINK of tcp connection failed\n");
1370 
1371 	/*
1372 	 * Figure out the device number of the ptm's mux fd, and pass that
1373 	 * to the net's mux.
1374 	 */
1375 	if (fstat(ptmfd, &buf) < 0)
1376 		fatalperror(f, "cannot determine device number"
1377 		    " of pty side of /dev/logindmux");
1378 	rloginp.dev = buf.st_rdev;
1379 	rloginp.flag = 0;
1380 
1381 	rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
1382 	rloginmod.ic_timout = -1;
1383 	rloginmod.ic_len = sizeof (struct protocol_arg);
1384 	rloginmod.ic_dp = (char *)&rloginp;
1385 
1386 	if (ioctl(netfd, I_STR, &rloginmod) < 0)
1387 		fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
1388 
1389 	/*
1390 	 * Figure out the device number of the net's mux fd, and pass that
1391 	 * to the ptm's mux.
1392 	 */
1393 	if (fstat(netfd, &buf))
1394 		fatalperror(f, "cannot determine device number"
1395 		    " of network side of /dev/logindmux");
1396 	rloginp.dev = buf.st_rdev;
1397 	rloginp.flag = 1;
1398 
1399 	rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
1400 	rloginmod.ic_timout = -1;
1401 	rloginmod.ic_len = sizeof (struct protocol_arg);
1402 	rloginmod.ic_dp = (char *)&rloginp;
1403 
1404 	if (ioctl(ptmfd, I_STR, &rloginmod) < 0)
1405 		fatal(netfd, "ioctl LOGDMXZ_IOC_QEXCHANGE of ptmfd failed\n");
1406 	/*
1407 	 * Send an ioctl type RL_IOC_ENABLE to reenable the
1408 	 * message queue and reinsert the data read from streamhead
1409 	 * at the time of pushing rloginmod module.
1410 	 * We need to send this ioctl even if no data was read earlier
1411 	 * since we need to reenable the message queue of rloginmod module.
1412 	 */
1413 	rloginmod.ic_cmd = RL_IOC_ENABLE;
1414 	rloginmod.ic_timout = -1;
1415 	if (nsize) {
1416 		rloginmod.ic_len = nsize;
1417 		rloginmod.ic_dp = rlbuf;
1418 	} else {
1419 		rloginmod.ic_len = 0;
1420 		rloginmod.ic_dp = NULL;
1421 	}
1422 
1423 	if (ioctl(netfd, I_STR, &rloginmod) < 0)
1424 		fatal(netfd, "ioctl RL_IOC_ENABLE of netfd failed\n");
1425 
1426 	/*
1427 	 * User level daemon now pauses till the shell exits.
1428 	 */
1429 	(void) pause();
1430 }
1431 
1432 /* This is a signal handler, hence the dummy argument */
1433 /*ARGSUSED*/
1434 static void
1435 cleanup(int dummy)
1436 {
1437 	rmut();
1438 	exit(EXIT_FAILURE);
1439 	/*NOTREACHED*/
1440 }
1441 
1442 /*
1443  * TPI style replacement for socket send() primitive, so we don't require
1444  * sockmod to be on the stream.
1445  */
1446 static int
1447 send_oob(int fd, void *ptr, size_t count)
1448 {
1449 	struct T_exdata_req exd_req;
1450 	struct strbuf hdr, dat;
1451 	int ret;
1452 
1453 	exd_req.PRIM_type = T_EXDATA_REQ;
1454 	exd_req.MORE_flag = 0;
1455 
1456 	hdr.buf = (char *)&exd_req;
1457 	hdr.len = sizeof (exd_req);
1458 
1459 	dat.buf = ptr;
1460 	dat.len = count;
1461 
1462 	ret = putmsg(fd, &hdr, &dat, 0);
1463 	if (ret == 0)
1464 		ret = count;
1465 	return (ret);
1466 }
1467 
1468 static void
1469 fatal(int fd, const char *msg)
1470 {
1471 	char *bufp;
1472 	size_t len = strlen(msg) + 16;		/* enough for our wrapper */
1473 
1474 	bufp = alloca(len);
1475 	/* ASCII 001 is the error indicator */
1476 	len = snprintf(bufp, len, "\01rlogind: %s.\r\n", msg);
1477 	(void) write(fd, bufp, len);
1478 	exit(EXIT_FAILURE);
1479 	/*NOTREACHED*/
1480 }
1481 
1482 /*PRINTFLIKE2*/
1483 static void
1484 fatalperror(int fd, const char *msg)
1485 {
1486 	char *bufp;
1487 	const char *errstr;
1488 	int save_errno = errno;
1489 	size_t len = strlen(msg);
1490 
1491 	if ((errstr = strerror(save_errno))) {
1492 		len += strlen(errstr) + 3;	/* 3 for ": " and \0 below */
1493 		bufp = alloca(len);
1494 		(void) snprintf(bufp, len, "%s: %s", msg, errstr);
1495 	} else {
1496 		const char fmt[] = "%s: Error %d";
1497 
1498 		/* -4 for %s & %d. "*8/3" is bytes->decimal, pessimistically */
1499 		len += sizeof (fmt) -4 + (sizeof (save_errno) *8 /3);
1500 		bufp = alloca(len);
1501 		(void) snprintf(bufp, len, fmt, msg, save_errno);
1502 	}
1503 	fatal(fd, bufp);
1504 	/*NOTREACHED*/
1505 }
1506 
1507 static void
1508 rmut(void)
1509 {
1510 	pam_handle_t *pamh;
1511 	struct utmpx *up;
1512 	char user[sizeof (up->ut_user) + 1];
1513 	char ttyn[sizeof (up->ut_line) + 1];
1514 	char rhost[sizeof (up->ut_host) + 1];
1515 
1516 	/* while cleaning up dont allow disruption */
1517 	(void) sigset(SIGCHLD, SIG_IGN);
1518 
1519 	setutxent();
1520 	while (up = getutxent()) {
1521 		if (up->ut_pid == pid) {
1522 			if (up->ut_type == DEAD_PROCESS)
1523 				break;		/* Cleaned up elsewhere. */
1524 
1525 			/*
1526 			 * call pam_close_session if login changed
1527 			 * the utmpx user entry from type LOGIN_PROCESS
1528 			 * to type USER_PROCESS, which happens
1529 			 * after pam_open_session is called.
1530 			 */
1531 			if (up->ut_type == USER_PROCESS) {
1532 				(void) strlcpy(user, up->ut_user,
1533 					    sizeof (user));
1534 				(void) strlcpy(ttyn, up->ut_line,
1535 					    sizeof (ttyn));
1536 				(void) strlcpy(rhost, up->ut_host,
1537 					    sizeof (rhost));
1538 
1539 				/*
1540 				 * Use the same pam_prog_name that
1541 				 * 'login' used.
1542 				 */
1543 				if ((pam_start(pam_prog_name, user,  NULL,
1544 					    &pamh))
1545 				    == PAM_SUCCESS) {
1546 					(void) pam_set_item(pamh, PAM_TTY,
1547 							    ttyn);
1548 					(void) pam_set_item(pamh, PAM_RHOST,
1549 							    rhost);
1550 					(void) pam_close_session(pamh, 0);
1551 					(void) pam_end(pamh, PAM_SUCCESS);
1552 				}
1553 			}
1554 
1555 			up->ut_type = DEAD_PROCESS;
1556 			up->ut_exit.e_termination = WTERMSIG(0);
1557 			up->ut_exit.e_exit = WEXITSTATUS(0);
1558 			(void) time(&up->ut_tv.tv_sec);
1559 
1560 			if (modutx(up) == NULL) {
1561 				/*
1562 				 * Since modutx failed we'll
1563 				 * write out the new entry
1564 				 * ourselves.
1565 				 */
1566 				(void) pututxline(up);
1567 				updwtmpx("wtmpx", up);
1568 			}
1569 			break;
1570 		}
1571 	}
1572 
1573 	endutxent();
1574 
1575 	(void) sigset(SIGCHLD, cleanup);
1576 }
1577 
1578 static int
1579 readstream(int fd, char *buf, int size)
1580 {
1581 	struct strbuf ctlbuf, datbuf;
1582 	union T_primitives tpi;
1583 	int	nbytes = 0;
1584 	int	ret = 0;
1585 	int	flags = 0;
1586 	int	bufsize = size;
1587 	int	nread;
1588 
1589 	(void) memset(&ctlbuf, 0, sizeof (ctlbuf));
1590 	(void) memset(&datbuf, 0, sizeof (datbuf));
1591 
1592 	ctlbuf.buf = (char *)&tpi;
1593 	ctlbuf.maxlen = sizeof (tpi);
1594 	datbuf.buf = buf;
1595 	datbuf.maxlen = size;
1596 
1597 	for (;;) {
1598 		if (ioctl(fd, I_NREAD, &nread) < 0) {
1599 			syslog(LOG_ERR, "I_NREAD returned error %m");
1600 			return (-1);
1601 		}
1602 		if (nread + nbytes > bufsize) {
1603 			buf = (char *)realloc(buf, (unsigned)(bufsize + nread));
1604 			if (buf == NULL) {
1605 				syslog(LOG_WARNING,
1606 				    "buffer allocation failed\n");
1607 				return (-1);
1608 			}
1609 			bufsize += nread;
1610 			rlbuf = buf;
1611 			datbuf.buf = buf + nbytes;
1612 		}
1613 		datbuf.maxlen = bufsize - nbytes;
1614 		ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
1615 		if (ret < 0) {
1616 			syslog(LOG_ERR, "getmsg failed error %m");
1617 			return (-1);
1618 		}
1619 		if ((ctlbuf.len == 0) && (datbuf.len == 0)) {
1620 			/*
1621 			 * getmsg() returned no data - this indicates
1622 			 * that the connection is closing down.
1623 			 */
1624 			cleanup(0);
1625 		}
1626 		if (ctlbuf.len <= 0) {
1627 			nbytes += datbuf.len;
1628 			datbuf.buf += datbuf.len;
1629 			continue;
1630 		}
1631 		if (tpi.type == T_DATA_REQ) {
1632 			return (nbytes);
1633 		}
1634 		if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
1635 			cleanup(0);
1636 	}
1637 }
1638 
1639 /*
1640  * Verify that the named module is at the top of the stream
1641  * and then pop it off.
1642  */
1643 static int
1644 removemod(int f, char *modname)
1645 {
1646 	char topmodname[BUFSIZ];
1647 
1648 	if (ioctl(f, I_LOOK, topmodname) < 0)
1649 		return (-1);
1650 	if (strcmp(modname, topmodname) != 0) {
1651 		errno = ENXIO;
1652 		return (-1);
1653 	}
1654 	if (ioctl(f, I_POP, 0) < 0)
1655 		return (-1);
1656 	return (0);
1657 }
1658