Home | History | Annotate | Download | only in usr.sbin
      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 2008 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 
     92 static void error(char *, ...);
     93 static void doit(int, struct sockaddr_storage *, char **);
     94 static void getstr(int, char *, int, char *);
     95 
     96 static int legalenvvar(char *);
     97 static void add_to_envinit(char *);
     98 static int locale_envmatch(char *, char *);
     99 
    100 /* Function decls. for functions not in any header file.  (Grrrr.) */
    101 extern int audit_rshd_setup(void);
    102 extern int audit_rshd_success(char *, char *, char *, char *);
    103 extern int audit_rshd_fail(char *, char *, char *, char *, char *);
    104 extern int audit_settid(int);
    105 
    106 static int do_encrypt = 0;
    107 static 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 
    135 static krb5_context bsd_context;
    136 static krb5_keytab keytab = NULL;
    137 static krb5_ccache ccache = NULL;
    138 static krb5_keyblock *sessionkey = NULL;
    139 
    140 static int require_encrypt = 0;
    141 static int resolve_hostname = 0;
    142 static int krb5auth_flag = 0;	/* Flag set, when KERBEROS is enabled */
    143 static enum kcmd_proto kcmd_protocol;
    144 
    145 #ifdef DEBUG
    146 static 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)
    160 static int auth_ok = 0;
    161 static int auth_sent = 0;
    162 static int checksum_required = 0;
    163 static 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
    171 static char *save_env[MAXENV];
    172 static int num_env = 0;
    173 
    174 static void usage(void);
    175 static krb5_error_code recvauth(int, int *);
    176 
    177 /*ARGSUSED*/
    178 int
    179 main(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 			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  */
    382 static 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  */
    392 static char	*homedir;	/* "HOME=" */
    393 static char	*shell;		/* "SHELL=" */
    394 static char	*username;	/* "USER=" */
    395 static char	*tz;		/* "TZ=" */
    396 
    397 static char	homestr[] = "HOME=";
    398 static char	shellstr[] = "SHELL=";
    399 static char	userstr[] = "USER=";
    400 static char	tzstr[] = "TZ=";
    401 
    402 static 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  */
    409 static char	userpath[] = "PATH=/usr/bin:";
    410 static char	rootpath[] = "PATH=/usr/sbin:/usr/bin";
    411 
    412 static char cmdbuf[NCARGS+1];
    413 static char hostname [MAXHOSTNAMELEN + 1];
    414 static char locuser[USERNAME_LEN + 1];
    415 static 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
    421 static char repository[MAX_REPOSITORY_LEN];
    422 
    423 static char *kremuser;
    424 static krb5_principal client = NULL;
    425 
    426 static char	remote_addr[64];
    427 static char	local_addr[64];
    428 
    429 #define	_PATH_DEFAULT_LOGIN "/etc/default/login"
    430 
    431 static void
    432 doit(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 	{ int t = open("/dev/tty", 2);
    480 	    if (t >= 0) {
    481 		(void) setsid();
    482 		(void) close(t);
    483 	    }
    484 	}
    485 #endif
    486 	if (fromp->ss_family == AF_INET) {
    487 		sin = (struct sockaddr_in *)fromp;
    488 		port = ntohs((ushort_t)sin->sin_port);
    489 		fromplen = sizeof (struct sockaddr_in);
    490 	} else if (fromp->ss_family == AF_INET6) {
    491 		sin6 = (struct sockaddr_in6 *)fromp;
    492 		port = ntohs((ushort_t)sin6->sin6_port);
    493 		fromplen = sizeof (struct sockaddr_in6);
    494 	} else {
    495 		syslog(LOG_ERR, "wrong address family\n");
    496 		exit(1);
    497 	}
    498 
    499 	if (fromp->ss_family == AF_INET6) {
    500 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    501 			struct in_addr ipv4_addr;
    502 
    503 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr);
    504 			(void) inet_ntop(AF_INET, &ipv4_addr, abuf,
    505 			    sizeof (abuf));
    506 		} else {
    507 			(void) inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
    508 			    sizeof (abuf));
    509 		}
    510 	} else if (fromp->ss_family == AF_INET) {
    511 		(void) inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof (abuf));
    512 	}
    513 
    514 	sin_len = sizeof (struct sockaddr_in);
    515 	if (getsockname(f, (struct sockaddr *)&localaddr, &sin_len) < 0) {
    516 		perror("getsockname");
    517 		exit(1);
    518 	}
    519 
    520 	netf = f;
    521 
    522 	bad_port = (port >= IPPORT_RESERVED ||
    523 		port < (uint_t)(IPPORT_RESERVED/2));
    524 
    525 	/* Get the name of the client side host to use later */
    526 	no_name = (getnameinfo((const struct sockaddr *) fromp, fromplen,
    527 		hostname, sizeof (hostname), NULL, 0, 0) != 0);
    528 
    529 	if (bad_port || no_name != 0) {
    530 		/*
    531 		 * If there is no host name available then use the
    532 		 * IP address to identify the host in the PAM call
    533 		 * below.  Do the same if a bad port was used, to
    534 		 * prevent untrustworthy authentication.
    535 		 */
    536 		(void) strlcpy(hostname, abuf, sizeof (hostname));
    537 	}
    538 
    539 	if (no_name != 0) {
    540 		/*
    541 		 * If the '-U' option was given on the cmd line,
    542 		 * we must be able to lookup the hostname
    543 		 */
    544 		if (resolve_hostname) {
    545 			syslog(LOG_ERR, "rshd: Couldn't resolve your "
    546 			    "address into a host name.\r\n Please "
    547 			    "contact your net administrator");
    548 			exit(1);
    549 		}
    550 	} else {
    551 		/*
    552 		 * Even if getnameinfo() succeeded, we still have to check
    553 		 * for spoofing.
    554 		 */
    555 		check_address("rshd", fromp, sin, sin6, abuf, hostname,
    556 		    sizeof (hostname));
    557 	}
    558 
    559 	if (!krb5auth_flag && bad_port) {
    560 		if (no_name)
    561 			syslog(LOG_NOTICE, "connection from %s - "
    562 			    "bad port\n", abuf);
    563 		else
    564 			syslog(LOG_NOTICE, "connection from %s (%s) - "
    565 			    "bad port\n", hostname, abuf);
    566 		exit(1);
    567 	}
    568 
    569 	(void) alarm(60);
    570 	port = 0;
    571 	for (;;) {
    572 		char c;
    573 		if ((cc = read(f, &c, 1)) != 1) {
    574 			if (cc < 0)
    575 				syslog(LOG_NOTICE, "read: %m");
    576 			(void) shutdown(f, 1+1);
    577 			exit(1);
    578 		}
    579 		if (c == 0)
    580 			break;
    581 		port = port * 10 + c - '0';
    582 	}
    583 	(void) alarm(0);
    584 	if (port != 0) {
    585 		int lport = 0;
    586 		struct sockaddr_storage ctl_addr;
    587 		int addrlen;
    588 
    589 		(void) memset(&ctl_addr, 0, sizeof (ctl_addr));
    590 		addrlen = sizeof (ctl_addr);
    591 		if (getsockname(f, (struct sockaddr *)&ctl_addr,
    592 			&addrlen) < 0) {
    593 			syslog(LOG_ERR, "getsockname: %m");
    594 			exit(1);
    595 		}
    596 get_port:
    597 		/*
    598 		 * 0 means that rresvport_addr() will bind to a port in
    599 		 * the anonymous priviledged port range.
    600 		 */
    601 		if (krb5auth_flag) {
    602 			/*
    603 			 * Kerberos does not support IPv6 yet.
    604 			 */
    605 			lport = IPPORT_RESERVED - 1;
    606 		}
    607 		s = rresvport_addr(&lport, &ctl_addr);
    608 
    609 		if (s < 0) {
    610 			syslog(LOG_ERR, "can't get stderr port: %m");
    611 			exit(1);
    612 		}
    613 		if (!krb5auth_flag && (port >= IPPORT_RESERVED)) {
    614 			syslog(LOG_ERR, "2nd port not reserved\n");
    615 			exit(1);
    616 		}
    617 		if (fromp->ss_family == AF_INET) {
    618 			sin->sin_port = htons((ushort_t)port);
    619 		} else if (fromp->ss_family == AF_INET6) {
    620 			sin6->sin6_port = htons((ushort_t)port);
    621 		}
    622 		if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) {
    623 			if (errno == EADDRINUSE) {
    624 				(void) close(s);
    625 				goto get_port;
    626 			}
    627 			syslog(LOG_INFO, "connect second port: %m");
    628 			exit(1);
    629 		}
    630 	}
    631 	(void) dup2(f, 0);
    632 	(void) dup2(f, 1);
    633 	(void) dup2(f, 2);
    634 
    635 #ifdef DEBUG
    636 	syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname);
    637 	if (debug_port)
    638 		syslog(LOG_NOTICE, "rshd: Debug port is %d", debug_port);
    639 	if (krb5auth_flag > 0)
    640 		syslog(LOG_NOTICE, "rshd: Kerberos mode is ON");
    641 	else
    642 		syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF");
    643 #endif /* DEBUG */
    644 
    645 	if (krb5auth_flag > 0) {
    646 		if ((status = recvauth(f, &valid_checksum))) {
    647 			syslog(LOG_ERR, gettext("Kerberos Authentication "
    648 					"failed \n"));
    649 			error("Authentication failed: %s\n",
    650 					error_message(status));
    651 			(void) audit_rshd_fail("Kerberos Authentication "
    652 				"failed", hostname, remuser, locuser, cmdbuf);
    653 			exit(1);
    654 		}
    655 
    656 		if (checksum_required && !valid_checksum &&
    657 			kcmd_protocol == KCMD_OLD_PROTOCOL) {
    658 			syslog(LOG_WARNING, "Client did not supply required"
    659 					" checksum--connection rejected.");
    660 			error("Client did not supply required"
    661 				"checksum--connection rejected.\n");
    662 			(void) audit_rshd_fail("Client did not supply required"
    663 				" checksum--connection rejected.", hostname,
    664 				remuser, locuser, cmdbuf);	/* BSM */
    665 			goto signout;
    666 		}
    667 
    668 		/*
    669 		 * Authentication has succeeded, we now need
    670 		 * to check authorization.
    671 		 *
    672 		 * krb5_kuserok returns 1 if OK.
    673 		 */
    674 		if (client && krb5_kuserok(bsd_context, client, locuser)) {
    675 			auth_sent |= AUTH_KRB5;
    676 		} else {
    677 			syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
    678 				"%s failed krb5_kuserok.\n",
    679 				kremuser, remuser, hostname, locuser);
    680 		}
    681 	} else {
    682 		getstr(netf, remuser, sizeof (remuser), "remuser");
    683 		getstr(netf, locuser, sizeof (locuser), "locuser");
    684 		getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
    685 	}
    686 
    687 #ifdef DEBUG
    688 	syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s",
    689 			locuser, remuser, cmdbuf);
    690 #endif /* DEBUG */
    691 
    692 	/*
    693 	 * Note that there is no rsh conv functions at present.
    694 	 */
    695 	if (krb5auth_flag > 0) {
    696 		if ((err = pam_start("krsh", locuser, NULL, &pamh))
    697 				!= PAM_SUCCESS) {
    698 			syslog(LOG_ERR, "pam_start() failed: %s\n",
    699 				pam_strerror(0, err));
    700 			exit(1);
    701 		}
    702 	}
    703 	else
    704 	{
    705 		if ((err = pam_start("rsh", locuser, NULL, &pamh))
    706 				!= PAM_SUCCESS) {
    707 			syslog(LOG_ERR, "pam_start() failed: %s\n",
    708 				pam_strerror(0, err));
    709 			exit(1);
    710 		}
    711 	}
    712 	if ((err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) {
    713 		syslog(LOG_ERR, "pam_set_item() failed: %s\n",
    714 			pam_strerror(pamh, err));
    715 		exit(1);
    716 	}
    717 	if ((err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS) {
    718 		syslog(LOG_ERR, "pam_set_item() failed: %s\n",
    719 			pam_strerror(pamh, err));
    720 		exit(1);
    721 	}
    722 
    723 	pwd = getpwnam(locuser);
    724 	shpwd = getspnam(locuser);
    725 	if ((pwd == NULL) || (shpwd == NULL)) {
    726 		if (krb5auth_flag > 0)
    727 			syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
    728 				"%s has no account.\n", kremuser, remuser,
    729 							hostname, locuser);
    730 		error("permission denied.\n");
    731 		(void) audit_rshd_fail("Login incorrect", hostname,
    732 			remuser, locuser, cmdbuf);	/* BSM */
    733 		exit(1);
    734 	}
    735 
    736 	if (krb5auth_flag > 0) {
    737 		(void) snprintf(repository, sizeof (repository),
    738 					KRB5_REPOSITORY_NAME);
    739 		/*
    740 		 * We currently only support special handling of the
    741 		 * KRB5 PAM repository
    742 		 */
    743 		if (strlen(locuser) != 0) {
    744 			krb5_repository_data_t krb5_data;
    745 			pam_repository_t pam_rep_data;
    746 
    747 			krb5_data.principal = locuser;
    748 			krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
    749 
    750 			pam_rep_data.type = repository;
    751 			pam_rep_data.scope = (void *)&krb5_data;
    752 			pam_rep_data.scope_len = sizeof (krb5_data);
    753 
    754 			(void) pam_set_item(pamh, PAM_REPOSITORY,
    755 					(void *)&pam_rep_data);
    756 		}
    757 	}
    758 
    759 	if (shpwd->sp_pwdp != 0) {
    760 		if (*shpwd->sp_pwdp != '\0') {
    761 			if ((v = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
    762 				error("permission denied\n");
    763 				(void) audit_rshd_fail("Permission denied",
    764 				    hostname, remuser, locuser, cmdbuf);
    765 				(void) pam_end(pamh, v);
    766 				exit(1);
    767 			}
    768 		} else {
    769 			int flags;
    770 			char *p;
    771 			/*
    772 			 * maintain 2.1 and 4.* and BSD semantics with
    773 			 * anonymous rshd unless PASSREQ is set to YES in
    774 			 * /etc/default/login: then we deny logins with empty
    775 			 * passwords.
    776 			 */
    777 			if (defopen(_PATH_DEFAULT_LOGIN) == 0) {
    778 				flags = defcntl(DC_GETFLAGS, 0);
    779 				TURNOFF(flags, DC_CASE);
    780 				(void) defcntl(DC_SETFLAGS, flags);
    781 
    782 				if ((p = defread("PASSREQ=")) != NULL &&
    783 				    strcasecmp(p, "YES") == 0) {
    784 					error("permission denied\n");
    785 					(void) audit_rshd_fail(
    786 					    "Permission denied", hostname,
    787 					    remuser, locuser, cmdbuf);
    788 					(void) pam_end(pamh, PAM_ABORT);
    789 					(void) defopen(NULL);
    790 					syslog(LOG_AUTH|LOG_NOTICE,
    791 					    "empty password not allowed for "
    792 					    "%s from %s.", locuser, hostname);
    793 					exit(1);
    794 				}
    795 				(void) defopen(NULL);
    796 			}
    797 			/*
    798 			 * /etc/default/login not found or PASSREQ not set
    799 			 * to YES. Allow logins without passwords.
    800 			 */
    801 		}
    802 	}
    803 
    804 	if (krb5auth_flag > 0) {
    805 		if (require_encrypt && (!do_encrypt)) {
    806 			error("You must use encryption.\n");
    807 			(void) audit_rshd_fail("You must use encryption.",
    808 				hostname, remuser, locuser, cmdbuf); /* BSM */
    809 			goto signout;
    810 		}
    811 
    812 		if (!(auth_ok & auth_sent)) {
    813 			if (auth_sent) {
    814 				error("Another authentication mechanism "
    815 				    "must be used to access this host.\n");
    816 				(void) audit_rshd_fail("Another authentication"
    817 					" mechanism must be used to access"
    818 					" this host.\n", hostname, remuser,
    819 					locuser, cmdbuf); /* BSM */
    820 				goto signout;
    821 			} else {
    822 				error("Permission denied.\n");
    823 				(void) audit_rshd_fail("Permission denied.",
    824 					hostname, remuser, locuser, cmdbuf);
    825 					/* BSM */
    826 				goto signout;
    827 			}
    828 		}
    829 
    830 
    831 		if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
    832 			error("Logins currently disabled.\n");
    833 			(void) audit_rshd_fail("Logins currently disabled.",
    834 				hostname, remuser, locuser, cmdbuf);
    835 			goto signout;
    836 		}
    837 
    838 		/* Log access to account */
    839 		if (pwd && (pwd->pw_uid == 0)) {
    840 			syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)"
    841 			    " as ROOT", cmdbuf,
    842 			    kremuser, remuser, hostname);
    843 		}
    844 	}
    845 
    846 	if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
    847 		switch (v) {
    848 		case PAM_NEW_AUTHTOK_REQD:
    849 			error("password expired\n");
    850 			(void) audit_rshd_fail("Password expired", hostname,
    851 				remuser, locuser, cmdbuf); /* BSM */
    852 			break;
    853 		case PAM_PERM_DENIED:
    854 			error("account expired\n");
    855 			(void) audit_rshd_fail("Account expired", hostname,
    856 				remuser, locuser, cmdbuf); /* BSM */
    857 			break;
    858 		case PAM_AUTHTOK_EXPIRED:
    859 			error("password expired\n");
    860 			(void) audit_rshd_fail("Password expired", hostname,
    861 				remuser, locuser, cmdbuf); /* BSM */
    862 			break;
    863 		default:
    864 			error("login incorrect\n");
    865 			(void) audit_rshd_fail("Permission denied", hostname,
    866 				remuser, locuser, cmdbuf); /* BSM */
    867 			break;
    868 		}
    869 		(void) pam_end(pamh, PAM_ABORT);
    870 		exit(1);
    871 	}
    872 
    873 	if (chdir(pwd->pw_dir) < 0) {
    874 		(void) chdir("/");
    875 #ifdef notdef
    876 		error("No remote directory.\n");
    877 
    878 		exit(1);
    879 #endif
    880 	}
    881 
    882 	/*
    883 	 * XXX There is no session management currently being done
    884 	 */
    885 
    886 	(void) write(STDERR_FILENO, "\0", 1);
    887 	if (port || do_encrypt) {
    888 		if ((pipe(pv) < 0)) {
    889 			error("Can't make pipe.\n");
    890 			(void) pam_end(pamh, PAM_ABORT);
    891 			exit(1);
    892 		}
    893 		if (do_encrypt) {
    894 			if (pipe(pw) < 0) {
    895 				error("Can't make pipe 2.\n");
    896 				(void) pam_end(pamh, PAM_ABORT);
    897 				exit(1);
    898 			}
    899 			if (pipe(px) < 0) {
    900 				error("Can't make pipe 3.\n");
    901 				(void) pam_end(pamh, PAM_ABORT);
    902 				exit(1);
    903 			}
    904 		}
    905 		pid = fork();
    906 		if (pid == (pid_t)-1)  {
    907 			error("Fork (to start shell) failed on server.  "
    908 				"Please try again later.\n");
    909 			(void) pam_end(pamh, PAM_ABORT);
    910 			exit(1);
    911 		}
    912 		if (pid) {
    913 			fd_set ready;
    914 			fd_set readfrom;
    915 
    916 			(void) close(STDIN_FILENO);
    917 			(void) close(STDOUT_FILENO);
    918 			(void) close(STDERR_FILENO);
    919 			(void) close(pv[1]);
    920 			if (do_encrypt) {
    921 				(void) close(pw[1]);
    922 				(void) close(px[0]);
    923 			} else {
    924 				(void) close(f);
    925 			}
    926 
    927 			(void) FD_ZERO(&readfrom);
    928 
    929 			FD_SET(pv[0], &readfrom);
    930 			if (do_encrypt) {
    931 				FD_SET(pw[0], &readfrom);
    932 				FD_SET(f, &readfrom);
    933 			}
    934 			if (port)
    935 				FD_SET(s, &readfrom);
    936 
    937 			/* read f (net), write to px[1] (child stdin) */
    938 			/* read pw[0] (child stdout), write to f (net) */
    939 			/* read s (alt. channel), signal child */
    940 			/* read pv[0] (child stderr), write to s */
    941 			if (ioctl(pv[0], FIONBIO, (char *)&one) == -1)
    942 				syslog(LOG_INFO, "ioctl FIONBIO: %m");
    943 			if (do_encrypt &&
    944 				ioctl(pw[0], FIONBIO, (char *)&one) == -1)
    945 				syslog(LOG_INFO, "ioctl FIONBIO: %m");
    946 			do {
    947 				ready = readfrom;
    948 				if (select(FD_SETSIZE, &ready, NULL,
    949 					NULL, NULL) < 0) {
    950 					if (errno == EINTR) {
    951 						continue;
    952 					} else {
    953 						break;
    954 					}
    955 				}
    956 				/*
    957 				 * Read from child stderr, write to net
    958 				 */
    959 				if (port && FD_ISSET(pv[0], &ready)) {
    960 					errno = 0;
    961 					cc = read(pv[0], buf, sizeof (buf));
    962 					if (cc <= 0) {
    963 						(void) shutdown(s, 2);
    964 						FD_CLR(pv[0], &readfrom);
    965 					} else {
    966 						(void) deswrite(s, buf, cc, 1);
    967 					}
    968 				}
    969 				/*
    970 				 * Read from alternate channel, signal child
    971 				 */
    972 				if (port && FD_ISSET(s, &ready)) {
    973 					if ((int)desread(s, &sig, 1, 1) <= 0)
    974 						FD_CLR(s, &readfrom);
    975 					else
    976 						(void) killpg(pid, sig);
    977 				}
    978 				/*
    979 				 * Read from child stdout, write to net
    980 				 */
    981 				if (do_encrypt && FD_ISSET(pw[0], &ready)) {
    982 					errno = 0;
    983 					cc = read(pw[0], buf, sizeof (buf));
    984 					if (cc <= 0) {
    985 						(void) shutdown(f, 2);
    986 						FD_CLR(pw[0], &readfrom);
    987 					} else {
    988 						(void) deswrite(f, buf, cc, 0);
    989 					}
    990 				}
    991 				/*
    992 				 * Read from the net, write to child stdin
    993 				 */
    994 				if (do_encrypt && FD_ISSET(f, &ready)) {
    995 					errno = 0;
    996 					cc = desread(f, buf, sizeof (buf), 0);
    997 					if (cc <= 0) {
    998 						(void) close(px[1]);
    999 						FD_CLR(f, &readfrom);
   1000 					} else {
   1001 						int wcc;
   1002 						wcc = write(px[1], buf, cc);
   1003 						if (wcc == -1) {
   1004 							/*
   1005 							 * pipe closed,
   1006 							 * don't read any
   1007 							 * more
   1008 							 *
   1009 							 * might check for
   1010 							 * EPIPE
   1011 							 */
   1012 						    (void) close(px[1]);
   1013 						    FD_CLR(f, &readfrom);
   1014 						} else if (wcc != cc) {
   1015 						    /* CSTYLED */
   1016 						    syslog(LOG_INFO, gettext("only wrote %d/%d to child"),
   1017 						    wcc, cc);
   1018 						}
   1019 					}
   1020 				}
   1021 			} while ((port && FD_ISSET(s, &readfrom)) ||
   1022 				(port && FD_ISSET(pv[0], &readfrom)) ||
   1023 				(do_encrypt && FD_ISSET(f, &readfrom)) ||
   1024 				(do_encrypt && FD_ISSET(pw[0], &readfrom)));
   1025 #ifdef DEBUG
   1026 			syslog(LOG_INFO, "Shell process completed.");
   1027 #endif /* DEBUG */
   1028 			if (ccache)
   1029 				(void) pam_close_session(pamh, 0);
   1030 			(void) pam_end(pamh, PAM_SUCCESS);
   1031 
   1032 			exit(0);
   1033 		} /* End of Parent block */
   1034 
   1035 		(void) setsid();	/* Should be the same as above. */
   1036 		(void) close(pv[0]);
   1037 		(void) dup2(pv[1], 2);
   1038 		(void) close(pv[1]);
   1039 		if (port)
   1040 			(void) close(s);
   1041 		if (do_encrypt) {
   1042 			(void) close(f);
   1043 			(void) close(pw[0]);
   1044 			(void) close(px[1]);
   1045 
   1046 			(void) dup2(px[0], 0);
   1047 			(void) dup2(pw[1], 1);
   1048 
   1049 			(void) close(px[0]);
   1050 			(void) close(pw[1]);
   1051 		}
   1052 	}
   1053 
   1054 	if (*pwd->pw_shell == '\0')
   1055 		pwd->pw_shell = "/bin/sh";
   1056 	if (!do_encrypt)
   1057 		(void) close(f);
   1058 	/*
   1059 	 * write audit record before making uid switch
   1060 	 */
   1061 	(void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */
   1062 
   1063 	/* set the real (and effective) GID */
   1064 	if (setgid(pwd->pw_gid) == -1) {
   1065 		error("Invalid gid.\n");
   1066 		(void) pam_end(pamh, PAM_ABORT);
   1067 		exit(1);
   1068 	}
   1069 
   1070 	/*
   1071 	 * Initialize the supplementary group access list.
   1072 	 */
   1073 	if (strlen(locuser) == 0) {
   1074 		error("No local user.\n");
   1075 		(void) pam_end(pamh, PAM_ABORT);
   1076 		exit(1);
   1077 	}
   1078 	if (initgroups(locuser, pwd->pw_gid) == -1) {
   1079 		error("Initgroup failed.\n");
   1080 		(void) pam_end(pamh, PAM_ABORT);
   1081 		exit(1);
   1082 	}
   1083 
   1084 	if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
   1085 		error("Insufficient credentials.\n");
   1086 		(void) pam_end(pamh, v);
   1087 		exit(1);
   1088 	}
   1089 
   1090 	/* set the real (and effective) UID */
   1091 	if (setuid(pwd->pw_uid) == -1) {
   1092 		error("Invalid uid.\n");
   1093 		(void) pam_end(pamh, PAM_ABORT);
   1094 		exit(1);
   1095 	}
   1096 
   1097 	/* Change directory only after becoming the appropriate user. */
   1098 	if (chdir(pwd->pw_dir) < 0) {
   1099 		(void) chdir("/");
   1100 		if (krb5auth_flag > 0) {
   1101 			syslog(LOG_ERR, "Principal %s  (%s@%s) for local user"
   1102 				" %s has no home directory.",
   1103 				kremuser, remuser, hostname, locuser);
   1104 			error("No remote directory.\n");
   1105 			goto signout;
   1106 		}
   1107 #ifdef notdef
   1108 		error("No remote directory.\n");
   1109 		exit(1);
   1110 #endif
   1111 	}
   1112 
   1113 	path = (pwd->pw_uid == 0) ? rootpath : userpath;
   1114 
   1115 	/*
   1116 	 * Space for the following environment variables are dynamically
   1117 	 * allocated because their lengths are not known before calling
   1118 	 * getpwnam().
   1119 	 */
   1120 	homedir_len = strlen(pwd->pw_dir) + strlen(homestr) + 1;
   1121 	shell_len = strlen(pwd->pw_shell) + strlen(shellstr) + 1;
   1122 	username_len = strlen(pwd->pw_name) + strlen(userstr) + 1;
   1123 	homedir = (char *)malloc(homedir_len);
   1124 	shell = (char *)malloc(shell_len);
   1125 	username = (char *)malloc(username_len);
   1126 	if (homedir == NULL || shell == NULL || username == NULL) {
   1127 		perror("malloc");
   1128 		exit(1);
   1129 	}
   1130 	(void) snprintf(homedir, homedir_len, "%s%s", homestr, pwd->pw_dir);
   1131 	(void) snprintf(shell, shell_len, "%s%s", shellstr, pwd->pw_shell);
   1132 	(void) snprintf(username, username_len, "%s%s", userstr, pwd->pw_name);
   1133 
   1134 	/* Pass timezone to executed command. */
   1135 	if (tzenv = getenv("TZ")) {
   1136 		tz_len = strlen(tzenv) + strlen(tzstr) + 1;
   1137 		tz = malloc(tz_len);
   1138 		if (tz != NULL)
   1139 			(void) snprintf(tz, tz_len, "%s%s", tzstr, tzenv);
   1140 	}
   1141 
   1142 	add_to_envinit(homedir);
   1143 	add_to_envinit(shell);
   1144 	add_to_envinit(path);
   1145 	add_to_envinit(username);
   1146 	add_to_envinit(tz);
   1147 
   1148 	if (krb5auth_flag > 0) {
   1149 		int length;
   1150 		char *buffer;
   1151 
   1152 		/*
   1153 		 * If we have KRB5CCNAME set, then copy into the child's
   1154 		 * environment.  This can't really have a fixed position
   1155 		 * because `tz' may or may not be set.
   1156 		 */
   1157 		if (getenv("KRB5CCNAME")) {
   1158 			length = (int)strlen(getenv("KRB5CCNAME")) +
   1159 					(int)strlen("KRB5CCNAME=") + 1;
   1160 			buffer = (char *)malloc(length);
   1161 
   1162 			if (buffer) {
   1163 				(void) snprintf(buffer, length, "KRB5CCNAME=%s",
   1164 						getenv("KRB5CCNAME"));
   1165 				add_to_envinit(buffer);
   1166 			}
   1167 		} {
   1168 			/* These two are covered by ADDRPAD */
   1169 			length = strlen(inet_ntoa(localaddr.sin_addr)) + 1 +
   1170 					strlen("KRB5LOCALADDR=");
   1171 			(void) snprintf(local_addr, length, "KRB5LOCALADDR=%s",
   1172 				inet_ntoa(localaddr.sin_addr));
   1173 			add_to_envinit(local_addr);
   1174 
   1175 			length = strlen(inet_ntoa(sin->sin_addr)) + 1 +
   1176 					strlen("KRB5REMOTEADDR=");
   1177 			(void) snprintf(remote_addr, length,
   1178 				"KRB5REMOTEADDR=%s", inet_ntoa(sin->sin_addr));
   1179 			add_to_envinit(remote_addr);
   1180 		}
   1181 
   1182 		/*
   1183 		 * If we do anything else, make sure there is
   1184 		 * space in the array.
   1185 		 */
   1186 		for (cnt = 0; cnt < num_env; cnt++) {
   1187 			char *buf;
   1188 
   1189 			if (getenv(save_env[cnt])) {
   1190 				length = (int)strlen(getenv(save_env[cnt])) +
   1191 					(int)strlen(save_env[cnt]) + 2;
   1192 
   1193 				buf = (char *)malloc(length);
   1194 				if (buf) {
   1195 					(void) snprintf(buf, length, "%s=%s",
   1196 						save_env[cnt],
   1197 						getenv(save_env[cnt]));
   1198 					add_to_envinit(buf);
   1199 				}
   1200 			}
   1201 		}
   1202 
   1203 	}
   1204 
   1205 	/*
   1206 	 * add PAM environment variables set by modules
   1207 	 * -- only allowed 16 (PAM_ENV_ELIM)
   1208 	 * -- check to see if the environment variable is legal
   1209 	 */
   1210 	if ((pam_env = pam_getenvlist(pamh)) != 0) {
   1211 		while (pam_env[idx] != 0) {
   1212 			if (idx < PAM_ENV_ELIM &&
   1213 			    legalenvvar(pam_env[idx])) {
   1214 				add_to_envinit(pam_env[idx]);
   1215 			}
   1216 			idx++;
   1217 		}
   1218 	}
   1219 
   1220 	(void) pam_end(pamh, PAM_SUCCESS);
   1221 
   1222 	/*
   1223 	 * Pick up locale environment variables, if any.
   1224 	 */
   1225 	lenvp = renvp;
   1226 	while (*lenvp != NULL) {
   1227 		int	index;
   1228 
   1229 		for (index = 0; localeenv[index] != NULL; index++)
   1230 			/*
   1231 			 * locale_envmatch() returns 1 if
   1232 			 * *lenvp is localenev[index] and valid.
   1233 			 */
   1234 			if (locale_envmatch(localeenv[index], *lenvp)) {
   1235 				add_to_envinit(*lenvp);
   1236 				break;
   1237 			}
   1238 
   1239 		lenvp++;
   1240 	}
   1241 
   1242 	cp = strrchr(pwd->pw_shell, '/');
   1243 	if (cp != NULL)
   1244 		cp++;
   1245 	else
   1246 		cp = pwd->pw_shell;
   1247 	/*
   1248 	 * rdist has been moved to /usr/bin, so /usr/ucb/rdist might not
   1249 	 * be present on a system.  So if it doesn't exist we fall back
   1250 	 * and try for it in /usr/bin.  We take care to match the space
   1251 	 * after the name because the only purpose of this is to protect
   1252 	 * the internal call from old rdist's, not humans who type
   1253 	 * "rsh foo /usr/ucb/rdist".
   1254 	 */
   1255 #define	RDIST_PROG_NAME	"/usr/ucb/rdist -Server"
   1256 	if (strncmp(cmdbuf, RDIST_PROG_NAME, strlen(RDIST_PROG_NAME)) == 0) {
   1257 		if (stat("/usr/ucb/rdist", &statb) != 0) {
   1258 			(void) strncpy(cmdbuf + 5, "bin", 3);
   1259 		}
   1260 	}
   1261 
   1262 #ifdef DEBUG
   1263 	syslog(LOG_NOTICE, "rshd: cmdbuf = %s", cmdbuf);
   1264 	if (do_encrypt)
   1265 		syslog(LOG_NOTICE, "rshd: cmd to be exec'ed = %s",
   1266 			((char *)cmdbuf + 3));
   1267 #endif /* DEBUG */
   1268 
   1269 	if (do_encrypt && (strncmp(cmdbuf, "-x ", 3) == 0)) {
   1270 		(void) execle(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3,
   1271 				NULL, envinit);
   1272 	} else {
   1273 		(void) execle(pwd->pw_shell, cp, "-c", cmdbuf, NULL,
   1274 				envinit);
   1275 	}
   1276 
   1277 	perror(pwd->pw_shell);
   1278 	exit(1);
   1279 
   1280 signout:
   1281 	if (ccache)
   1282 		(void) pam_close_session(pamh, 0);
   1283 	ccache = NULL;
   1284 	(void) pam_end(pamh, PAM_ABORT);
   1285 	exit(1);
   1286 }
   1287 
   1288 static void
   1289 getstr(fd, buf, cnt, err)
   1290 	int fd;
   1291 	char *buf;
   1292 	int cnt;
   1293 	char *err;
   1294 {
   1295 	char c;
   1296 
   1297 	do {
   1298 		if (read(fd, &c, 1) != 1)
   1299 			exit(1);
   1300 		if (cnt-- == 0) {
   1301 			error("%s too long\n", err);
   1302 			exit(1);
   1303 		}
   1304 		*buf++ = c;
   1305 	} while (c != 0);
   1306 }
   1307 
   1308 /*PRINTFLIKE1*/
   1309 static void
   1310 error(char *fmt, ...)
   1311 {
   1312 	va_list ap;
   1313 	char buf[RSHD_BUFSIZ];
   1314 
   1315 	buf[0] = 1;
   1316 	va_start(ap, fmt);
   1317 	(void) vsnprintf(&buf[1], sizeof (buf) - 1, fmt, ap);
   1318 	va_end(ap);
   1319 	(void) write(STDERR_FILENO, buf, strlen(buf));
   1320 }
   1321 
   1322 static char *illegal[] = {
   1323 	"SHELL=",
   1324 	"HOME=",
   1325 	"LOGNAME=",
   1326 #ifndef NO_MAIL
   1327 	"MAIL=",
   1328 #endif
   1329 	"CDPATH=",
   1330 	"IFS=",
   1331 	"PATH=",
   1332 	"USER=",
   1333 	"TZ=",
   1334 	0
   1335 };
   1336 
   1337 /*
   1338  * legalenvvar - can PAM modules insert this environmental variable?
   1339  */
   1340 
   1341 static int
   1342 legalenvvar(char *s)
   1343 {
   1344 	register char **p;
   1345 
   1346 	for (p = illegal; *p; p++)
   1347 		if (strncmp(s, *p, strlen(*p)) == 0)
   1348 			return (0);
   1349 
   1350 	if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
   1351 		return (0);
   1352 
   1353 	return (1);
   1354 }
   1355 
   1356 /*
   1357  * Add a string to the environment of the new process.
   1358  */
   1359 
   1360 static void
   1361 add_to_envinit(char *string)
   1362 {
   1363 	/*
   1364 	 * Reserve space for 2 * 8 = 16 environment entries initially which
   1365 	 * should be enough to avoid reallocation of "envinit" in most cases.
   1366 	 */
   1367 	static int	size = 8;
   1368 	static int	index = 0;
   1369 
   1370 	if (string == NULL)
   1371 		return;
   1372 
   1373 	if ((envinit == NULL) || (index == size)) {
   1374 		size *= 2;
   1375 		envinit = realloc(envinit, (size + 1) * sizeof (char *));
   1376 		if (envinit == NULL) {
   1377 			perror("malloc");
   1378 			exit(1);
   1379 		}
   1380 	}
   1381 
   1382 	envinit[index++] = string;
   1383 	envinit[index] = NULL;
   1384 }
   1385 
   1386 /*
   1387  * Check if lenv and penv matches or not.
   1388  */
   1389 static int
   1390 locale_envmatch(char *lenv, char *penv)
   1391 {
   1392 	while ((*lenv == *penv) && (*lenv != '\0') && (*penv != '=')) {
   1393 		lenv++;
   1394 		penv++;
   1395 	}
   1396 
   1397 	/*
   1398 	 * '/' is eliminated for security reason.
   1399 	 */
   1400 	return ((*lenv == '\0' && *penv == '=' && *(penv + 1) != '/'));
   1401 }
   1402 
   1403 #ifndef	KRB_SENDAUTH_VLEN
   1404 #define	KRB_SENDAUTH_VLEN	8	/* length for version strings */
   1405 #endif
   1406 
   1407 /* MUST be KRB_SENDAUTH_VLEN chars */
   1408 #define	KRB_SENDAUTH_VERS	"AUTHV0.1"
   1409 #define	SIZEOF_INADDR sizeof (struct in_addr)
   1410 
   1411 static krb5_error_code
   1412 recvauth(int netf, int *valid_checksum)
   1413 {
   1414 	krb5_auth_context auth_context = NULL;
   1415 	krb5_error_code status;
   1416 	struct sockaddr_in laddr;
   1417 	int len;
   1418 	krb5_data inbuf;
   1419 	krb5_authenticator *authenticator;
   1420 	krb5_ticket *ticket;
   1421 	krb5_rcache rcache;
   1422 	krb5_data version;
   1423 	krb5_encrypt_block eblock;	/* eblock for encrypt/decrypt */
   1424 	krb5_data desinbuf;
   1425 	krb5_data desoutbuf;
   1426 	char des_inbuf[2 * RSHD_BUFSIZ];
   1427 			/* needs to be > largest read size */
   1428 	char des_outbuf[2 * RSHD_BUFSIZ + 4];
   1429 			/* needs to be > largest write size */
   1430 
   1431 	*valid_checksum = 0;
   1432 	len = sizeof (laddr);
   1433 
   1434 	if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
   1435 		exit(1);
   1436 	}
   1437 
   1438 	if (status = krb5_auth_con_init(bsd_context, &auth_context))
   1439 		return (status);
   1440 
   1441 	if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf,
   1442 		KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))
   1443 		return (status);
   1444 
   1445 	status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
   1446 	if (status)
   1447 		return (status);
   1448 
   1449 	if (!rcache) {
   1450 		krb5_principal server;
   1451 
   1452 		status = krb5_sname_to_principal(bsd_context, 0, 0,
   1453 			KRB5_NT_SRV_HST, &server);
   1454 		if (status)
   1455 			return (status);
   1456 
   1457 		status = krb5_get_server_rcache(bsd_context,
   1458 			krb5_princ_component(bsd_context, server, 0),
   1459 			&rcache);
   1460 		krb5_free_principal(bsd_context, server);
   1461 		if (status)
   1462 			return (status);
   1463 
   1464 		status = krb5_auth_con_setrcache(bsd_context, auth_context,
   1465 							rcache);
   1466 		if (status)
   1467 			return (status);
   1468 	}
   1469 
   1470 	status = krb5_recvauth_version(bsd_context, &auth_context, &netf,
   1471 		NULL,		/* Specify daemon principal */
   1472 		0,		/* no flags */
   1473 		keytab,		/* normally NULL to use v5srvtab */
   1474 		&ticket,	/* return ticket */
   1475 		&version);	/* application version string */
   1476 
   1477 
   1478 	if (status) {
   1479 		getstr(netf, locuser, sizeof (locuser), "locuser");
   1480 		getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
   1481 		getstr(netf, remuser, sizeof (locuser), "remuser");
   1482 		return (status);
   1483 	}
   1484 	getstr(netf, locuser, sizeof (locuser), "locuser");
   1485 	getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
   1486 
   1487 	/* Must be V5  */
   1488 
   1489 	kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
   1490 	if (version.length != 9 || version.data == NULL) {
   1491 		syslog(LOG_ERR, "bad application version length");
   1492 		error(gettext("bad application version length\n"));
   1493 		exit(1);
   1494 	}
   1495 	if (strncmp(version.data, "KCMDV0.1", 9) == 0) {
   1496 		kcmd_protocol = KCMD_OLD_PROTOCOL;
   1497 	} else if (strncmp(version.data, "KCMDV0.2", 9) == 0) {
   1498 		kcmd_protocol = KCMD_NEW_PROTOCOL;
   1499 	} else {
   1500 		syslog(LOG_ERR, "Unrecognized KCMD protocol (%s)",
   1501 			(char *)version.data);
   1502 		error(gettext("Unrecognized KCMD protocol (%s)"),
   1503 			(char *)version.data);
   1504 		exit(1);
   1505 	}
   1506 	getstr(netf, remuser, sizeof (locuser), "remuser");
   1507 
   1508 	if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client,
   1509 			&kremuser)))
   1510 		return (status);
   1511 
   1512 	if ((status = krb5_copy_principal(bsd_context,
   1513 				ticket->enc_part2->client, &client)))
   1514 		return (status);
   1515 
   1516 
   1517 	if (checksum_required && (kcmd_protocol == KCMD_OLD_PROTOCOL)) {
   1518 		if ((status = krb5_auth_con_getauthenticator(bsd_context,
   1519 			auth_context, &authenticator)))
   1520 			return (status);
   1521 
   1522 		if (authenticator->checksum && checksum_required) {
   1523 			struct sockaddr_in adr;
   1524 			int adr_length = sizeof (adr);
   1525 			int chksumsize = strlen(cmdbuf) + strlen(locuser) + 32;
   1526 			krb5_data input;
   1527 			krb5_keyblock key;
   1528 
   1529 			char *chksumbuf = (char *)malloc(chksumsize);
   1530 
   1531 			if (chksumbuf == 0)
   1532 				goto error_cleanup;
   1533 			if (getsockname(netf, (struct sockaddr *)&adr,
   1534 					&adr_length) != 0)
   1535 				goto error_cleanup;
   1536 
   1537 			(void) snprintf(chksumbuf, chksumsize, "%u:",
   1538 					ntohs(adr.sin_port));
   1539 			if (strlcat(chksumbuf, cmdbuf,
   1540 					chksumsize) >= chksumsize) {
   1541 				syslog(LOG_ERR, "cmd buffer too long.");
   1542 				free(chksumbuf);
   1543 				return (-1);
   1544 			}
   1545 			if (strlcat(chksumbuf, locuser,
   1546 					chksumsize) >= chksumsize) {
   1547 				syslog(LOG_ERR, "locuser too long.");
   1548 				free(chksumbuf);
   1549 				return (-1);
   1550 			}
   1551 
   1552 			input.data = chksumbuf;
   1553 			input.length = strlen(chksumbuf);
   1554 			key.magic = ticket->enc_part2->session->magic;
   1555 			key.enctype = ticket->enc_part2->session->enctype;
   1556 			key.contents = ticket->enc_part2->session->contents;
   1557 			key.length = ticket->enc_part2->session->length;
   1558 
   1559 			status = krb5_c_verify_checksum(bsd_context,
   1560 			    &key, 0, &input, authenticator->checksum,
   1561 			    (unsigned int *)valid_checksum);
   1562 
   1563 			if (status == 0 && *valid_checksum == 0)
   1564 			    status = KRB5KRB_AP_ERR_BAD_INTEGRITY;
   1565 error_cleanup:
   1566 			if (chksumbuf)
   1567 				krb5_xfree(chksumbuf);
   1568 			if (status) {
   1569 				krb5_free_authenticator(bsd_context,
   1570 						authenticator);
   1571 				return (status);
   1572 			}
   1573 		}
   1574 		krb5_free_authenticator(bsd_context, authenticator);
   1575 	}
   1576 
   1577 
   1578 	if ((strncmp(cmdbuf, "-x ", 3) == 0)) {
   1579 		if (krb5_privacy_allowed()) {
   1580 			do_encrypt = 1;
   1581 		} else {
   1582 			syslog(LOG_ERR, "rshd: Encryption not supported");
   1583 			error("rshd: Encryption not supported. \n");
   1584 			exit(2);
   1585 		}
   1586 
   1587 		status = krb5_auth_con_getremotesubkey(bsd_context,
   1588 						    auth_context,
   1589 						    &sessionkey);
   1590 		if (status) {
   1591 			syslog(LOG_ERR, "Error getting KRB5 session subkey");
   1592 			error(gettext("Error getting KRB5 session subkey"));
   1593 			exit(1);
   1594 		}
   1595 		/*
   1596 		 * The "new" protocol requires that a subkey be sent.
   1597 		 */
   1598 		if (sessionkey == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) {
   1599 			syslog(LOG_ERR, "No KRB5 session subkey sent");
   1600 			error(gettext("No KRB5 session subkey sent"));
   1601 			exit(1);
   1602 		}
   1603 		/*
   1604 		 * The "old" protocol does not permit an authenticator subkey.
   1605 		 * The key is taken from the ticket instead (see below).
   1606 		 */
   1607 		if (sessionkey != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) {
   1608 			syslog(LOG_ERR, "KRB5 session subkey not permitted "
   1609 				"with old KCMD protocol");
   1610 			error(gettext("KRB5 session subkey not permitted "
   1611 				"with old KCMD protocol"));
   1612 			exit(1);
   1613 		}
   1614 		/*
   1615 		 * If no key at this point, use the session key from
   1616 		 * the ticket.
   1617 		 */
   1618 		if (sessionkey == NULL) {
   1619 			/*
   1620 			 * Save the session key so we can configure the crypto
   1621 			 * module later.
   1622 			 */
   1623 			status = krb5_copy_keyblock(bsd_context,
   1624 						ticket->enc_part2->session,
   1625 						&sessionkey);
   1626 			if (status) {
   1627 				syslog(LOG_ERR, "krb5_copy_keyblock failed");
   1628 				error(gettext("krb5_copy_keyblock failed"));
   1629 				exit(1);
   1630 			}
   1631 		}
   1632 		/*
   1633 		 * If session key still cannot be found, we must
   1634 		 * exit because encryption is required here
   1635 		 * when encr_flag (-x) is set.
   1636 		 */
   1637 		if (sessionkey == NULL) {
   1638 			syslog(LOG_ERR, "Could not find an encryption key");
   1639 			error(gettext("Could not find an encryption key"));
   1640 			exit(1);
   1641 		}
   1642 
   1643 		/*
   1644 		 * Initialize parameters/buffers for desread & deswrite here.
   1645 		 */
   1646 		desinbuf.data = des_inbuf;
   1647 		desoutbuf.data = des_outbuf;
   1648 		desinbuf.length = sizeof (des_inbuf);
   1649 		desoutbuf.length = sizeof (des_outbuf);
   1650 
   1651 		eblock.crypto_entry = sessionkey->enctype;
   1652 		eblock.key = (krb5_keyblock *)sessionkey;
   1653 
   1654 		init_encrypt(do_encrypt, bsd_context, kcmd_protocol,
   1655 				&desinbuf, &desoutbuf, SERVER, &eblock);
   1656 	}
   1657 
   1658 	ticket->enc_part2->session = 0;
   1659 
   1660 	if ((status = krb5_read_message(bsd_context, (krb5_pointer) & netf,
   1661 				&inbuf))) {
   1662 		error(gettext("Error reading message: %s\n"),
   1663 				error_message(status));
   1664 		exit(1);
   1665 	}
   1666 
   1667 	if (inbuf.length) {
   1668 		krb5_creds **creds = NULL;
   1669 
   1670 		/* Forwarding being done, read creds */
   1671 		if ((status = krb5_rd_cred(bsd_context,
   1672 					    auth_context, &inbuf, &creds,
   1673 					    NULL))) {
   1674 			error("Can't get forwarded credentials: %s\n",
   1675 				error_message(status));
   1676 			exit(1);
   1677 		}
   1678 
   1679 		/* Store the forwarded creds in the ccache */
   1680 		if ((status = store_forw_creds(bsd_context,
   1681 					    creds, ticket, locuser,
   1682 					    &ccache))) {
   1683 			error("Can't store forwarded credentials: %s\n",
   1684 				error_message(status));
   1685 			exit(1);
   1686 		}
   1687 		krb5_free_creds(bsd_context, *creds);
   1688 	}
   1689 
   1690 	krb5_free_ticket(bsd_context, ticket);
   1691 	return (0);
   1692 }
   1693 
   1694 static void
   1695 usage(void)
   1696 {
   1697 	(void) fprintf(stderr, gettext("%s: rshd [-k5eciU] "
   1698 			"[-P path] [-M realm] [-s tos] "
   1699 #ifdef DEBUG
   1700 			"[-D port] "
   1701 #endif /* DEBUG */
   1702 			"[-S keytab]"), gettext("usage"));
   1703 
   1704 	syslog(LOG_ERR, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] "
   1705 #ifdef DEBUG
   1706 			"[-D port] "
   1707 #endif /* DEBUG */
   1708 			"[-S keytab]", gettext("usage"));
   1709 }
   1710