1 /*
2  * Copyright 2006 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 
73 #define	KRB5_RECVAUTH_V5 5
74 #define	UT_NAMESIZE	sizeof (((struct utmpx *)0)->ut_name)
75 
76 static char lusername[UT_NAMESIZE+1];
77 static char rusername[UT_NAMESIZE+1];
78 static char *krusername = NULL;
79 static char term[64];
80 
81 static krb5_ccache ccache = NULL;
82 static krb5_keyblock *session_key = NULL;
83 static int chksum_flag = 0;
84 static int use_auth = 0;
85 static enum kcmd_proto kcmd_protocol;
86 #ifdef ALLOW_KCMD_V2
87 static krb5_data encr_iv = { NULL, 0 };
88 static krb5_data decr_iv = { NULL, 0 };
89 #endif /* ALLOW_KCMD_V2 */
90 
91 #define	CHKSUM_REQUIRED 0x01
92 #define	CHKSUM_IGNORED  0x02
93 #define	VALID_CHKSUM(x) ((x) == 0 || (x) == CHKSUM_REQUIRED ||\
94 			(x) == CHKSUM_IGNORED)
95 
96 #define	PWD_IF_FAIL  0x01
97 #define	PWD_REQUIRED 0x02
98 
99 #define	AUTH_NONE 0x00
100 
101 #define	ARGSTR "k5exEXciM:s:S:D:"
102 #define	DEFAULT_TOS 16
103 
104 #define	KRB5_PROG_NAME "krlogin"
105 
106 #define	SECURE_MSG "This rlogin session is using encryption " \
107 	"for all data transmissions.\r\n"
108 
109 #define	KRB_V5_SENDAUTH_VERS	"KRB5_SENDAUTH_V1.0"
110 #define	KRB5_RECVAUTH_V5	5
111 
112 static krb5_error_code krb5_compat_recvauth(krb5_context context,
113 					    krb5_auth_context *auth_context,
114 					    krb5_pointer fdp,
115 					    krb5_principal server,
116 					    krb5_int32 flags,
117 					    krb5_keytab keytab,
118 					    krb5_ticket **ticket,
119 					    krb5_int32 *auth_sys,
120 					    krb5_data *version);
121 
122 static void do_krb_login(int, char *, char *, krb5_context, int, krb5_keytab);
123 static int configure_stream(int, krb5_keyblock *, int, krb5_data *, uint_t);
124 
125 extern krb5_error_code krb5_read_message(krb5_context, krb5_pointer,
126 					krb5_data *);
127 extern krb5_error_code krb5_net_read(krb5_context, int, char *, int);
128 
129 #define	LOGIN_PROGRAM "/bin/login"
130 
131 #define	DEFAULT_PROG_NAME	"rlogin"
132 
133 static const char *pam_prog_name = DEFAULT_PROG_NAME;
134 static void	rmut(void);
135 static void	doit(int,  struct sockaddr_storage *, krb5_context, int,
136 		    krb5_keytab);
137 static void	protocol(int, int, int);
138 
139 static int	readstream(int, char *, int);
140 static void	fatal(int, const char *);
141 static void	fatalperror(int, const char *);
142 static int	send_oob(int fd, void *ptr, size_t count);
143 static int	removemod(int f, char *modname);
144 
145 static int
146 issock(int fd)
147 {
148 	struct stat stats;
149 
150 	if (fstat(fd, &stats) == -1)
151 		return (0);
152 	return (S_ISSOCK(stats.st_mode));
153 }
154 
155 /*
156  * audit_rlogin_settid stores the terminal id while it is still
157  * available.  Subsequent calls to adt_load_hostname() return
158  * the id which is stored here.
159  */
160 static int
161 audit_rlogin_settid(int fd) {
162 	adt_session_data_t	*ah;
163 	adt_termid_t		*termid;
164 	int			rc;
165 
166 	if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
167 		if ((rc = adt_load_termid(fd, &termid)) == 0) {
168 			if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
169 			    ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
170 			    termid, ADT_SETTID)) == 0)
171 				(void) adt_set_proc(ah);
172 			free(termid);
173 		}
174 		(void) adt_end_session(ah);
175 	}
176 	return (rc);
177 }
178 
179 
180 /* ARGSUSED */
181 int
182 main(int argc, char *argv[])
183 {
184 	int on = 1;
185 	socklen_t fromlen;
186 	struct sockaddr_storage from;
187 	int fd = -1;
188 
189 	extern char *optarg;
190 	char c;
191 	int tos = -1;
192 	krb5_context krb_context;
193 	krb5_keytab keytab = NULL;
194 	krb5_error_code status;
195 	char *realm = NULL;
196 	char *keytab_file = NULL;
197 	int encr_flag = 0;
198 	struct sockaddr_storage ouraddr;
199 	socklen_t ourlen;
200 #ifdef DEBUG
201 	int debug_port = 0;
202 #endif /* DEBUG */
203 	openlog("rlogind", LOG_PID | LOG_ODELAY, LOG_DAEMON);
204 
205 	while ((c = getopt(argc, argv, ARGSTR)) != -1) {
206 		switch (c) {
207 		case 'k':
208 		case '5':
209 			use_auth = KRB5_RECVAUTH_V5;
210 			break;
211 		case 'e':
212 		case 'E':
213 		case 'x':
214 		case 'X':
215 			encr_flag = 1;
216 			break;
217 		case 'M':
218 			realm = (char *)strdup(optarg);
219 			break;
220 		case 'S':
221 			keytab_file = (char *)strdup(optarg);
222 			break;
223 		case 'c':
224 			chksum_flag |= CHKSUM_REQUIRED;
225 			break;
226 		case 'i':
227 			chksum_flag |= CHKSUM_IGNORED;
228 			break;
229 		case 's':
230 			if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
231 			    tos > 255) {
232 				syslog(LOG_ERR, "%s: illegal tos value: "
233 				    "%s\n", argv[0], optarg);
234 			} else {
235 				if (tos < 0)
236 					tos = DEFAULT_TOS;
237 			}
238 			break;
239 #ifdef DEBUG
240 		case 'D':
241 			debug_port = atoi(optarg);
242 			break;
243 #endif /* DEBUG */
244 		default:
245 			syslog(LOG_ERR, "Unrecognized command line option "
246 			    "(%s), exiting", argv[optind]);
247 			exit(EXIT_FAILURE);
248 		}
249 	}
250 	if (use_auth == KRB5_RECVAUTH_V5) {
251 		status = krb5_init_context(&krb_context);
252 		if (status) {
253 			syslog(LOG_ERR, "Error initializing krb5: %s",
254 			    error_message(status));
255 			exit(EXIT_FAILURE);
256 		}
257 		if (realm != NULL)
258 			krb5_set_default_realm(krb_context, realm);
259 		if (keytab_file != NULL) {
260 			if ((status = krb5_kt_resolve(krb_context,
261 						    keytab_file,
262 						    &keytab))) {
263 				com_err(argv[0],
264 					status,
265 					"while resolving srvtab file %s",
266 					keytab_file);
267 				exit(EXIT_FAILURE);
268 			}
269 		}
270 	}
271 
272 #ifdef DEBUG
273 	if (debug_port) {
274 		int s;
275 		struct sockaddr_in sin;
276 
277 		if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
278 			fatalperror(STDERR_FILENO, "Error in socket");
279 		}
280 
281 		(void) memset((char *)&sin, 0, sizeof (sin));
282 		sin.sin_family = AF_INET;
283 		sin.sin_port = htons(debug_port);
284 		sin.sin_addr.s_addr = INADDR_ANY;
285 
286 		(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
287 					(char *)&on, sizeof (on));
288 
289 		if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
290 			fatalperror(STDERR_FILENO, "bind error");
291 		}
292 
293 		if ((listen(s, 5)) < 0) {
294 			fatalperror(STDERR_FILENO, "listen error");
295 		}
296 
297 		fromlen = sizeof (from);
298 		if ((fd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) {
299 			fatalperror(STDERR_FILENO, "accept error");
300 		}
301 
302 		(void) close(s);
303 	} else
304 #endif /* DEBUG */
305 	{
306 		if (!issock(STDIN_FILENO))
307 			fatal(STDIN_FILENO,
308 				"stdin is not a socket file descriptor");
309 		fd = STDIN_FILENO;
310 	}
311 
312 	fromlen = sizeof (from);
313 	if (getpeername(fd, (struct sockaddr *)&from, &fromlen) < 0)
314 		fatalperror(STDERR_FILENO, "getpeername");
315 
316 	if (audit_rlogin_settid(fd))	/* set terminal ID */
317 		fatalperror(STDERR_FILENO, "audit");
318 
319 	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
320 	    sizeof (on)) < 0)
321 		syslog(LOG_WARNING, "setsockopt(SO_KEEPALIVE): %m");
322 
323 	if (!VALID_CHKSUM(chksum_flag)) {
324 		syslog(LOG_ERR, "Configuration error: mutually exclusive "
325 		    "options specified (-c and -i)");
326 		fatal(fd, "Checksums are required and ignored (-c and -i);"
327 		    "these options are mutually exclusive - check "
328 		    "the documentation.");
329 	}
330 	ourlen = sizeof (ouraddr);
331 	if (getsockname(fd, (struct sockaddr *)&ouraddr, &ourlen) == -1) {
332 		syslog(LOG_ERR, "getsockname error: %m");
333 		exit(EXIT_FAILURE);
334 	}
335 
336 	if (tos != -1 &&
337 	    ouraddr.ss_family != AF_INET6 &&
338 	    setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
339 					sizeof (tos)) < 0 &&
340 					errno != ENOPROTOOPT) {
341 		syslog(LOG_ERR, "setsockopt(IP_TOS %d): %m", tos);
342 	}
343 	doit(fd, &from, krb_context, encr_flag, keytab);
344 	return (0);
345 }
346 
347 static void	cleanup(int);
348 static int	nsize = 0;	/* bytes read prior to pushing rlmod */
349 static char	*rlbuf;		/* buffer where nbytes are read to */
350 static char	*line;
351 
352 static struct winsize win = { 0, 0, 0, 0 };
353 static pid_t pid;
354 static char hostname[MAXHOSTNAMELEN + 1];
355 
356 static void
357 getstr(int f, char *buf, int cnt, char *err)
358 {
359 	char c;
360 	do {
361 		if (read(f, &c, 1) != 1 || (--cnt < 0)) {
362 			syslog(LOG_ERR, "Error reading \'%s\' field", err);
363 			exit(EXIT_FAILURE);
364 		}
365 		*buf++ = c;
366 	} while (c != '\0');
367 }
368 
369 static krb5_error_code
370 recvauth(int f,
371 	krb5_context krb_context,
372 	unsigned int *valid_checksum,
373 	krb5_ticket **ticket,
374 	int *auth_type,
375 	krb5_principal *client,
376 	int encr_flag,
377 	krb5_keytab keytab)
378 {
379 	krb5_error_code status = 0;
380 	krb5_auth_context auth_context = NULL;
381 	krb5_rcache rcache;
382 	krb5_authenticator *authenticator;
383 	krb5_data inbuf;
384 	krb5_data auth_version;
385 
386 	*valid_checksum = 0;
387 
388 	if ((status = krb5_auth_con_init(krb_context, &auth_context)))
389 		return (status);
390 
391 	/* Only need remote address for rd_cred() to verify client */
392 	if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f,
393 			KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
394 		return (status);
395 
396 	status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache);
397 	if (status)
398 		return (status);
399 
400 	if (!rcache) {
401 		krb5_principal server;
402 
403 		status = krb5_sname_to_principal(krb_context, 0, 0,
404 						KRB5_NT_SRV_HST, &server);
405 		if (status)
406 			return (status);
407 
408 		status = krb5_get_server_rcache(krb_context,
409 				krb5_princ_component(krb_context, server, 0),
410 				&rcache);
411 		krb5_free_principal(krb_context, server);
412 		if (status)
413 			return (status);
414 
415 		status = krb5_auth_con_setrcache(krb_context, auth_context,
416 						rcache);
417 		if (status)
418 			return (status);
419 	}
420 	if ((status = krb5_compat_recvauth(krb_context,
421 					&auth_context,
422 					&f,
423 					NULL,	/* Specify daemon principal */
424 					0,	/* no flags */
425 					keytab,	/* NULL to use v5srvtab */
426 					ticket,	/* return ticket */
427 					auth_type, /* authentication system */
428 					&auth_version))) {
429 		if (*auth_type == KRB5_RECVAUTH_V5) {
430 			/*
431 			 * clean up before exiting
432 			 */
433 			getstr(f, rusername, sizeof (rusername), "remuser");
434 			getstr(f, lusername, sizeof (lusername), "locuser");
435 			getstr(f, term, sizeof (term), "Terminal type");
436 		}
437 		return (status);
438 	}
439 
440 	getstr(f, lusername, sizeof (lusername), "locuser");
441 	getstr(f, term, sizeof (term), "Terminal type");
442 
443 	kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
444 	if (auth_version.length != 9 || auth_version.data == NULL) {
445 		syslog(LOG_ERR, "Bad application protocol version length in "
446 		    "KRB5 exchange, exiting");
447 		fatal(f, "Bad application version length, exiting.");
448 	}
449 	/*
450 	 * Determine which Kerberos CMD protocol was used.
451 	 */
452 	if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) {
453 		kcmd_protocol = KCMD_OLD_PROTOCOL;
454 	} else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) {
455 		kcmd_protocol = KCMD_NEW_PROTOCOL;
456 	} else {
457 		syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting",
458 			(char *)auth_version.data);
459 		fatal(f, "Unrecognized KCMD protocol, exiting");
460 	}
461 
462 	if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag &&
463 		kcmd_protocol == KCMD_OLD_PROTOCOL) {
464 		if ((status = krb5_auth_con_getauthenticator(krb_context,
465 							    auth_context,
466 							    &authenticator)))
467 			return (status);
468 		if (authenticator->checksum) {
469 			struct sockaddr_storage adr;
470 			int adr_length = sizeof (adr);
471 			int buflen;
472 			krb5_data input;
473 			krb5_keyblock key;
474 			char *chksumbuf;
475 
476 			/*
477 			 * Define the lenght of the chksum buffer.
478 			 * chksum string = "[portnum]:termstr:username"
479 			 * The extra 32 is to hold a integer string for
480 			 * the portnumber.
481 			 */
482 			buflen = strlen(term) + strlen(lusername) + 32;
483 			chksumbuf = (char *)malloc(buflen);
484 			if (chksumbuf == 0) {
485 				krb5_free_authenticator(krb_context,
486 							authenticator);
487 				fatal(f, "Out of memory error");
488 			}
489 
490 			if (getsockname(f, (struct sockaddr *)&adr,
491 							&adr_length) != 0) {
492 				krb5_free_authenticator(krb_context,
493 							authenticator);
494 				fatal(f, "getsockname error");
495 			}
496 
497 			(void) snprintf(chksumbuf, buflen,
498 					"%u:%s%s",
499 					ntohs(SOCK_PORT(adr)),
500 					term, lusername);
501 
502 			input.data = chksumbuf;
503 			input.length = strlen(chksumbuf);
504 			key.contents = (*ticket)->enc_part2->session->contents;
505 			key.length = (*ticket)->enc_part2->session->length;
506 			status = krb5_c_verify_checksum(krb_context,
507 						&key, 0,
508 						&input,
509 						authenticator->checksum,
510 						valid_checksum);
511 
512 			if (status == 0 && *valid_checksum == 0)
513 				status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
514 
515 			if (chksumbuf)
516 				krb5_xfree(chksumbuf);
517 			if (status) {
518 				krb5_free_authenticator(krb_context,
519 							authenticator);
520 				return (status);
521 			}
522 		}
523 		krb5_free_authenticator(krb_context, authenticator);
524 	}
525 
526 	if ((status = krb5_copy_principal(krb_context,
527 					(*ticket)->enc_part2->client,
528 					client)))
529 		return (status);
530 
531 	/* Get the Unix username of the remote user */
532 	getstr(f, rusername, sizeof (rusername), "remuser");
533 
534 	/* Get the Kerberos principal name string of the remote user */
535 	if ((status = krb5_unparse_name(krb_context, *client, &krusername)))
536 		return (status);
537 
538 #ifdef DEBUG
539 	syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s",
540 	    (krusername != NULL ? krusername : "<unknown>"));
541 #endif
542 
543 	if (encr_flag) {
544 		status = krb5_auth_con_getremotesubkey(krb_context,
545 						    auth_context,
546 						    &session_key);
547 		if (status) {
548 			syslog(LOG_ERR, "Error getting KRB5 session "
549 			    "subkey, exiting");
550 			fatal(f, "Error getting KRB5 session subkey, exiting");
551 		}
552 		/*
553 		 * The "new" protocol requires that a subkey be sent.
554 		 */
555 		if (session_key == NULL &&
556 		    kcmd_protocol == KCMD_NEW_PROTOCOL) {
557 			syslog(LOG_ERR, "No KRB5 session subkey sent, exiting");
558 			fatal(f, "No KRB5 session subkey sent, exiting");
559 		}
560 		/*
561 		 * The "old" protocol does not permit an authenticator subkey.
562 		 * The key is taken from the ticket instead (see below).
563 		 */
564 		if (session_key != NULL &&
565 		    kcmd_protocol == KCMD_OLD_PROTOCOL) {
566 			syslog(LOG_ERR, "KRB5 session subkey not permitted "
567 			    "with old KCMD protocol, exiting");
568 
569 			fatal(f, "KRB5 session subkey not permitted "
570 			    "with old KCMD protocol, exiting");
571 		}
572 		/*
573 		 * If no key at this point, use the session key from
574 		 * the ticket.
575 		 */
576 		if (session_key == NULL) {
577 			/*
578 			 * Save the session key so we can configure the crypto
579 			 * module later.
580 			 */
581 			status = krb5_copy_keyblock(krb_context,
582 					    (*ticket)->enc_part2->session,
583 					    &session_key);
584 			if (status) {
585 				syslog(LOG_ERR, "krb5_copy_keyblock failed");
586 				fatal(f, "krb5_copy_keyblock failed");
587 			}
588 		}
589 		/*
590 		 * If session key still cannot be found, we must
591 		 * exit because encryption is required here
592 		 * when encr_flag (-x) is set.
593 		 */
594 		if (session_key == NULL) {
595 			syslog(LOG_ERR, "Could not find an encryption key,"
596 				    "exiting");
597 			fatal(f, "Encryption required but key not found, "
598 			    "exiting");
599 		}
600 	}
601 	/*
602 	 * Use krb5_read_message to read the principal stuff.
603 	 */
604 	if ((status = krb5_read_message(krb_context, (krb5_pointer)&f,
605 					&inbuf)))
606 		fatal(f, "Error reading krb5 message");
607 
608 	if ((inbuf.length) && /* Forwarding being done, read creds */
609 	    (status = rd_and_store_for_creds(krb_context, auth_context,
610 					    &inbuf, *ticket, lusername,
611 					    &ccache))) {
612 		if (rcache)
613 		    (void) krb5_rc_close(krb_context, rcache);
614 		fatal(f, "Can't get forwarded credentials");
615 	}
616 	if (rcache)
617 		(void) krb5_rc_close(krb_context, rcache);
618 
619 	return (status);
620 }
621 
622 static void
623 do_krb_login(int f, char *host_addr, char *hostname,
624 	    krb5_context krb_context, int encr_flag,
625 	    krb5_keytab keytab)
626 {
627 	krb5_error_code status;
628 	uint_t valid_checksum;
629 	krb5_ticket	*ticket = NULL;
630 	int auth_sys = 0;
631 	int auth_sent = 0;
632 	krb5_principal client = NULL;
633 
634 	if (getuid())
635 		fatal(f, "Error authorizing KRB5 connection, "
636 			"server lacks privilege");
637 
638 	status = recvauth(f, krb_context, &valid_checksum, &ticket,
639 			&auth_sys, &client, encr_flag, keytab);
640 	if (status) {
641 		if (ticket)
642 			krb5_free_ticket(krb_context, ticket);
643 		if (status != 255)
644 			syslog(LOG_ERR,
645 			    "Authentication failed from %s(%s): %s\n",
646 			    host_addr, hostname, error_message(status));
647 		fatal(f, "Kerberos authentication failed, exiting");
648 	}
649 
650 	if (auth_sys != KRB5_RECVAUTH_V5) {
651 		fatal(f, "This server only supports Kerberos V5");
652 	} else {
653 		/*
654 		 * Authenticated OK, now check authorization.
655 		 */
656 		if (client && krb5_kuserok(krb_context, client, lusername))
657 		    auth_sent = KRB5_RECVAUTH_V5;
658 	}
659 
660 	if (auth_sent == KRB5_RECVAUTH_V5 &&
661 	    kcmd_protocol == KCMD_OLD_PROTOCOL &&
662 	    chksum_flag == CHKSUM_REQUIRED && !valid_checksum) {
663 		syslog(LOG_ERR, "Client did not supply required checksum, "
664 		    "connection rejected.");
665 		fatal(f, "Client did not supply required checksum, "
666 		    "connection rejected.");
667 	}
668 
669 	if (auth_sys != auth_sent) {
670 		char *msg_fail = NULL;
671 		int msgsize = 0;
672 
673 		if (ticket)
674 			krb5_free_ticket(krb_context, ticket);
675 
676 		if (krusername != NULL) {
677 			/*
678 			 * msgsize must be enough to hold
679 			 * krusername, lusername and a brief
680 			 * message describing the failure.
681 			 */
682 			msgsize = strlen(krusername) +
683 				strlen(lusername) + 80;
684 			msg_fail = (char *)malloc(msgsize);
685 		}
686 		if (msg_fail == NULL) {
687 			syslog(LOG_ERR, "User is not authorized to login to "
688 			    "specified account");
689 
690 			fatal(f, "User is not authorized to login to "
691 			    "specified account");
692 		}
693 		if (auth_sent != 0)
694 			(void) snprintf(msg_fail, msgsize,
695 					"Access denied because of improper "
696 					"KRB5 credentials");
697 		else
698 			(void) snprintf(msg_fail, msgsize,
699 					"User %s is not authorized to login "
700 					"to account %s",
701 					krusername, lusername);
702 		syslog(LOG_ERR, "%s", msg_fail);
703 		fatal(f, msg_fail);
704 	}
705 }
706 
707 /*
708  * stop_stream
709  *
710  * Utility routine to send a CRYPTIOCSTOP ioctl to the
711  * crypto module(cryptmod).
712  */
713 static void
714 stop_stream(int fd, int dir)
715 {
716 	struct strioctl  crioc;
717 	uint32_t stopdir = dir;
718 
719 	crioc.ic_cmd = CRYPTIOCSTOP;
720 	crioc.ic_timout = -1;
721 	crioc.ic_len = sizeof (stopdir);
722 	crioc.ic_dp = (char *)&stopdir;
723 
724 	if (ioctl(fd, I_STR, &crioc))
725 		syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
726 }
727 
728 /*
729  * start_stream
730  *
731  * Utility routine to send a CRYPTIOCSTART ioctl to the
732  * crypto module(cryptmod).  This routine may contain optional
733  * payload data that the cryptmod will interpret as bytes that
734  * need to be decrypted and sent back up to the application
735  * via the data stream.
736  */
737 static void
738 start_stream(int fd, int dir)
739 {
740 	struct strioctl crioc;
741 	uint32_t iocval;
742 	size_t datalen = 0;
743 	char *data = NULL;
744 
745 	if (dir == CRYPT_DECRYPT) {
746 		iocval = CRYPTIOCSTARTDEC;
747 
748 		/* Look for data not yet processed */
749 		if (ioctl(fd, I_NREAD, &datalen) < 0) {
750 			syslog(LOG_ERR, "I_NREAD returned error %m");
751 			datalen = 0;
752 		} else {
753 			if (datalen > 0) {
754 				data = malloc(datalen);
755 				if (data != NULL) {
756 					int nbytes = read(fd, data, datalen);
757 					datalen = nbytes;
758 				} else {
759 					syslog(LOG_ERR,
760 						"malloc error (%d bytes)",
761 						datalen);
762 					datalen = 0;
763 				}
764 			} else {
765 				datalen = 0;
766 			}
767 		}
768 	} else {
769 		iocval = CRYPTIOCSTARTENC;
770 	}
771 
772 	crioc.ic_cmd = iocval;
773 	crioc.ic_timout = -1;
774 	crioc.ic_len = datalen;
775 	crioc.ic_dp = data;
776 
777 	if (ioctl(fd, I_STR, &crioc))
778 		syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
779 
780 	if (data != NULL)
781 		free(data);
782 }
783 
784 static int
785 configure_stream(int fd, krb5_keyblock *skey, int dir, krb5_data *ivec,
786 		uint_t iv_usage)
787 {
788 	struct cr_info_t setup_info;
789 	struct strioctl crioc;
790 	int retval = 0;
791 
792 	switch (skey->enctype) {
793 	case ENCTYPE_DES_CBC_CRC:
794 		setup_info.crypto_method = CRYPT_METHOD_DES_CBC_CRC;
795 		break;
796 	case ENCTYPE_DES_CBC_MD5:
797 		setup_info.crypto_method = CRYPT_METHOD_DES_CBC_MD5;
798 		break;
799 	case ENCTYPE_DES_CBC_RAW:
800 		setup_info.crypto_method = CRYPT_METHOD_DES_CBC_NULL;
801 		break;
802 	case ENCTYPE_DES3_CBC_SHA1:
803 		setup_info.crypto_method = CRYPT_METHOD_DES3_CBC_SHA1;
804 		break;
805 	case ENCTYPE_ARCFOUR_HMAC:
806 		setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5;
807 		break;
808 	case ENCTYPE_ARCFOUR_HMAC_EXP:
809 		setup_info.crypto_method = CRYPT_METHOD_ARCFOUR_HMAC_MD5_EXP;
810 		break;
811 	case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
812 		setup_info.crypto_method = CRYPT_METHOD_AES128;
813 		break;
814 	case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
815 		setup_info.crypto_method = CRYPT_METHOD_AES256;
816 		break;
817 	default:
818 		syslog(LOG_ERR, "Enctype in kerberos session key "
819 		    "is not supported by crypto module(%d)",
820 		    skey->enctype);
821 		return (-1);
822 	}
823 	if (ivec == NULL || ivec->length == 0) {
824 		(void) memset(&setup_info.ivec, 0, sizeof (setup_info.ivec));
825 
826 		if (skey->enctype != ENCTYPE_ARCFOUR_HMAC &&
827 		    skey->enctype != ENCTYPE_ARCFOUR_HMAC_EXP)
828 			/* Kerberos IVs are 8 bytes long for DES keys */
829 			setup_info.iveclen = KRB5_MIT_DES_KEYSIZE;
830 		else
831 			setup_info.iveclen = 0;
832 	} else {
833 		(void) memcpy(&setup_info.ivec, ivec->data, ivec->length);
834 		setup_info.iveclen = ivec->length;
835 	}
836 
837 	setup_info.ivec_usage = iv_usage;
838 	(void) memcpy(&setup_info.key, skey->contents, skey->length);
839 
840 	setup_info.keylen = skey->length;
841 	setup_info.direction_mask = dir;
842 	/*
843 	 * R* commands get special handling by crypto module -
844 	 * 4 byte length field is used before each encrypted block
845 	 * of data.
846 	 */
847 	setup_info.option_mask = (kcmd_protocol == KCMD_OLD_PROTOCOL ?
848 				CRYPTOPT_RCMD_MODE_V1 :
849 				CRYPTOPT_RCMD_MODE_V2);
850 
851 	crioc.ic_cmd = CRYPTIOCSETUP;
852 	crioc.ic_timout = -1;
853 	crioc.ic_len = sizeof (setup_info);
854 	crioc.ic_dp = (char *)&setup_info;
855 
856 	if (ioctl(fd, I_STR, &crioc)) {
857 		syslog(LOG_ERR, "Error sending CRYPTIOCSETUP ioctl: %m");
858 		retval = -1;
859 	}
860 	return (retval);
861 }
862 
863 static krb5_error_code
864 krb5_compat_recvauth(krb5_context context,
865 		    krb5_auth_context *auth_context,
866 		    krb5_pointer fdp,	/* IN */
867 		    krb5_principal server,	/* IN */
868 		    krb5_int32 flags,	/* IN */
869 		    krb5_keytab keytab,	/* IN */
870 		    krb5_ticket **ticket, /* OUT */
871 		    krb5_int32 *auth_sys, /* OUT */
872 		    krb5_data *version)   /* OUT */
873 {
874 	krb5_int32 vlen;
875 	char	*buf;
876 	int	len, length;
877 	krb5_int32	retval;
878 	int		fd = *((int *)fdp);
879 
880 	if ((retval = krb5_net_read(context, fd, (char *)&vlen, 4)) != 4)
881 		return ((retval < 0) ? errno : ECONNABORTED);
882 
883 	/*
884 	 * Assume that we're talking to a V5 recvauth; read in the
885 	 * the version string, and make sure it matches.
886 	 */
887 	len = (int)ntohl(vlen);
888 
889 	if (len < 0 || len > 255)
890 		return (KRB5_SENDAUTH_BADAUTHVERS);
891 
892 	buf = malloc(len);
893 	if (buf == NULL)
894 		return (ENOMEM);
895 
896 	length = krb5_net_read(context, fd, buf, len);
897 	if (len != length) {
898 		krb5_xfree(buf);
899 		return ((len < 0) ? errno : ECONNABORTED);
900 	}
901 
902 	if (strcmp(buf, KRB_V5_SENDAUTH_VERS) != 0) {
903 		krb5_xfree(buf);
904 		return (KRB5_SENDAUTH_BADAUTHVERS);
905 	}
906 	krb5_xfree(buf);
907 
908 	*auth_sys = KRB5_RECVAUTH_V5;
909 
910 	retval = krb5_recvauth_version(context, auth_context, fdp,
911 				    server, flags | KRB5_RECVAUTH_SKIP_VERSION,
912 				    keytab, ticket, version);
913 
914 	return (retval);
915 }
916 
917 
918 static void
919 doit(int f,
920 	struct sockaddr_storage *fromp,
921 	krb5_context krb_context,
922 	int encr_flag,
923 	krb5_keytab keytab)
924 {
925 	int p, t, on = 1;
926 	char c;
927 	char abuf[INET6_ADDRSTRLEN];
928 	struct sockaddr_in *sin;
929 	struct sockaddr_in6 *sin6;
930 	int fromplen;
931 	in_port_t port;
932 	struct termios tp;
933 	boolean_t bad_port;
934 	boolean_t no_name;
935 	char rhost_addra[INET6_ADDRSTRLEN];
936 
937 	if (!(rlbuf = malloc(BUFSIZ))) {
938 		syslog(LOG_ERR, "rlbuf malloc failed\n");
939 		exit(EXIT_FAILURE);
940 	}
941 	(void) alarm(60);
942 	if (read(f, &c, 1) != 1 || c != 0) {
943 		syslog(LOG_ERR, "failed to receive protocol zero byte\n");
944 		exit(EXIT_FAILURE);
945 	}
946 	(void) alarm(0);
947 	if (fromp->ss_family == AF_INET) {
948 		sin = (struct sockaddr_in *)fromp;
949 		port = sin->sin_port = ntohs((ushort_t)sin->sin_port);
950 		fromplen = sizeof (struct sockaddr_in);
951 
952 		if (!inet_ntop(AF_INET, &sin->sin_addr,
953 			    rhost_addra, sizeof (rhost_addra)))
954 			goto badconversion;
955 	} else if (fromp->ss_family == AF_INET6) {
956 		sin6 = (struct sockaddr_in6 *)fromp;
957 		port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port);
958 		fromplen = sizeof (struct sockaddr_in6);
959 
960 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
961 			struct in_addr ipv4_addr;
962 
963 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
964 					    &ipv4_addr);
965 			if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra,
966 				    sizeof (rhost_addra)))
967 				goto badconversion;
968 		} else {
969 			if (!inet_ntop(AF_INET6, &sin6->sin6_addr,
970 				    rhost_addra, sizeof (rhost_addra)))
971 				goto badconversion;
972 		}
973 	} else {
974 		syslog(LOG_ERR, "unknown address family %d\n",
975 		    fromp->ss_family);
976 		fatal(f, "Permission denied");
977 	}
978 
979 	/*
980 	 * Allow connections only from the "ephemeral" reserved
981 	 * ports(ports 512 - 1023) by checking the remote port
982 	 * because other utilities(e.g. in.ftpd) can be used to
983 	 * allow a unprivileged user to originate a connection
984 	 * from a privileged port and provide untrustworthy
985 	 * authentication.
986 	 */
987 	bad_port = (use_auth != KRB5_RECVAUTH_V5 &&
988 		    (port >= (in_port_t)IPPORT_RESERVED) ||
989 		    (port < (in_port_t)(IPPORT_RESERVED/2)));
990 	no_name = getnameinfo((const struct sockaddr *) fromp,
991 			    fromplen, hostname, sizeof (hostname),
992 			    NULL, 0, 0) != 0;
993 
994 	if (no_name || bad_port) {
995 		(void) strlcpy(abuf, rhost_addra, sizeof (abuf));
996 		/* If no host name, use IP address for name later on. */
997 		if (no_name)
998 			(void) strlcpy(hostname, abuf, sizeof (hostname));
999 	}
1000 
1001 	if (!no_name) {
1002 		/*
1003 		 * Even if getnameinfo() succeeded, we still have to check
1004 		 * for spoofing.
1005 		 */
1006 		check_address("rlogind", fromp, sin, sin6, rhost_addra,
1007 		    hostname, sizeof (hostname));
1008 	}
1009 
1010 	if (bad_port) {
1011 		if (no_name)
1012 			syslog(LOG_NOTICE,
1013 			    "connection from %s - bad port\n",
1014 			    abuf);
1015 		else
1016 			syslog(LOG_NOTICE,
1017 			    "connection from %s(%s) - bad port\n",
1018 			    hostname, abuf);
1019 		fatal(f, "Permission denied");
1020 	}
1021 
1022 	if (use_auth == KRB5_RECVAUTH_V5) {
1023 		do_krb_login(f, rhost_addra, hostname,
1024 			    krb_context, encr_flag, keytab);
1025 		if (krusername != NULL && strlen(krusername)) {
1026 			/*
1027 			 * Kerberos Authentication succeeded,
1028 			 * so set the proper program name to use
1029 			 * with pam (important during 'cleanup'
1030 			 * routine later).
1031 			 */
1032 			pam_prog_name = KRB5_PROG_NAME;
1033 		}
1034 	}
1035 
1036 	if (write(f, "", 1) != 1) {
1037 		syslog(LOG_NOTICE,
1038 		    "send of the zero byte(to %s) failed:"
1039 		    " cannot start data transfer mode\n",
1040 		    (no_name ? abuf : hostname));
1041 		exit(EXIT_FAILURE);
1042 	}
1043 	if ((p = open("/dev/ptmx", O_RDWR)) == -1)
1044 		fatalperror(f, "cannot open /dev/ptmx");
1045 	if (grantpt(p) == -1)
1046 		fatal(f, "could not grant slave pty");
1047 	if (unlockpt(p) == -1)
1048 		fatal(f, "could not unlock slave pty");
1049 	if ((line = ptsname(p)) == NULL)
1050 		fatal(f, "could not enable slave pty");
1051 	if ((t = open(line, O_RDWR)) == -1)
1052 		fatal(f, "could not open slave pty");
1053 	if (ioctl(t, I_PUSH, "ptem") == -1)
1054 		fatalperror(f, "ioctl I_PUSH ptem");
1055 	if (ioctl(t, I_PUSH, "ldterm") == -1)
1056 		fatalperror(f, "ioctl I_PUSH ldterm");
1057 	if (ioctl(t, I_PUSH, "ttcompat") == -1)
1058 		fatalperror(f, "ioctl I_PUSH ttcompat");
1059 	/*
1060 	 * POP the sockmod and push the rlmod module.
1061 	 *
1062 	 * Note that sockmod has to be removed since readstream assumes
1063 	 * a "raw" TPI endpoint(e.g. it uses getmsg).
1064 	 */
1065 	if (removemod(f, "sockmod") < 0)
1066 		fatalperror(f, "couldn't remove sockmod");
1067 
1068 	if (encr_flag) {
1069 		if (ioctl(f, I_PUSH, "cryptmod") < 0)
1070 		    fatalperror(f, "ioctl I_PUSH rlmod");
1071 
1072 	}
1073 
1074 	if (ioctl(f, I_PUSH, "rlmod") < 0)
1075 		fatalperror(f, "ioctl I_PUSH rlmod");
1076 
1077 	if (encr_flag) {
1078 		/*
1079 		 * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
1080 		 */
1081 		uchar_t passthru = 1;
1082 		struct strioctl rlmodctl;
1083 
1084 		rlmodctl.ic_cmd = CRYPTPASSTHRU;
1085 		rlmodctl.ic_timout = -1;
1086 		rlmodctl.ic_len = sizeof (uchar_t);
1087 		rlmodctl.ic_dp = (char *)&passthru;
1088 
1089 		if (ioctl(f, I_STR, &rlmodctl) < 0)
1090 			fatal(f, "ioctl CRYPTPASSTHRU failed\n");
1091 	}
1092 
1093 	/*
1094 	 * readstream will do a getmsg till it receives
1095 	 * M_PROTO type T_DATA_REQ from rloginmodopen()
1096 	 * indicating all data on the stream prior to pushing rlmod has
1097 	 * been drained at the stream head.
1098 	 */
1099 	if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0)
1100 		fatalperror(f, "readstream failed");
1101 	/*
1102 	 * Make sure the pty doesn't modify the strings passed
1103 	 * to login as part of the "rlogin protocol."  The login
1104 	 * program should set these flags to apropriate values
1105 	 * after it has read the strings.
1106 	 */
1107 	if (ioctl(t, TCGETS, &tp) == -1)
1108 		fatalperror(f, "ioctl TCGETS");
1109 	tp.c_lflag &= ~(ECHO|ICANON);
1110 	tp.c_oflag &= ~(XTABS|OCRNL);
1111 	tp.c_iflag &= ~(IGNPAR|ICRNL);
1112 	if (ioctl(t, TCSETS, &tp) == -1)
1113 		fatalperror(f, "ioctl TCSETS");
1114 
1115 	/*
1116 	 * System V ptys allow the TIOC{SG}WINSZ ioctl to be
1117 	 * issued on the master side of the pty.  Luckily, that's
1118 	 * the only tty ioctl we need to do do, so we can close the
1119 	 * slave side in the parent process after the fork.
1120 	 */
1121 	(void) ioctl(p, TIOCSWINSZ, &win);
1122 
1123 	pid = fork();
1124 	if (pid < 0)
1125 		fatalperror(f, "fork");
1126 	if (pid == 0) {
1127 		int tt;
1128 		struct utmpx ut;
1129 
1130 		/* System V login expects a utmp entry to already be there */
1131 		(void) memset(&ut, 0, sizeof (ut));
1132 		(void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user));
1133 		(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
1134 		ut.ut_pid = getpid();
1135 		ut.ut_id[0] = 'r';
1136 		ut.ut_id[1] = (char)SC_WILDC;
1137 		ut.ut_id[2] = (char)SC_WILDC;
1138 		ut.ut_id[3] = (char)SC_WILDC;
1139 		ut.ut_type = LOGIN_PROCESS;
1140 		ut.ut_exit.e_termination = 0;
1141 		ut.ut_exit.e_exit = 0;
1142 		(void) time(&ut.ut_tv.tv_sec);
1143 		if (makeutx(&ut) == NULL)
1144 			syslog(LOG_INFO, "in.rlogind:\tmakeutx failed");
1145 
1146 		/* controlling tty */
1147 		if (setsid() == -1)
1148 			fatalperror(f, "setsid");
1149 		if ((tt = open(line, O_RDWR)) == -1)
1150 			fatalperror(f, "could not re-open slave pty");
1151 
1152 		if (close(p) == -1)
1153 			fatalperror(f, "error closing pty master");
1154 		if (close(t) == -1)
1155 			fatalperror(f, "error closing pty slave"
1156 				    " opened before session established");
1157 		/*
1158 		 * If this fails we may or may not be able to output an
1159 		 * error message.
1160 		 */
1161 		if (close(f) == -1)
1162 			fatalperror(f, "error closing deamon stdout");
1163 		if (dup2(tt, STDIN_FILENO) == -1 ||
1164 		    dup2(tt, STDOUT_FILENO) == -1 ||
1165 		    dup2(tt, STDERR_FILENO) == -1)
1166 			exit(EXIT_FAILURE);	/* Disaster! No stderr! */
1167 
1168 		(void) close(tt);
1169 
1170 		if (use_auth == KRB5_RECVAUTH_V5 &&
1171 		    krusername != NULL && strlen(krusername)) {
1172 			(void) execl(LOGIN_PROGRAM, "login",
1173 				    "-d", line,
1174 				    "-r", hostname,
1175 				    "-u", krusername, /* KRB5 principal name */
1176 				    "-s", pam_prog_name,
1177 				    "-t", term,	/* Remote Terminal */
1178 				    "-U", rusername,	/* Remote User */
1179 				    "-R", KRB5_REPOSITORY_NAME,
1180 				    lusername,  /* local user */
1181 				    NULL);
1182 		} else {
1183 			(void) execl(LOGIN_PROGRAM, "login",
1184 				"-d", line,
1185 				"-r", hostname,
1186 				NULL);
1187 		}
1188 
1189 		fatalperror(STDERR_FILENO, "/bin/login");
1190 		/*NOTREACHED*/
1191 	}
1192 	(void) close(t);
1193 	(void) ioctl(f, FIONBIO, &on);
1194 	(void) ioctl(p, FIONBIO, &on);
1195 
1196 	/*
1197 	 * Must ignore SIGTTOU, otherwise we'll stop
1198 	 * when we try and set slave pty's window shape
1199 	 * (our controlling tty is the master pty).
1200 	 * Likewise, we don't want any of the tty-generated
1201 	 * signals from chars passing through.
1202 	 */
1203 	(void) sigset(SIGTSTP, SIG_IGN);
1204 	(void) sigset(SIGINT, SIG_IGN);
1205 	(void) sigset(SIGQUIT, SIG_IGN);
1206 	(void) sigset(SIGTTOU, SIG_IGN);
1207 	(void) sigset(SIGTTIN, SIG_IGN);
1208 	(void) sigset(SIGCHLD, cleanup);
1209 	(void) setpgrp();
1210 
1211 	if (encr_flag) {
1212 		krb5_data ivec, *ivptr;
1213 		uint_t ivec_usage;
1214 		stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT);
1215 
1216 		/*
1217 		 * Configure the STREAMS crypto module.  For now,
1218 		 * don't use any IV parameter.  KCMDV0.2 support
1219 		 * will require the use of Initialization Vectors
1220 		 * for both encrypt and decrypt modes.
1221 		 */
1222 		if (kcmd_protocol == KCMD_OLD_PROTOCOL) {
1223 			if (session_key->enctype == ENCTYPE_DES_CBC_CRC) {
1224 				/*
1225 				 * This is gross but necessary for MIT compat.
1226 				 */
1227 				ivec.length = session_key->length;
1228 				ivec.data = (char *)session_key->contents;
1229 				ivec_usage = IVEC_REUSE;
1230 				ivptr = &ivec;
1231 			} else {
1232 				ivptr = NULL; /* defaults to all 0's */
1233 				ivec_usage = IVEC_NEVER;
1234 			}
1235 			/*
1236 			 * configure both sides of stream together
1237 			 * since they share the same IV.
1238 			 * This is what makes the OLD KCMD protocol
1239 			 * less secure than the newer one - Bad ivecs.
1240 			 */
1241 			if (configure_stream(f, session_key,
1242 				CRYPT_ENCRYPT|CRYPT_DECRYPT,
1243 				ivptr, ivec_usage) != 0)
1244 				fatal(f, "Cannot initialize encryption -"
1245 					" exiting.\n");
1246 		} else {
1247 			size_t blocksize;
1248 			if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC ||
1249 			    session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
1250 				if (configure_stream(f, session_key,
1251 					CRYPT_ENCRYPT|CRYPT_DECRYPT,
1252 					NULL, IVEC_NEVER) != 0)
1253 					fatal(f,
1254 					"Cannot initialize encryption -"
1255 					" exiting.\n");
1256 				goto startcrypto;
1257 			}
1258 			if (krb5_c_block_size(krb_context,
1259 					    session_key->enctype,
1260 					    &blocksize)) {
1261 				syslog(LOG_ERR, "Cannot determine blocksize "
1262 				    "for encryption type %d",
1263 				    session_key->enctype);
1264 				fatal(f, "Cannot determine blocksize "
1265 				    "for encryption - exiting.\n");
1266 			}
1267 			ivec.data = (char *)malloc(blocksize);
1268 			ivec.length = blocksize;
1269 			if (ivec.data == NULL)
1270 				fatal(f, "memory error - exiting\n");
1271 			/*
1272 			 * Following MIT convention -
1273 			 *   encrypt IV = 0x01 x blocksize
1274 			 *   decrypt IV = 0x00 x blocksize
1275 			 *   ivec_usage = IVEC_ONETIME
1276 			 *
1277 			 * configure_stream separately for encrypt and
1278 			 * decrypt because there are 2 different IVs.
1279 			 *
1280 			 * AES uses 0's for IV.
1281 			 */
1282 			if (session_key->enctype ==
1283 				ENCTYPE_AES128_CTS_HMAC_SHA1_96 ||
1284 			    session_key->enctype ==
1285 				ENCTYPE_AES256_CTS_HMAC_SHA1_96)
1286 				(void) memset(ivec.data, 0x00, blocksize);
1287 			else
1288 				(void) memset(ivec.data, 0x01, blocksize);
1289 			if (configure_stream(f, session_key, CRYPT_ENCRYPT,
1290 				&ivec, IVEC_ONETIME) != 0)
1291 				fatal(f, "Cannot initialize encryption -"
1292 					" exiting.\n");
1293 			(void) memset(ivec.data, 0x00, blocksize);
1294 			if (configure_stream(f, session_key, CRYPT_DECRYPT,
1295 				&ivec, IVEC_ONETIME) != 0)
1296 				fatal(f, "Cannot initialize encryption -"
1297 					" exiting.\n");
1298 
1299 			(void) free(ivec.data);
1300 		}
1301 startcrypto:
1302 		start_stream(f, CRYPT_ENCRYPT);
1303 		start_stream(f, CRYPT_DECRYPT);
1304 	}
1305 	protocol(f, p, encr_flag);
1306 	cleanup(0);
1307 	/*NOTREACHED*/
1308 
1309 badconversion:
1310 	fatalperror(f, "address conversion");
1311 	/*NOTREACHED*/
1312 }
1313 
1314 /*
1315  * rlogin "protocol" machine.
1316  */
1317 static void
1318 protocol(int f, int p, int encr_flag)
1319 {
1320 	struct	stat	buf;
1321 	struct 	protocol_arg	rloginp;
1322 	struct	strioctl	rloginmod;
1323 	int	ptmfd;	/* fd of logindmux coneected to ptmx */
1324 	int	netfd;	/* fd of logindmux connected to netf */
1325 	static uchar_t	oobdata[] = {TIOCPKT_WINDOW};
1326 
1327 	/* indicate new rlogin */
1328 	if (send_oob(f, oobdata, 1) < 0)
1329 		fatalperror(f, "send_oob");
1330 	/*
1331 	 * We cannot send the SECURE_MSG until after the
1332 	 * client has been signaled with the oobdata (above).
1333 	 */
1334 	if (encr_flag) {
1335 		if (write(f, SECURE_MSG, strlen(SECURE_MSG)) < 0)
1336 			fatalperror(f, "Error writing SECURE message");
1337 	}
1338 
1339 	/*
1340 	 * Open logindmux driver and link netf and ptmx
1341 	 * underneath logindmux.
1342 	 */
1343 	if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1)
1344 		fatalperror(f, "open /dev/logindmux");
1345 
1346 	if ((netfd = open("/dev/logindmux", O_RDWR)) == -1)
1347 		fatalperror(f, "open /dev/logindmux");
1348 
1349 	if (ioctl(ptmfd, I_LINK, p) < 0)
1350 		fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
1351 
1352 	if (ioctl(netfd, I_LINK, f) < 0)
1353 		fatal(f, "ioctl I_LINK of tcp connection failed\n");
1354 
1355 	/*
1356 	 * Figure out the device number of the ptm's mux fd, and pass that
1357 	 * to the net's mux.
1358 	 */
1359 	if (fstat(ptmfd, &buf) < 0)
1360 		fatalperror(f, "cannot determine device number"
1361 		    " of pty side of /dev/logindmux");
1362 	rloginp.dev = buf.st_rdev;
1363 	rloginp.flag = 0;
1364 
1365 	rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
1366 	rloginmod.ic_timout = -1;
1367 	rloginmod.ic_len = sizeof (struct protocol_arg);
1368 	rloginmod.ic_dp = (char *)&rloginp;
1369 
1370 	if (ioctl(netfd, I_STR, &rloginmod) < 0)
1371 		fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
1372 
1373 	/*
1374 	 * Figure out the device number of the net's mux fd, and pass that
1375 	 * to the ptm's mux.
1376 	 */
1377 	if (fstat(netfd, &buf))
1378 		fatalperror(f, "cannot determine device number"
1379 		    " of network side of /dev/logindmux");
1380 	rloginp.dev = buf.st_rdev;
1381 	rloginp.flag = 1;
1382 
1383 	rloginmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
1384 	rloginmod.ic_timout = -1;
1385 	rloginmod.ic_len = sizeof (struct protocol_arg);
1386 	rloginmod.ic_dp = (char *)&rloginp;
1387 
1388 	if (ioctl(ptmfd, I_STR, &rloginmod) < 0)
1389 		fatal(netfd, "ioctl LOGDMXZ_IOC_QEXCHANGE of ptmfd failed\n");
1390 	/*
1391 	 * Send an ioctl type RL_IOC_ENABLE to reenable the
1392 	 * message queue and reinsert the data read from streamhead
1393 	 * at the time of pushing rloginmod module.
1394 	 * We need to send this ioctl even if no data was read earlier
1395 	 * since we need to reenable the message queue of rloginmod module.
1396 	 */
1397 	rloginmod.ic_cmd = RL_IOC_ENABLE;
1398 	rloginmod.ic_timout = -1;
1399 	if (nsize) {
1400 		rloginmod.ic_len = nsize;
1401 		rloginmod.ic_dp = rlbuf;
1402 	} else {
1403 		rloginmod.ic_len = 0;
1404 		rloginmod.ic_dp = NULL;
1405 	}
1406 
1407 	if (ioctl(netfd, I_STR, &rloginmod) < 0)
1408 		fatal(netfd, "ioctl RL_IOC_ENABLE of netfd failed\n");
1409 
1410 	/*
1411 	 * User level daemon now pauses till the shell exits.
1412 	 */
1413 	(void) pause();
1414 }
1415 
1416 /* This is a signal handler, hence the dummy argument */
1417 /*ARGSUSED*/
1418 static void
1419 cleanup(int dummy)
1420 {
1421 	rmut();
1422 	exit(EXIT_FAILURE);
1423 	/*NOTREACHED*/
1424 }
1425 
1426 /*
1427  * TPI style replacement for socket send() primitive, so we don't require
1428  * sockmod to be on the stream.
1429  */
1430 static int
1431 send_oob(int fd, void *ptr, size_t count)
1432 {
1433 	struct T_exdata_req exd_req;
1434 	struct strbuf hdr, dat;
1435 	int ret;
1436 
1437 	exd_req.PRIM_type = T_EXDATA_REQ;
1438 	exd_req.MORE_flag = 0;
1439 
1440 	hdr.buf = (char *)&exd_req;
1441 	hdr.len = sizeof (exd_req);
1442 
1443 	dat.buf = ptr;
1444 	dat.len = count;
1445 
1446 	ret = putmsg(fd, &hdr, &dat, 0);
1447 	if (ret == 0)
1448 		ret = count;
1449 	return (ret);
1450 }
1451 
1452 static void
1453 fatal(int fd, const char *msg)
1454 {
1455 	char *bufp;
1456 	size_t len = strlen(msg) + 16;		/* enough for our wrapper */
1457 
1458 	bufp = alloca(len);
1459 	/* ASCII 001 is the error indicator */
1460 	len = snprintf(bufp, len, "\01rlogind: %s.\r\n", msg);
1461 	(void) write(fd, bufp, len);
1462 	exit(EXIT_FAILURE);
1463 	/*NOTREACHED*/
1464 }
1465 
1466 /*PRINTFLIKE2*/
1467 static void
1468 fatalperror(int fd, const char *msg)
1469 {
1470 	char *bufp;
1471 	const char *errstr;
1472 	int save_errno = errno;
1473 	size_t len = strlen(msg);
1474 
1475 	if ((errstr = strerror(save_errno))) {
1476 		len += strlen(errstr) + 3;	/* 3 for ": " and \0 below */
1477 		bufp = alloca(len);
1478 		(void) snprintf(bufp, len, "%s: %s", msg, errstr);
1479 	} else {
1480 		const char fmt[] = "%s: Error %d";
1481 
1482 		/* -4 for %s & %d. "*8/3" is bytes->decimal, pessimistically */
1483 		len += sizeof (fmt) -4 + (sizeof (save_errno) *8 /3);
1484 		bufp = alloca(len);
1485 		(void) snprintf(bufp, len, fmt, msg, save_errno);
1486 	}
1487 	fatal(fd, bufp);
1488 	/*NOTREACHED*/
1489 }
1490 
1491 static void
1492 rmut(void)
1493 {
1494 	pam_handle_t *pamh;
1495 	struct utmpx *up;
1496 	char user[sizeof (up->ut_user) + 1];
1497 	char ttyn[sizeof (up->ut_line) + 1];
1498 	char rhost[sizeof (up->ut_host) + 1];
1499 
1500 	/* while cleaning up dont allow disruption */
1501 	(void) sigset(SIGCHLD, SIG_IGN);
1502 
1503 	setutxent();
1504 	while (up = getutxent()) {
1505 		if (up->ut_pid == pid) {
1506 			if (up->ut_type == DEAD_PROCESS)
1507 				break;		/* Cleaned up elsewhere. */
1508 
1509 			/*
1510 			 * call pam_close_session if login changed
1511 			 * the utmpx user entry from type LOGIN_PROCESS
1512 			 * to type USER_PROCESS, which happens
1513 			 * after pam_open_session is called.
1514 			 */
1515 			if (up->ut_type == USER_PROCESS) {
1516 				(void) strlcpy(user, up->ut_user,
1517 					    sizeof (user));
1518 				(void) strlcpy(ttyn, up->ut_line,
1519 					    sizeof (ttyn));
1520 				(void) strlcpy(rhost, up->ut_host,
1521 					    sizeof (rhost));
1522 
1523 				/*
1524 				 * Use the same pam_prog_name that
1525 				 * 'login' used.
1526 				 */
1527 				if ((pam_start(pam_prog_name, user,  NULL,
1528 					    &pamh))
1529 				    == PAM_SUCCESS) {
1530 					(void) pam_set_item(pamh, PAM_TTY,
1531 							    ttyn);
1532 					(void) pam_set_item(pamh, PAM_RHOST,
1533 							    rhost);
1534 					(void) pam_close_session(pamh, 0);
1535 					(void) pam_end(pamh, PAM_SUCCESS);
1536 				}
1537 			}
1538 
1539 			up->ut_type = DEAD_PROCESS;
1540 			up->ut_exit.e_termination = WTERMSIG(0);
1541 			up->ut_exit.e_exit = WEXITSTATUS(0);
1542 			(void) time(&up->ut_tv.tv_sec);
1543 
1544 			if (modutx(up) == NULL) {
1545 				/*
1546 				 * Since modutx failed we'll
1547 				 * write out the new entry
1548 				 * ourselves.
1549 				 */
1550 				(void) pututxline(up);
1551 				updwtmpx("wtmpx", up);
1552 			}
1553 			break;
1554 		}
1555 	}
1556 
1557 	endutxent();
1558 
1559 	(void) sigset(SIGCHLD, cleanup);
1560 }
1561 
1562 static int
1563 readstream(int fd, char *buf, int size)
1564 {
1565 	struct strbuf ctlbuf, datbuf;
1566 	union T_primitives tpi;
1567 	int	nbytes = 0;
1568 	int	ret = 0;
1569 	int	flags = 0;
1570 	int	bufsize = size;
1571 	int	nread;
1572 
1573 	(void) memset(&ctlbuf, 0, sizeof (ctlbuf));
1574 	(void) memset(&datbuf, 0, sizeof (datbuf));
1575 
1576 	ctlbuf.buf = (char *)&tpi;
1577 	ctlbuf.maxlen = sizeof (tpi);
1578 	datbuf.buf = buf;
1579 	datbuf.maxlen = size;
1580 
1581 	for (;;) {
1582 		if (ioctl(fd, I_NREAD, &nread) < 0) {
1583 			syslog(LOG_ERR, "I_NREAD returned error %m");
1584 			return (-1);
1585 		}
1586 		if (nread + nbytes > bufsize) {
1587 			buf = (char *)realloc(buf, (unsigned)(bufsize + nread));
1588 			if (buf == NULL) {
1589 				syslog(LOG_WARNING,
1590 				    "buffer allocation failed\n");
1591 				return (-1);
1592 			}
1593 			bufsize += nread;
1594 			rlbuf = buf;
1595 			datbuf.buf = buf + nbytes;
1596 		}
1597 		datbuf.maxlen = bufsize - nbytes;
1598 		ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
1599 		if (ret < 0) {
1600 			syslog(LOG_ERR, "getmsg failed error %m");
1601 			return (-1);
1602 		}
1603 		if ((ctlbuf.len == 0) && (datbuf.len == 0)) {
1604 			/*
1605 			 * getmsg() returned no data - this indicates
1606 			 * that the connection is closing down.
1607 			 */
1608 			cleanup(0);
1609 		}
1610 		if (ctlbuf.len <= 0) {
1611 			nbytes += datbuf.len;
1612 			datbuf.buf += datbuf.len;
1613 			continue;
1614 		}
1615 		if (tpi.type == T_DATA_REQ) {
1616 			return (nbytes);
1617 		}
1618 		if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
1619 			cleanup(0);
1620 	}
1621 }
1622 
1623 /*
1624  * Verify that the named module is at the top of the stream
1625  * and then pop it off.
1626  */
1627 static int
1628 removemod(int f, char *modname)
1629 {
1630 	char topmodname[BUFSIZ];
1631 
1632 	if (ioctl(f, I_LOOK, topmodname) < 0)
1633 		return (-1);
1634 	if (strcmp(modname, topmodname) != 0) {
1635 		errno = ENXIO;
1636 		return (-1);
1637 	}
1638 	if (ioctl(f, I_POP, 0) < 0)
1639 		return (-1);
1640 	return (0);
1641 }
1642