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