1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1983-1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35#define	_FILE_OFFSET_BITS 64
36
37/*
38 * remote shell server:
39 *	remuser\0
40 *	locuser\0
41 *	command\0
42 *	data
43 */
44#include <sys/types.h>
45#include <sys/ioctl.h>
46#include <sys/telioctl.h>
47#include <sys/param.h>
48#include <sys/socket.h>
49#include <sys/time.h>
50#include <sys/stat.h>
51#include <sys/file.h>
52#include <sys/select.h>
53
54#include <netinet/in.h>
55
56#include <arpa/inet.h>
57
58#include <unistd.h>
59#include <string.h>
60#include <stdio.h>
61#include <stdarg.h>
62#include <errno.h>
63#include <pwd.h>
64#include <grp.h>
65#include <signal.h>
66#include <netdb.h>
67#include <syslog.h>
68#include <fcntl.h>
69#include <ctype.h>
70#include <locale.h>
71
72#include <sys/resource.h>
73#include <sys/filio.h>
74#include <shadow.h>
75#include <stdlib.h>
76
77#include <security/pam_appl.h>
78#include <deflt.h>
79
80#include <k5-int.h>
81#include <krb5_repository.h>
82#include <com_err.h>
83#include <kcmd.h>
84
85#include <addr_match.h>
86#include <store_forw_creds.h>
87
88#ifndef NCARGS
89#define	NCARGS	5120
90#endif /* !NCARGS */
91
92static void error(char *, ...);
93static void doit(int, struct sockaddr_storage *, char **);
94static void getstr(int, char *, int, char *);
95
96static int legalenvvar(char *);
97static void add_to_envinit(char *);
98static int locale_envmatch(char *, char *);
99
100/* Function decls. for functions not in any header file.  (Grrrr.) */
101extern int audit_rshd_setup(void);
102extern int audit_rshd_success(char *, char *, char *, char *);
103extern int audit_rshd_fail(char *, char *, char *, char *, char *);
104extern int audit_settid(int);
105
106static int do_encrypt = 0;
107static pam_handle_t *pamh;
108
109/*
110 * This is the shell/kshell daemon. The very basic protocol for checking
111 * authentication and authorization is:
112 * 1) Check authentication.
113 * 2) Check authorization via the access-control files:
114 *    ~/.k5login (using krb5_kuserok) and/or
115 * Execute command if configured authoriztion checks pass, else deny
116 * permission.
117 *
118 * The configuration is done either by command-line arguments passed by inetd,
119 * or by the name of the daemon. If command-line arguments are present, they
120 * take priority. The options are:
121 * -k allow kerberos authentication (krb5 only; krb4 support is not provided)
122 * -5 same as `-k', mainly for compatability with MIT
123 * -e allow encrypted session
124 * -c demand authenticator checksum
125 * -i ignore authenticator checksum
126 * -U Refuse connections that cannot be mapped to a name via `gethostbyname'
127 * -s <tos>	Set the IP TOS option
128 * -S <keytab>	Set the keytab file to use
129 * -M <realm>	Set the Kerberos realm to use
130 */
131
132#define	ARGSTR	"ek5ciUD:M:S:L:?:"
133#define	RSHD_BUFSIZ	(50 * 1024)
134
135static krb5_context bsd_context;
136static krb5_keytab keytab = NULL;
137static krb5_ccache ccache = NULL;
138static krb5_keyblock *sessionkey = NULL;
139
140static int require_encrypt = 0;
141static int resolve_hostname = 0;
142static int krb5auth_flag = 0;	/* Flag set, when KERBEROS is enabled */
143static enum kcmd_proto kcmd_protocol;
144
145#ifdef DEBUG
146static int debug_port = 0;
147#endif /* DEBUG */
148
149/*
150 * There are two authentication related masks:
151 * auth_ok and auth_sent.
152 * The auth_ok mask is the or'ing of authentication
153 * systems any one of which can be used.
154 * The auth_sent mask is the or'ing of one or more authentication/authorization
155 * systems that succeeded.  If the and'ing
156 * of these two masks is true, then authorization is successful.
157 */
158
159#define	AUTH_KRB5	(0x2)
160static int auth_ok = 0;
161static int auth_sent = 0;
162static int checksum_required = 0;
163static int checksum_ignored = 0;
164
165/*
166 * Leave room for 4 environment variables to be passed.
167 * The "-L env_var" option has been added primarily to
168 * maintain compatability with MIT.
169 */
170#define	MAXENV	4
171static char *save_env[MAXENV];
172static int num_env = 0;
173
174static void usage(void);
175static krb5_error_code recvauth(int, int *);
176
177/*ARGSUSED*/
178int
179main(int argc, char **argv, char **renvp)
180{
181	struct linger linger;
182	int on = 1, fromlen;
183	struct sockaddr_storage from;
184	int fd = 0;
185
186	extern int opterr, optind;
187	extern char *optarg;
188	int ch;
189	int tos = -1;
190	krb5_error_code status;
191
192	openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
193	(void) audit_rshd_setup();	/* BSM */
194	fromlen = sizeof (from);
195
196	(void) setlocale(LC_ALL, "");
197
198	/*
199	 * Analyze parameters.
200	 */
201	opterr = 0;
202	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
203		switch (ch) {
204		case '5':
205		case 'k':
206			auth_ok |= AUTH_KRB5;
207			krb5auth_flag++;
208			break;
209
210		case 'c':
211			checksum_required = 1;
212			krb5auth_flag++;
213			break;
214		case 'i':
215			checksum_ignored = 1;
216			krb5auth_flag++;
217			break;
218
219		case 'e':
220			require_encrypt = 1;
221			krb5auth_flag++;
222			break;
223#ifdef DEBUG
224		case 'D':
225			debug_port = atoi(optarg);
226			break;
227#endif /* DEBUG */
228		case 'U':
229			resolve_hostname = 1;
230			break;
231
232		case 'M':
233			(void) krb5_set_default_realm(bsd_context, optarg);
234			krb5auth_flag++;
235			break;
236
237		case 'S':
238			if ((status = krb5_kt_resolve(bsd_context, optarg,
239				&keytab))) {
240				com_err("rsh", status,
241					gettext("while resolving "
242						"srvtab file %s"), optarg);
243				exit(2);
244			}
245			krb5auth_flag++;
246			break;
247
248		case 's':
249			if (optarg == NULL || ((tos = atoi(optarg)) < 0) ||
250				(tos > 255)) {
251				syslog(LOG_ERR, "rshd: illegal tos value: "
252				    "%s\n", optarg);
253			}
254			break;
255
256		case 'L':
257			if (num_env < MAXENV) {
258				save_env[num_env] = strdup(optarg);
259				if (!save_env[num_env++]) {
260					com_err("rsh", ENOMEM,
261						gettext("in saving env"));
262					exit(2);
263				}
264			} else {
265				(void) fprintf(stderr, gettext("rshd: Only %d"
266						" -L arguments allowed\n"),
267						MAXENV);
268				exit(2);
269			}
270			break;
271
272		case '?':
273		default:
274			usage();
275			exit(1);
276			break;
277		}
278
279	if (optind == 0) {
280		usage();
281		exit(1);
282	}
283	argc -= optind;
284	argv += optind;
285
286	if (krb5auth_flag > 0) {
287		status = krb5_init_context(&bsd_context);
288		if (status) {
289			syslog(LOG_ERR, "Error initializing krb5: %s",
290			    error_message(status));
291			exit(1);
292		}
293	}
294
295	if (!checksum_required && !checksum_ignored)
296		checksum_ignored = 1;
297
298	if (checksum_required && checksum_ignored) {
299		syslog(LOG_CRIT, gettext("Checksums are required and ignored."
300		"These options are mutually exclusive"
301		"--check the documentation."));
302		error("Configuration error: mutually exclusive "
303				"options specified.\n");
304		exit(1);
305	}
306
307#ifdef DEBUG
308	if (debug_port) {
309		int s;
310		struct sockaddr_in sin;
311
312		if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
313			fprintf(stderr, gettext("Error in socket: %s\n"),
314					strerror(errno));
315			exit(2);
316		}
317		(void) memset((char *)&sin, 0, sizeof (sin));
318		sin.sin_family = AF_INET;
319		sin.sin_port = htons(debug_port);
320		sin.sin_addr.s_addr = INADDR_ANY;
321
322		(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
323			(char *)&on, sizeof (on));
324
325		if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
326			(void) fprintf(stderr, gettext("Error in bind: %s\n"),
327					strerror(errno));
328			exit(2);
329		}
330		if ((listen(s, 5)) < 0) {
331			(void) fprintf(stderr, gettext("Error in listen: %s\n"),
332					strerror(errno));
333			exit(2);
334		}
335		if ((fd = accept(s, (struct sockaddr *)&from,
336					&fromlen)) < 0) {
337			(void) fprintf(stderr, gettext("Error in accept: %s\n"),
338					strerror(errno));
339			exit(2);
340		}
341		(void) close(s);
342	}
343	else
344#endif /* DEBUG */
345	{
346		if (getpeername(STDIN_FILENO, (struct sockaddr *)&from,
347				(socklen_t *)&fromlen) < 0) {
348			(void) fprintf(stderr, "rshd: ");
349			perror("getpeername");
350			_exit(1);
351		}
352		fd = STDIN_FILENO;
353	}
354
355	if (audit_settid(fd) != 0) {
356		perror("settid");
357		exit(1);
358	}
359
360	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
361	    sizeof (on)) < 0)
362		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
363	linger.l_onoff = 1;
364	linger.l_linger = 60;			/* XXX */
365	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger,
366	    sizeof (linger)) < 0)
367		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
368
369	if ((tos != -1) && (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
370				sizeof (tos)) < 0) &&
371				(errno != ENOPROTOOPT)) {
372		syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m");
373	}
374
375	doit(dup(fd), &from, renvp);
376	return (0);
377}
378
379/*
380 * locale environments to be passed to shells.
381 */
382static char *localeenv[] = {
383	"LANG",
384	"LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
385	"LC_MONETARY", "LC_MESSAGES", "LC_ALL", NULL};
386
387/*
388 * The following is for the environment variable list
389 * used in the call to execle().  envinit is declared here,
390 * but populated after the call to getpwnam().
391 */
392static char	*homedir;	/* "HOME=" */
393static char	*shell;		/* "SHELL=" */
394static char	*username;	/* "USER=" */
395static char	*tz;		/* "TZ=" */
396
397static char	homestr[] = "HOME=";
398static char	shellstr[] = "SHELL=";
399static char	userstr[] = "USER=";
400static char	tzstr[] = "TZ=";
401
402static char	**envinit;
403#define	PAM_ENV_ELIM	16	/* allow 16 PAM environment variables */
404#define	USERNAME_LEN	16	/* maximum number of characters in user name */
405
406/*
407 *	See PSARC opinion 1992/025
408 */
409static char	userpath[] = "PATH=/usr/bin:";
410static char	rootpath[] = "PATH=/usr/sbin:/usr/bin";
411
412static char cmdbuf[NCARGS+1];
413static char hostname [MAXHOSTNAMELEN + 1];
414static char locuser[USERNAME_LEN + 1];
415static char remuser[USERNAME_LEN + 1];
416
417#define	KRB5_RECVAUTH_V5	5
418#define	SIZEOF_INADDR sizeof	(struct in_addr)
419
420#define	MAX_REPOSITORY_LEN	255
421static char repository[MAX_REPOSITORY_LEN];
422
423static char *kremuser;
424static krb5_principal client = NULL;
425
426static char	remote_addr[64];
427static char	local_addr[64];
428
429#define	_PATH_DEFAULT_LOGIN "/etc/default/login"
430
431static void
432doit(int f, struct sockaddr_storage *fromp, char **renvp)
433{
434	char *cp;
435
436	struct passwd *pwd;
437	char *path;
438	char *tzenv;
439	struct spwd *shpwd;
440	struct stat statb;
441	char **lenvp;
442
443	krb5_error_code status;
444	int valid_checksum;
445	int cnt;
446	int sin_len;
447	struct sockaddr_in localaddr;
448
449	int s;
450	in_port_t port;
451	pid_t pid;
452	int pv[2], pw[2], px[2], cc;
453	char buf[RSHD_BUFSIZ];
454	char sig;
455	int one = 1;
456	int v = 0;
457	int err = 0;
458	int idx = 0;
459	char **pam_env;
460	char abuf[INET6_ADDRSTRLEN];
461	struct sockaddr_in *sin;
462	struct sockaddr_in6 *sin6;
463	int fromplen;
464	int homedir_len, shell_len, username_len, tz_len;
465	int no_name;
466	boolean_t bad_port;
467	int netf = 0;
468
469	(void) signal(SIGINT, SIG_DFL);
470	(void) signal(SIGQUIT, SIG_DFL);
471	(void) signal(SIGTERM, SIG_DFL);
472	(void) signal(SIGXCPU, SIG_DFL);
473	(void) signal(SIGXFSZ, SIG_DFL);
474	(void) sigset(SIGCHLD, SIG_IGN);
475	(void) signal(SIGPIPE, SIG_DFL);
476	(void) signal(SIGHUP, SIG_DFL);
477
478#ifdef DEBUG
479	{
480	    int t = open("/dev/tty", 2);
481
482	    if (t >= 0) {
483		(void) setsid();
484		(void) close(t);
485	    }
486	}
487#endif
488	if (fromp->ss_family == AF_INET) {
489		sin = (struct sockaddr_in *)fromp;
490		port = ntohs((ushort_t)sin->sin_port);
491		fromplen = sizeof (struct sockaddr_in);
492	} else if (fromp->ss_family == AF_INET6) {
493		sin6 = (struct sockaddr_in6 *)fromp;
494		port = ntohs((ushort_t)sin6->sin6_port);
495		fromplen = sizeof (struct sockaddr_in6);
496	} else {
497		syslog(LOG_ERR, "wrong address family\n");
498		exit(1);
499	}
500
501	if (fromp->ss_family == AF_INET6) {
502		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
503			struct in_addr ipv4_addr;
504
505			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr);
506			(void) inet_ntop(AF_INET, &ipv4_addr, abuf,
507			    sizeof (abuf));
508		} else {
509			(void) inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
510			    sizeof (abuf));
511		}
512	} else if (fromp->ss_family == AF_INET) {
513		(void) inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof (abuf));
514	}
515
516	sin_len = sizeof (struct sockaddr_in);
517	if (getsockname(f, (struct sockaddr *)&localaddr, &sin_len) < 0) {
518		perror("getsockname");
519		exit(1);
520	}
521
522	netf = f;
523
524	bad_port = (port >= IPPORT_RESERVED ||
525		port < (uint_t)(IPPORT_RESERVED/2));
526
527	/* Get the name of the client side host to use later */
528	no_name = (getnameinfo((const struct sockaddr *) fromp, fromplen,
529		hostname, sizeof (hostname), NULL, 0, 0) != 0);
530
531	if (bad_port || no_name != 0) {
532		/*
533		 * If there is no host name available then use the
534		 * IP address to identify the host in the PAM call
535		 * below.  Do the same if a bad port was used, to
536		 * prevent untrustworthy authentication.
537		 */
538		(void) strlcpy(hostname, abuf, sizeof (hostname));
539	}
540
541	if (no_name != 0) {
542		/*
543		 * If the '-U' option was given on the cmd line,
544		 * we must be able to lookup the hostname
545		 */
546		if (resolve_hostname) {
547			syslog(LOG_ERR, "rshd: Couldn't resolve your "
548			    "address into a host name.\r\n Please "
549			    "contact your net administrator");
550			exit(1);
551		}
552	} else {
553		/*
554		 * Even if getnameinfo() succeeded, we still have to check
555		 * for spoofing.
556		 */
557		check_address("rshd", fromp, sin, sin6, abuf, hostname,
558		    sizeof (hostname));
559	}
560
561	if (!krb5auth_flag && bad_port) {
562		if (no_name)
563			syslog(LOG_NOTICE, "connection from %s - "
564			    "bad port\n", abuf);
565		else
566			syslog(LOG_NOTICE, "connection from %s (%s) - "
567			    "bad port\n", hostname, abuf);
568		exit(1);
569	}
570
571	(void) alarm(60);
572	port = 0;
573	for (;;) {
574		char c;
575		if ((cc = read(f, &c, 1)) != 1) {
576			if (cc < 0)
577				syslog(LOG_NOTICE, "read: %m");
578			(void) shutdown(f, 1+1);
579			exit(1);
580		}
581		if (c == 0)
582			break;
583		port = port * 10 + c - '0';
584	}
585	(void) alarm(0);
586	if (port != 0) {
587		int lport = 0;
588		struct sockaddr_storage ctl_addr;
589		int addrlen;
590
591		(void) memset(&ctl_addr, 0, sizeof (ctl_addr));
592		addrlen = sizeof (ctl_addr);
593		if (getsockname(f, (struct sockaddr *)&ctl_addr,
594			&addrlen) < 0) {
595			syslog(LOG_ERR, "getsockname: %m");
596			exit(1);
597		}
598get_port:
599		/*
600		 * 0 means that rresvport_addr() will bind to a port in
601		 * the anonymous priviledged port range.
602		 */
603		if (krb5auth_flag) {
604			/*
605			 * Kerberos does not support IPv6 yet.
606			 */
607			lport = IPPORT_RESERVED - 1;
608		}
609		s = rresvport_addr(&lport, &ctl_addr);
610
611		if (s < 0) {
612			syslog(LOG_ERR, "can't get stderr port: %m");
613			exit(1);
614		}
615		if (!krb5auth_flag && (port >= IPPORT_RESERVED)) {
616			syslog(LOG_ERR, "2nd port not reserved\n");
617			exit(1);
618		}
619		if (fromp->ss_family == AF_INET) {
620			sin->sin_port = htons((ushort_t)port);
621		} else if (fromp->ss_family == AF_INET6) {
622			sin6->sin6_port = htons((ushort_t)port);
623		}
624		if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) {
625			if (errno == EADDRINUSE) {
626				(void) close(s);
627				goto get_port;
628			}
629			syslog(LOG_INFO, "connect second port: %m");
630			exit(1);
631		}
632	}
633	(void) dup2(f, 0);
634	(void) dup2(f, 1);
635	(void) dup2(f, 2);
636
637#ifdef DEBUG
638	syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname);
639	if (debug_port)
640		syslog(LOG_NOTICE, "rshd: Debug port is %d", debug_port);
641	if (krb5auth_flag > 0)
642		syslog(LOG_NOTICE, "rshd: Kerberos mode is ON");
643	else
644		syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF");
645#endif /* DEBUG */
646
647	if (krb5auth_flag > 0) {
648		if ((status = recvauth(f, &valid_checksum))) {
649			syslog(LOG_ERR, gettext("Kerberos Authentication "
650					"failed \n"));
651			error("Authentication failed: %s\n",
652					error_message(status));
653			(void) audit_rshd_fail("Kerberos Authentication "
654				"failed", hostname, remuser, locuser, cmdbuf);
655			exit(1);
656		}
657
658		if (checksum_required && !valid_checksum &&
659			kcmd_protocol == KCMD_OLD_PROTOCOL) {
660			syslog(LOG_WARNING, "Client did not supply required"
661					" checksum--connection rejected.");
662			error("Client did not supply required"
663				"checksum--connection rejected.\n");
664			(void) audit_rshd_fail("Client did not supply required"
665				" checksum--connection rejected.", hostname,
666				remuser, locuser, cmdbuf);	/* BSM */
667			goto signout;
668		}
669
670		/*
671		 * Authentication has succeeded, we now need
672		 * to check authorization.
673		 *
674		 * krb5_kuserok returns 1 if OK.
675		 */
676		if (client && krb5_kuserok(bsd_context, client, locuser)) {
677			auth_sent |= AUTH_KRB5;
678		} else {
679			syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
680				"%s failed krb5_kuserok.\n",
681				kremuser, remuser, hostname, locuser);
682		}
683	} else {
684		getstr(netf, remuser, sizeof (remuser), "remuser");
685		getstr(netf, locuser, sizeof (locuser), "locuser");
686		getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
687	}
688
689#ifdef DEBUG
690	syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s",
691			locuser, remuser, cmdbuf);
692#endif /* DEBUG */
693
694	/*
695	 * Note that there is no rsh conv functions at present.
696	 */
697	if (krb5auth_flag > 0) {
698		if ((err = pam_start("krsh", locuser, NULL, &pamh))
699				!= PAM_SUCCESS) {
700			syslog(LOG_ERR, "pam_start() failed: %s\n",
701				pam_strerror(0, err));
702			exit(1);
703		}
704	}
705	else
706	{
707		if ((err = pam_start("rsh", locuser, NULL, &pamh))
708				!= PAM_SUCCESS) {
709			syslog(LOG_ERR, "pam_start() failed: %s\n",
710				pam_strerror(0, err));
711			exit(1);
712		}
713	}
714	if ((err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) {
715		syslog(LOG_ERR, "pam_set_item() failed: %s\n",
716			pam_strerror(pamh, err));
717		exit(1);
718	}
719	if ((err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS) {
720		syslog(LOG_ERR, "pam_set_item() failed: %s\n",
721			pam_strerror(pamh, err));
722		exit(1);
723	}
724
725	pwd = getpwnam(locuser);
726	shpwd = getspnam(locuser);
727	if ((pwd == NULL) || (shpwd == NULL)) {
728		if (krb5auth_flag > 0)
729			syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
730				"%s has no account.\n", kremuser, remuser,
731							hostname, locuser);
732		error("permission denied.\n");
733		(void) audit_rshd_fail("Login incorrect", hostname,
734			remuser, locuser, cmdbuf);	/* BSM */
735		exit(1);
736	}
737
738	if (krb5auth_flag > 0) {
739		(void) snprintf(repository, sizeof (repository),
740					KRB5_REPOSITORY_NAME);
741		/*
742		 * We currently only support special handling of the
743		 * KRB5 PAM repository
744		 */
745		if (strlen(locuser) != 0) {
746			krb5_repository_data_t krb5_data;
747			pam_repository_t pam_rep_data;
748
749			krb5_data.principal = locuser;
750			krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
751
752			pam_rep_data.type = repository;
753			pam_rep_data.scope = (void *)&krb5_data;
754			pam_rep_data.scope_len = sizeof (krb5_data);
755
756			(void) pam_set_item(pamh, PAM_REPOSITORY,
757					(void *)&pam_rep_data);
758		}
759	}
760
761	if (shpwd->sp_pwdp != 0) {
762		if (*shpwd->sp_pwdp != '\0') {
763			if ((v = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
764				error("permission denied\n");
765				(void) audit_rshd_fail("Permission denied",
766				    hostname, remuser, locuser, cmdbuf);
767				(void) pam_end(pamh, v);
768				exit(1);
769			}
770		} else {
771			int flags;
772			char *p;
773			/*
774			 * maintain 2.1 and 4.* and BSD semantics with
775			 * anonymous rshd unless PASSREQ is set to YES in
776			 * /etc/default/login: then we deny logins with empty
777			 * passwords.
778			 */
779			if (defopen(_PATH_DEFAULT_LOGIN) == 0) {
780				flags = defcntl(DC_GETFLAGS, 0);
781				TURNOFF(flags, DC_CASE);
782				(void) defcntl(DC_SETFLAGS, flags);
783
784				if ((p = defread("PASSREQ=")) != NULL &&
785				    strcasecmp(p, "YES") == 0) {
786					error("permission denied\n");
787					(void) audit_rshd_fail(
788					    "Permission denied", hostname,
789					    remuser, locuser, cmdbuf);
790					(void) pam_end(pamh, PAM_ABORT);
791					(void) defopen(NULL);
792					syslog(LOG_AUTH|LOG_NOTICE,
793					    "empty password not allowed for "
794					    "%s from %s.", locuser, hostname);
795					exit(1);
796				}
797				(void) defopen(NULL);
798			}
799			/*
800			 * /etc/default/login not found or PASSREQ not set
801			 * to YES. Allow logins without passwords.
802			 */
803		}
804	}
805
806	if (krb5auth_flag > 0) {
807		if (require_encrypt && (!do_encrypt)) {
808			error("You must use encryption.\n");
809			(void) audit_rshd_fail("You must use encryption.",
810				hostname, remuser, locuser, cmdbuf); /* BSM */
811			goto signout;
812		}
813
814		if (!(auth_ok & auth_sent)) {
815			if (auth_sent) {
816				error("Another authentication mechanism "
817				    "must be used to access this host.\n");
818				(void) audit_rshd_fail("Another authentication"
819					" mechanism must be used to access"
820					" this host.\n", hostname, remuser,
821					locuser, cmdbuf); /* BSM */
822				goto signout;
823			} else {
824				error("Permission denied.\n");
825				(void) audit_rshd_fail("Permission denied.",
826					hostname, remuser, locuser, cmdbuf);
827					/* BSM */
828				goto signout;
829			}
830		}
831
832
833		if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
834			error("Logins currently disabled.\n");
835			(void) audit_rshd_fail("Logins currently disabled.",
836				hostname, remuser, locuser, cmdbuf);
837			goto signout;
838		}
839
840		/* Log access to account */
841		if (pwd && (pwd->pw_uid == 0)) {
842			syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)"
843			    " as ROOT", cmdbuf,
844			    kremuser, remuser, hostname);
845		}
846	}
847
848	if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
849		switch (v) {
850		case PAM_NEW_AUTHTOK_REQD:
851			error("password expired\n");
852			(void) audit_rshd_fail("Password expired", hostname,
853				remuser, locuser, cmdbuf); /* BSM */
854			break;
855		case PAM_PERM_DENIED:
856			error("account expired\n");
857			(void) audit_rshd_fail("Account expired", hostname,
858				remuser, locuser, cmdbuf); /* BSM */
859			break;
860		case PAM_AUTHTOK_EXPIRED:
861			error("password expired\n");
862			(void) audit_rshd_fail("Password expired", hostname,
863				remuser, locuser, cmdbuf); /* BSM */
864			break;
865		default:
866			error("login incorrect\n");
867			(void) audit_rshd_fail("Permission denied", hostname,
868				remuser, locuser, cmdbuf); /* BSM */
869			break;
870		}
871		(void) pam_end(pamh, PAM_ABORT);
872		exit(1);
873	}
874
875	if (chdir(pwd->pw_dir) < 0) {
876		(void) chdir("/");
877#ifdef notdef
878		error("No remote directory.\n");
879
880		exit(1);
881#endif
882	}
883
884	/*
885	 * XXX There is no session management currently being done
886	 */
887
888	(void) write(STDERR_FILENO, "\0", 1);
889	if (port || do_encrypt) {
890		if ((pipe(pv) < 0)) {
891			error("Can't make pipe.\n");
892			(void) pam_end(pamh, PAM_ABORT);
893			exit(1);
894		}
895		if (do_encrypt) {
896			if (pipe(pw) < 0) {
897				error("Can't make pipe 2.\n");
898				(void) pam_end(pamh, PAM_ABORT);
899				exit(1);
900			}
901			if (pipe(px) < 0) {
902				error("Can't make pipe 3.\n");
903				(void) pam_end(pamh, PAM_ABORT);
904				exit(1);
905			}
906		}
907		pid = fork();
908		if (pid == (pid_t)-1)  {
909			error("Fork (to start shell) failed on server.  "
910				"Please try again later.\n");
911			(void) pam_end(pamh, PAM_ABORT);
912			exit(1);
913		}
914		if (pid) {
915			fd_set ready;
916			fd_set readfrom;
917
918			(void) close(STDIN_FILENO);
919			(void) close(STDOUT_FILENO);
920			(void) close(STDERR_FILENO);
921			(void) close(pv[1]);
922			if (do_encrypt) {
923				(void) close(pw[1]);
924				(void) close(px[0]);
925			} else {
926				(void) close(f);
927			}
928
929			(void) FD_ZERO(&readfrom);
930
931			FD_SET(pv[0], &readfrom);
932			if (do_encrypt) {
933				FD_SET(pw[0], &readfrom);
934				FD_SET(f, &readfrom);
935			}
936			if (port)
937				FD_SET(s, &readfrom);
938
939			/* read f (net), write to px[1] (child stdin) */
940			/* read pw[0] (child stdout), write to f (net) */
941			/* read s (alt. channel), signal child */
942			/* read pv[0] (child stderr), write to s */
943			if (ioctl(pv[0], FIONBIO, (char *)&one) == -1)
944				syslog(LOG_INFO, "ioctl FIONBIO: %m");
945			if (do_encrypt &&
946				ioctl(pw[0], FIONBIO, (char *)&one) == -1)
947				syslog(LOG_INFO, "ioctl FIONBIO: %m");
948			do {
949				ready = readfrom;
950				if (select(FD_SETSIZE, &ready, NULL,
951					NULL, NULL) < 0) {
952					if (errno == EINTR) {
953						continue;
954					} else {
955						break;
956					}
957				}
958				/*
959				 * Read from child stderr, write to net
960				 */
961				if (port && FD_ISSET(pv[0], &ready)) {
962					errno = 0;
963					cc = read(pv[0], buf, sizeof (buf));
964					if (cc <= 0) {
965						(void) shutdown(s, 2);
966						FD_CLR(pv[0], &readfrom);
967					} else {
968						(void) deswrite(s, buf, cc, 1);
969					}
970				}
971				/*
972				 * Read from alternate channel, signal child
973				 */
974				if (port && FD_ISSET(s, &ready)) {
975					if ((int)desread(s, &sig, 1, 1) <= 0)
976						FD_CLR(s, &readfrom);
977					else
978						(void) killpg(pid, sig);
979				}
980				/*
981				 * Read from child stdout, write to net
982				 */
983				if (do_encrypt && FD_ISSET(pw[0], &ready)) {
984					errno = 0;
985					cc = read(pw[0], buf, sizeof (buf));
986					if (cc <= 0) {
987						(void) shutdown(f, 2);
988						FD_CLR(pw[0], &readfrom);
989					} else {
990						(void) deswrite(f, buf, cc, 0);
991					}
992				}
993				/*
994				 * Read from the net, write to child stdin
995				 */
996				if (do_encrypt && FD_ISSET(f, &ready)) {
997					errno = 0;
998					cc = desread(f, buf, sizeof (buf), 0);
999					if (cc <= 0) {
1000						(void) close(px[1]);
1001						FD_CLR(f, &readfrom);
1002					} else {
1003						int wcc;
1004						wcc = write(px[1], buf, cc);
1005						if (wcc == -1) {
1006							/*
1007							 * pipe closed,
1008							 * don't read any
1009							 * more
1010							 *
1011							 * might check for
1012							 * EPIPE
1013							 */
1014						    (void) close(px[1]);
1015						    FD_CLR(f, &readfrom);
1016						} else if (wcc != cc) {
1017						    /* CSTYLED */
1018						    syslog(LOG_INFO, gettext("only wrote %d/%d to child"),
1019						    wcc, cc);
1020						}
1021					}
1022				}
1023			} while ((port && FD_ISSET(s, &readfrom)) ||
1024				(port && FD_ISSET(pv[0], &readfrom)) ||
1025				(do_encrypt && FD_ISSET(f, &readfrom)) ||
1026				(do_encrypt && FD_ISSET(pw[0], &readfrom)));
1027#ifdef DEBUG
1028			syslog(LOG_INFO, "Shell process completed.");
1029#endif /* DEBUG */
1030			if (ccache)
1031				(void) pam_close_session(pamh, 0);
1032			(void) pam_end(pamh, PAM_SUCCESS);
1033
1034			exit(0);
1035		} /* End of Parent block */
1036
1037		(void) setsid();	/* Should be the same as above. */
1038		(void) close(pv[0]);
1039		(void) dup2(pv[1], 2);
1040		(void) close(pv[1]);
1041		if (port)
1042			(void) close(s);
1043		if (do_encrypt) {
1044			(void) close(f);
1045			(void) close(pw[0]);
1046			(void) close(px[1]);
1047
1048			(void) dup2(px[0], 0);
1049			(void) dup2(pw[1], 1);
1050
1051			(void) close(px[0]);
1052			(void) close(pw[1]);
1053		}
1054	}
1055
1056	if (*pwd->pw_shell == '\0')
1057		pwd->pw_shell = "/bin/sh";
1058	if (!do_encrypt)
1059		(void) close(f);
1060	/*
1061	 * write audit record before making uid switch
1062	 */
1063	(void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */
1064
1065	/* set the real (and effective) GID */
1066	if (setgid(pwd->pw_gid) == -1) {
1067		error("Invalid gid.\n");
1068		(void) pam_end(pamh, PAM_ABORT);
1069		exit(1);
1070	}
1071
1072	/*
1073	 * Initialize the supplementary group access list.
1074	 */
1075	if (strlen(locuser) == 0) {
1076		error("No local user.\n");
1077		(void) pam_end(pamh, PAM_ABORT);
1078		exit(1);
1079	}
1080	if (initgroups(locuser, pwd->pw_gid) == -1) {
1081		error("Initgroup failed.\n");
1082		(void) pam_end(pamh, PAM_ABORT);
1083		exit(1);
1084	}
1085
1086	if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1087		error("Insufficient credentials.\n");
1088		(void) pam_end(pamh, v);
1089		exit(1);
1090	}
1091
1092	/* set the real (and effective) UID */
1093	if (setuid(pwd->pw_uid) == -1) {
1094		error("Invalid uid.\n");
1095		(void) pam_end(pamh, PAM_ABORT);
1096		exit(1);
1097	}
1098
1099	/* Change directory only after becoming the appropriate user. */
1100	if (chdir(pwd->pw_dir) < 0) {
1101		(void) chdir("/");
1102		if (krb5auth_flag > 0) {
1103			syslog(LOG_ERR, "Principal %s  (%s@%s) for local user"
1104				" %s has no home directory.",
1105				kremuser, remuser, hostname, locuser);
1106			error("No remote directory.\n");
1107			goto signout;
1108		}
1109#ifdef notdef
1110		error("No remote directory.\n");
1111		exit(1);
1112#endif
1113	}
1114
1115	path = (pwd->pw_uid == 0) ? rootpath : userpath;
1116
1117	/*
1118	 * Space for the following environment variables are dynamically
1119	 * allocated because their lengths are not known before calling
1120	 * getpwnam().
1121	 */
1122	homedir_len = strlen(pwd->pw_dir) + strlen(homestr) + 1;
1123	shell_len = strlen(pwd->pw_shell) + strlen(shellstr) + 1;
1124	username_len = strlen(pwd->pw_name) + strlen(userstr) + 1;
1125	homedir = (char *)malloc(homedir_len);
1126	shell = (char *)malloc(shell_len);
1127	username = (char *)malloc(username_len);
1128	if (homedir == NULL || shell == NULL || username == NULL) {
1129		perror("malloc");
1130		exit(1);
1131	}
1132	(void) snprintf(homedir, homedir_len, "%s%s", homestr, pwd->pw_dir);
1133	(void) snprintf(shell, shell_len, "%s%s", shellstr, pwd->pw_shell);
1134	(void) snprintf(username, username_len, "%s%s", userstr, pwd->pw_name);
1135
1136	/* Pass timezone to executed command. */
1137	if (tzenv = getenv("TZ")) {
1138		tz_len = strlen(tzenv) + strlen(tzstr) + 1;
1139		tz = malloc(tz_len);
1140		if (tz != NULL)
1141			(void) snprintf(tz, tz_len, "%s%s", tzstr, tzenv);
1142	}
1143
1144	add_to_envinit(homedir);
1145	add_to_envinit(shell);
1146	add_to_envinit(path);
1147	add_to_envinit(username);
1148	add_to_envinit(tz);
1149
1150	if (krb5auth_flag > 0) {
1151		int length;
1152		char *buffer;
1153
1154		/*
1155		 * If we have KRB5CCNAME set, then copy into the child's
1156		 * environment.  This can't really have a fixed position
1157		 * because `tz' may or may not be set.
1158		 */
1159		if (getenv("KRB5CCNAME")) {
1160			length = (int)strlen(getenv("KRB5CCNAME")) +
1161					(int)strlen("KRB5CCNAME=") + 1;
1162			buffer = (char *)malloc(length);
1163
1164			if (buffer) {
1165				(void) snprintf(buffer, length, "KRB5CCNAME=%s",
1166						getenv("KRB5CCNAME"));
1167				add_to_envinit(buffer);
1168			}
1169		} {
1170			/* These two are covered by ADDRPAD */
1171			length = strlen(inet_ntoa(localaddr.sin_addr)) + 1 +
1172					strlen("KRB5LOCALADDR=");
1173			(void) snprintf(local_addr, length, "KRB5LOCALADDR=%s",
1174				inet_ntoa(localaddr.sin_addr));
1175			add_to_envinit(local_addr);
1176
1177			length = strlen(inet_ntoa(sin->sin_addr)) + 1 +
1178					strlen("KRB5REMOTEADDR=");
1179			(void) snprintf(remote_addr, length,
1180				"KRB5REMOTEADDR=%s", inet_ntoa(sin->sin_addr));
1181			add_to_envinit(remote_addr);
1182		}
1183
1184		/*
1185		 * If we do anything else, make sure there is
1186		 * space in the array.
1187		 */
1188		for (cnt = 0; cnt < num_env; cnt++) {
1189			char *buf;
1190
1191			if (getenv(save_env[cnt])) {
1192				length = (int)strlen(getenv(save_env[cnt])) +
1193					(int)strlen(save_env[cnt]) + 2;
1194
1195				buf = (char *)malloc(length);
1196				if (buf) {
1197					(void) snprintf(buf, length, "%s=%s",
1198						save_env[cnt],
1199						getenv(save_env[cnt]));
1200					add_to_envinit(buf);
1201				}
1202			}
1203		}
1204
1205	}
1206
1207	/*
1208	 * add PAM environment variables set by modules
1209	 * -- only allowed 16 (PAM_ENV_ELIM)
1210	 * -- check to see if the environment variable is legal
1211	 */
1212	if ((pam_env = pam_getenvlist(pamh)) != 0) {
1213		while (pam_env[idx] != 0) {
1214			if (idx < PAM_ENV_ELIM &&
1215			    legalenvvar(pam_env[idx])) {
1216				add_to_envinit(pam_env[idx]);
1217			}
1218			idx++;
1219		}
1220	}
1221
1222	(void) pam_end(pamh, PAM_SUCCESS);
1223
1224	/*
1225	 * Pick up locale environment variables, if any.
1226	 */
1227	lenvp = renvp;
1228	while (*lenvp != NULL) {
1229		int	index;
1230
1231		for (index = 0; localeenv[index] != NULL; index++)
1232			/*
1233			 * locale_envmatch() returns 1 if
1234			 * *lenvp is localenev[index] and valid.
1235			 */
1236			if (locale_envmatch(localeenv[index], *lenvp)) {
1237				add_to_envinit(*lenvp);
1238				break;
1239			}
1240
1241		lenvp++;
1242	}
1243
1244	cp = strrchr(pwd->pw_shell, '/');
1245	if (cp != NULL)
1246		cp++;
1247	else
1248		cp = pwd->pw_shell;
1249	/*
1250	 * rdist has been moved to /usr/bin, so /usr/ucb/rdist might not
1251	 * be present on a system.  So if it doesn't exist we fall back
1252	 * and try for it in /usr/bin.  We take care to match the space
1253	 * after the name because the only purpose of this is to protect
1254	 * the internal call from old rdist's, not humans who type
1255	 * "rsh foo /usr/ucb/rdist".
1256	 */
1257#define	RDIST_PROG_NAME	"/usr/ucb/rdist -Server"
1258	if (strncmp(cmdbuf, RDIST_PROG_NAME, strlen(RDIST_PROG_NAME)) == 0) {
1259		if (stat("/usr/ucb/rdist", &statb) != 0) {
1260			(void) strncpy(cmdbuf + 5, "bin", 3);
1261		}
1262	}
1263
1264#ifdef DEBUG
1265	syslog(LOG_NOTICE, "rshd: cmdbuf = %s", cmdbuf);
1266	if (do_encrypt)
1267		syslog(LOG_NOTICE, "rshd: cmd to be exec'ed = %s",
1268			((char *)cmdbuf + 3));
1269#endif /* DEBUG */
1270
1271	if (do_encrypt && (strncmp(cmdbuf, "-x ", 3) == 0)) {
1272		(void) execle(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3,
1273				NULL, envinit);
1274	} else {
1275		(void) execle(pwd->pw_shell, cp, "-c", cmdbuf, NULL,
1276				envinit);
1277	}
1278
1279	perror(pwd->pw_shell);
1280	exit(1);
1281
1282signout:
1283	if (ccache)
1284		(void) pam_close_session(pamh, 0);
1285	ccache = NULL;
1286	(void) pam_end(pamh, PAM_ABORT);
1287	exit(1);
1288}
1289
1290static void
1291getstr(fd, buf, cnt, err)
1292	int fd;
1293	char *buf;
1294	int cnt;
1295	char *err;
1296{
1297	char c;
1298
1299	do {
1300		if (read(fd, &c, 1) != 1)
1301			exit(1);
1302		if (cnt-- == 0) {
1303			error("%s too long\n", err);
1304			exit(1);
1305		}
1306		*buf++ = c;
1307	} while (c != 0);
1308}
1309
1310/*PRINTFLIKE1*/
1311static void
1312error(char *fmt, ...)
1313{
1314	va_list ap;
1315	char buf[RSHD_BUFSIZ];
1316
1317	buf[0] = 1;
1318	va_start(ap, fmt);
1319	(void) vsnprintf(&buf[1], sizeof (buf) - 1, fmt, ap);
1320	va_end(ap);
1321	(void) write(STDERR_FILENO, buf, strlen(buf));
1322}
1323
1324static char *illegal[] = {
1325	"SHELL=",
1326	"HOME=",
1327	"LOGNAME=",
1328#ifndef NO_MAIL
1329	"MAIL=",
1330#endif
1331	"CDPATH=",
1332	"IFS=",
1333	"PATH=",
1334	"USER=",
1335	"TZ=",
1336	0
1337};
1338
1339/*
1340 * legalenvvar - can PAM modules insert this environmental variable?
1341 */
1342
1343static int
1344legalenvvar(char *s)
1345{
1346	register char **p;
1347
1348	for (p = illegal; *p; p++)
1349		if (strncmp(s, *p, strlen(*p)) == 0)
1350			return (0);
1351
1352	if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
1353		return (0);
1354
1355	return (1);
1356}
1357
1358/*
1359 * Add a string to the environment of the new process.
1360 */
1361
1362static void
1363add_to_envinit(char *string)
1364{
1365	/*
1366	 * Reserve space for 2 * 8 = 16 environment entries initially which
1367	 * should be enough to avoid reallocation of "envinit" in most cases.
1368	 */
1369	static int	size = 8;
1370	static int	index = 0;
1371
1372	if (string == NULL)
1373		return;
1374
1375	if ((envinit == NULL) || (index == size)) {
1376		size *= 2;
1377		envinit = realloc(envinit, (size + 1) * sizeof (char *));
1378		if (envinit == NULL) {
1379			perror("malloc");
1380			exit(1);
1381		}
1382	}
1383
1384	envinit[index++] = string;
1385	envinit[index] = NULL;
1386}
1387
1388/*
1389 * Check if lenv and penv matches or not.
1390 */
1391static int
1392locale_envmatch(char *lenv, char *penv)
1393{
1394	while ((*lenv == *penv) && (*lenv != '\0') && (*penv != '=')) {
1395		lenv++;
1396		penv++;
1397	}
1398
1399	/*
1400	 * '/' is eliminated for security reason.
1401	 */
1402	return ((*lenv == '\0' && *penv == '=' && *(penv + 1) != '/'));
1403}
1404
1405#ifndef	KRB_SENDAUTH_VLEN
1406#define	KRB_SENDAUTH_VLEN	8	/* length for version strings */
1407#endif
1408
1409/* MUST be KRB_SENDAUTH_VLEN chars */
1410#define	KRB_SENDAUTH_VERS	"AUTHV0.1"
1411#define	SIZEOF_INADDR sizeof (struct in_addr)
1412
1413static krb5_error_code
1414recvauth(int netf, int *valid_checksum)
1415{
1416	krb5_auth_context auth_context = NULL;
1417	krb5_error_code status;
1418	struct sockaddr_in laddr;
1419	int len;
1420	krb5_data inbuf;
1421	krb5_authenticator *authenticator;
1422	krb5_ticket *ticket;
1423	krb5_rcache rcache;
1424	krb5_data version;
1425	krb5_encrypt_block eblock;	/* eblock for encrypt/decrypt */
1426	krb5_data desinbuf;
1427	krb5_data desoutbuf;
1428	char des_inbuf[2 * RSHD_BUFSIZ];
1429			/* needs to be > largest read size */
1430	char des_outbuf[2 * RSHD_BUFSIZ + 4];
1431			/* needs to be > largest write size */
1432
1433	*valid_checksum = 0;
1434	len = sizeof (laddr);
1435
1436	if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
1437		exit(1);
1438	}
1439
1440	if (status = krb5_auth_con_init(bsd_context, &auth_context))
1441		return (status);
1442
1443	if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf,
1444		KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))
1445		return (status);
1446
1447	status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
1448	if (status)
1449		return (status);
1450
1451	if (!rcache) {
1452		krb5_principal server;
1453
1454		status = krb5_sname_to_principal(bsd_context, 0, 0,
1455			KRB5_NT_SRV_HST, &server);
1456		if (status)
1457			return (status);
1458
1459		status = krb5_get_server_rcache(bsd_context,
1460			krb5_princ_component(bsd_context, server, 0),
1461			&rcache);
1462		krb5_free_principal(bsd_context, server);
1463		if (status)
1464			return (status);
1465
1466		status = krb5_auth_con_setrcache(bsd_context, auth_context,
1467							rcache);
1468		if (status)
1469			return (status);
1470	}
1471
1472	status = krb5_recvauth_version(bsd_context, &auth_context, &netf,
1473		NULL,		/* Specify daemon principal */
1474		0,		/* no flags */
1475		keytab,		/* normally NULL to use v5srvtab */
1476		&ticket,	/* return ticket */
1477		&version);	/* application version string */
1478
1479
1480	if (status) {
1481		getstr(netf, locuser, sizeof (locuser), "locuser");
1482		getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
1483		getstr(netf, remuser, sizeof (locuser), "remuser");
1484		return (status);
1485	}
1486	getstr(netf, locuser, sizeof (locuser), "locuser");
1487	getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
1488
1489	/* Must be V5  */
1490
1491	kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
1492	if (version.length != 9 || version.data == NULL) {
1493		syslog(LOG_ERR, "bad application version length");
1494		error(gettext("bad application version length\n"));
1495		exit(1);
1496	}
1497	if (strncmp(version.data, "KCMDV0.1", 9) == 0) {
1498		kcmd_protocol = KCMD_OLD_PROTOCOL;
1499	} else if (strncmp(version.data, "KCMDV0.2", 9) == 0) {
1500		kcmd_protocol = KCMD_NEW_PROTOCOL;
1501	} else {
1502		syslog(LOG_ERR, "Unrecognized KCMD protocol (%s)",
1503			(char *)version.data);
1504		error(gettext("Unrecognized KCMD protocol (%s)"),
1505			(char *)version.data);
1506		exit(1);
1507	}
1508	getstr(netf, remuser, sizeof (locuser), "remuser");
1509
1510	if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client,
1511			&kremuser)))
1512		return (status);
1513
1514	if ((status = krb5_copy_principal(bsd_context,
1515				ticket->enc_part2->client, &client)))
1516		return (status);
1517
1518
1519	if (checksum_required && (kcmd_protocol == KCMD_OLD_PROTOCOL)) {
1520		if ((status = krb5_auth_con_getauthenticator(bsd_context,
1521			auth_context, &authenticator)))
1522			return (status);
1523
1524		if (authenticator->checksum && checksum_required) {
1525			struct sockaddr_in adr;
1526			int adr_length = sizeof (adr);
1527			int chksumsize = strlen(cmdbuf) + strlen(locuser) + 32;
1528			krb5_data input;
1529			krb5_keyblock key;
1530
1531			char *chksumbuf = (char *)malloc(chksumsize);
1532
1533			if (chksumbuf == 0)
1534				goto error_cleanup;
1535			if (getsockname(netf, (struct sockaddr *)&adr,
1536					&adr_length) != 0)
1537				goto error_cleanup;
1538
1539			(void) snprintf(chksumbuf, chksumsize, "%u:",
1540					ntohs(adr.sin_port));
1541			if (strlcat(chksumbuf, cmdbuf,
1542					chksumsize) >= chksumsize) {
1543				syslog(LOG_ERR, "cmd buffer too long.");
1544				free(chksumbuf);
1545				return (-1);
1546			}
1547			if (strlcat(chksumbuf, locuser,
1548					chksumsize) >= chksumsize) {
1549				syslog(LOG_ERR, "locuser too long.");
1550				free(chksumbuf);
1551				return (-1);
1552			}
1553
1554			input.data = chksumbuf;
1555			input.length = strlen(chksumbuf);
1556			key.magic = ticket->enc_part2->session->magic;
1557			key.enctype = ticket->enc_part2->session->enctype;
1558			key.contents = ticket->enc_part2->session->contents;
1559			key.length = ticket->enc_part2->session->length;
1560
1561			status = krb5_c_verify_checksum(bsd_context,
1562			    &key, 0, &input, authenticator->checksum,
1563			    (unsigned int *)valid_checksum);
1564
1565			if (status == 0 && *valid_checksum == 0)
1566			    status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1567error_cleanup:
1568			if (chksumbuf)
1569				krb5_xfree(chksumbuf);
1570			if (status) {
1571				krb5_free_authenticator(bsd_context,
1572						authenticator);
1573				return (status);
1574			}
1575		}
1576		krb5_free_authenticator(bsd_context, authenticator);
1577	}
1578
1579
1580	if ((strncmp(cmdbuf, "-x ", 3) == 0)) {
1581		if (krb5_privacy_allowed()) {
1582			do_encrypt = 1;
1583		} else {
1584			syslog(LOG_ERR, "rshd: Encryption not supported");
1585			error("rshd: Encryption not supported. \n");
1586			exit(2);
1587		}
1588
1589		status = krb5_auth_con_getremotesubkey(bsd_context,
1590						    auth_context,
1591						    &sessionkey);
1592		if (status) {
1593			syslog(LOG_ERR, "Error getting KRB5 session subkey");
1594			error(gettext("Error getting KRB5 session subkey"));
1595			exit(1);
1596		}
1597		/*
1598		 * The "new" protocol requires that a subkey be sent.
1599		 */
1600		if (sessionkey == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) {
1601			syslog(LOG_ERR, "No KRB5 session subkey sent");
1602			error(gettext("No KRB5 session subkey sent"));
1603			exit(1);
1604		}
1605		/*
1606		 * The "old" protocol does not permit an authenticator subkey.
1607		 * The key is taken from the ticket instead (see below).
1608		 */
1609		if (sessionkey != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) {
1610			syslog(LOG_ERR, "KRB5 session subkey not permitted "
1611				"with old KCMD protocol");
1612			error(gettext("KRB5 session subkey not permitted "
1613				"with old KCMD protocol"));
1614			exit(1);
1615		}
1616		/*
1617		 * If no key at this point, use the session key from
1618		 * the ticket.
1619		 */
1620		if (sessionkey == NULL) {
1621			/*
1622			 * Save the session key so we can configure the crypto
1623			 * module later.
1624			 */
1625			status = krb5_copy_keyblock(bsd_context,
1626						ticket->enc_part2->session,
1627						&sessionkey);
1628			if (status) {
1629				syslog(LOG_ERR, "krb5_copy_keyblock failed");
1630				error(gettext("krb5_copy_keyblock failed"));
1631				exit(1);
1632			}
1633		}
1634		/*
1635		 * If session key still cannot be found, we must
1636		 * exit because encryption is required here
1637		 * when encr_flag (-x) is set.
1638		 */
1639		if (sessionkey == NULL) {
1640			syslog(LOG_ERR, "Could not find an encryption key");
1641			error(gettext("Could not find an encryption key"));
1642			exit(1);
1643		}
1644
1645		/*
1646		 * Initialize parameters/buffers for desread & deswrite here.
1647		 */
1648		desinbuf.data = des_inbuf;
1649		desoutbuf.data = des_outbuf;
1650		desinbuf.length = sizeof (des_inbuf);
1651		desoutbuf.length = sizeof (des_outbuf);
1652
1653		eblock.crypto_entry = sessionkey->enctype;
1654		eblock.key = (krb5_keyblock *)sessionkey;
1655
1656		init_encrypt(do_encrypt, bsd_context, kcmd_protocol,
1657				&desinbuf, &desoutbuf, SERVER, &eblock);
1658	}
1659
1660	ticket->enc_part2->session = 0;
1661
1662	if ((status = krb5_read_message(bsd_context, (krb5_pointer) & netf,
1663				&inbuf))) {
1664		error(gettext("Error reading message: %s\n"),
1665				error_message(status));
1666		exit(1);
1667	}
1668
1669	if (inbuf.length) {
1670		krb5_creds **creds = NULL;
1671
1672		/* Forwarding being done, read creds */
1673		if ((status = krb5_rd_cred(bsd_context,
1674					    auth_context, &inbuf, &creds,
1675					    NULL))) {
1676			error("Can't get forwarded credentials: %s\n",
1677				error_message(status));
1678			exit(1);
1679		}
1680
1681		/* Store the forwarded creds in the ccache */
1682		if ((status = store_forw_creds(bsd_context,
1683					    creds, ticket, locuser,
1684					    &ccache))) {
1685			error("Can't store forwarded credentials: %s\n",
1686				error_message(status));
1687			exit(1);
1688		}
1689		krb5_free_creds(bsd_context, *creds);
1690	}
1691
1692	krb5_free_ticket(bsd_context, ticket);
1693	return (0);
1694}
1695
1696static void
1697usage(void)
1698{
1699	(void) fprintf(stderr, gettext("%s: rshd [-k5eciU] "
1700			"[-P path] [-M realm] [-s tos] "
1701#ifdef DEBUG
1702			"[-D port] "
1703#endif /* DEBUG */
1704			"[-S keytab]"), gettext("usage"));
1705
1706	syslog(LOG_ERR, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] "
1707#ifdef DEBUG
1708			"[-D port] "
1709#endif /* DEBUG */
1710			"[-S keytab]", gettext("usage"));
1711}
1712