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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     36 
     37 #define	_FILE_OFFSET_BITS 64
     38 
     39 /*
     40  * remote shell server:
     41  *	remuser\0
     42  *	locuser\0
     43  *	command\0
     44  *	data
     45  */
     46 #include <sys/types.h>
     47 #include <sys/ioctl.h>
     48 #include <sys/telioctl.h>
     49 #include <sys/param.h>
     50 #include <sys/socket.h>
     51 #include <sys/time.h>
     52 #include <sys/stat.h>
     53 #include <sys/file.h>
     54 #include <sys/select.h>
     55 
     56 #include <netinet/in.h>
     57 
     58 #include <arpa/inet.h>
     59 
     60 #include <unistd.h>
     61 #include <string.h>
     62 #include <stdio.h>
     63 #include <stdarg.h>
     64 #include <errno.h>
     65 #include <pwd.h>
     66 #include <grp.h>
     67 #include <signal.h>
     68 #include <netdb.h>
     69 #include <syslog.h>
     70 #include <fcntl.h>
     71 #include <ctype.h>
     72 #include <locale.h>
     73 
     74 #include <sys/resource.h>
     75 #include <sys/filio.h>
     76 #include <shadow.h>
     77 #include <stdlib.h>
     78 
     79 #include <security/pam_appl.h>
     80 
     81 #include <k5-int.h>
     82 #include <krb5_repository.h>
     83 #include <com_err.h>
     84 #include <kcmd.h>
     85 
     86 #include <addr_match.h>
     87 #include <store_forw_creds.h>
     88 
     89 #ifndef NCARGS
     90 #define	NCARGS	5120
     91 #endif /* !NCARGS */
     92 
     93 static void error(char *, ...);
     94 static void doit(int, struct sockaddr_storage *, char **);
     95 static void getstr(int, char *, int, char *);
     96 
     97 static int legalenvvar(char *);
     98 static void add_to_envinit(char *);
     99 static int locale_envmatch(char *, char *);
    100 
    101 /* Function decls. for functions not in any header file.  (Grrrr.) */
    102 extern int audit_rshd_setup(void);
    103 extern int audit_rshd_success(char *, char *, char *, char *);
    104 extern int audit_rshd_fail(char *, char *, char *, char *, char *);
    105 extern int audit_settid(int);
    106 
    107 static int do_encrypt = 0;
    108 static pam_handle_t *pamh;
    109 
    110 /*
    111  * This is the shell/kshell daemon. The very basic protocol for checking
    112  * authentication and authorization is:
    113  * 1) Check authentication.
    114  * 2) Check authorization via the access-control files:
    115  *    ~/.k5login (using krb5_kuserok) and/or
    116  * Execute command if configured authoriztion checks pass, else deny
    117  * permission.
    118  *
    119  * The configuration is done either by command-line arguments passed by inetd,
    120  * or by the name of the daemon. If command-line arguments are present, they
    121  * take priority. The options are:
    122  * -k allow kerberos authentication (krb5 only; krb4 support is not provided)
    123  * -5 same as `-k', mainly for compatability with MIT
    124  * -e allow encrypted session
    125  * -c demand authenticator checksum
    126  * -i ignore authenticator checksum
    127  * -U Refuse connections that cannot be mapped to a name via `gethostbyname'
    128  * -s <tos>	Set the IP TOS option
    129  * -S <keytab>	Set the keytab file to use
    130  * -M <realm>	Set the Kerberos realm to use
    131  */
    132 
    133 #define	ARGSTR	"ek5ciUD:M:S:L:?:"
    134 #define	RSHD_BUFSIZ	(50 * 1024)
    135 
    136 static krb5_context bsd_context;
    137 static krb5_keytab keytab = NULL;
    138 static krb5_ccache ccache = NULL;
    139 static krb5_keyblock *sessionkey = NULL;
    140 
    141 static int require_encrypt = 0;
    142 static int resolve_hostname = 0;
    143 static int krb5auth_flag = 0;	/* Flag set, when KERBEROS is enabled */
    144 static enum kcmd_proto kcmd_protocol;
    145 
    146 #ifdef DEBUG
    147 static int debug_port = 0;
    148 #endif /* DEBUG */
    149 
    150 /*
    151  * There are two authentication related masks:
    152  * auth_ok and auth_sent.
    153  * The auth_ok mask is the or'ing of authentication
    154  * systems any one of which can be used.
    155  * The auth_sent mask is the or'ing of one or more authentication/authorization
    156  * systems that succeeded.  If the and'ing
    157  * of these two masks is true, then authorization is successful.
    158  */
    159 
    160 #define	AUTH_KRB5	(0x2)
    161 static int auth_ok = 0;
    162 static int auth_sent = 0;
    163 static int checksum_required = 0;
    164 static int checksum_ignored = 0;
    165 
    166 /*
    167  * Leave room for 4 environment variables to be passed.
    168  * The "-L env_var" option has been added primarily to
    169  * maintain compatability with MIT.
    170  */
    171 #define	MAXENV	4
    172 static char *save_env[MAXENV];
    173 static int num_env = 0;
    174 
    175 static void usage(void);
    176 static krb5_error_code recvauth(int, int *);
    177 
    178 /*ARGSUSED*/
    179 int
    180 main(int argc, char **argv, char **renvp)
    181 {
    182 	struct linger linger;
    183 	int on = 1, fromlen;
    184 	struct sockaddr_storage from;
    185 	int fd = 0;
    186 
    187 	extern int opterr, optind;
    188 	extern char *optarg;
    189 	int ch;
    190 	int tos = -1;
    191 	krb5_error_code status;
    192 
    193 	openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
    194 	(void) audit_rshd_setup();	/* BSM */
    195 	fromlen = sizeof (from);
    196 
    197 	(void) setlocale(LC_ALL, "");
    198 
    199 	/*
    200 	 * Analyze parameters.
    201 	 */
    202 	opterr = 0;
    203 	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
    204 		switch (ch) {
    205 		case '5':
    206 		case 'k':
    207 			auth_ok |= AUTH_KRB5;
    208 			krb5auth_flag++;
    209 			break;
    210 
    211 		case 'c':
    212 			checksum_required = 1;
    213 			krb5auth_flag++;
    214 			break;
    215 		case 'i':
    216 			checksum_ignored = 1;
    217 			krb5auth_flag++;
    218 			break;
    219 
    220 		case 'e':
    221 			require_encrypt = 1;
    222 			krb5auth_flag++;
    223 			break;
    224 #ifdef DEBUG
    225 		case 'D':
    226 			debug_port = atoi(optarg);
    227 			break;
    228 #endif /* DEBUG */
    229 		case 'U':
    230 			resolve_hostname = 1;
    231 			break;
    232 
    233 		case 'M':
    234 			krb5_set_default_realm(bsd_context, optarg);
    235 			krb5auth_flag++;
    236 			break;
    237 
    238 		case 'S':
    239 			if ((status = krb5_kt_resolve(bsd_context, optarg,
    240 				&keytab))) {
    241 				com_err("rsh", status,
    242 					gettext("while resolving "
    243 						"srvtab file %s"), optarg);
    244 				exit(2);
    245 			}
    246 			krb5auth_flag++;
    247 			break;
    248 
    249 		case 's':
    250 			if (optarg == NULL || ((tos = atoi(optarg)) < 0) ||
    251 				(tos > 255)) {
    252 				syslog(LOG_ERR, "rshd: illegal tos value: "
    253 				    "%s\n", optarg);
    254 			}
    255 			break;
    256 
    257 		case 'L':
    258 			if (num_env < MAXENV) {
    259 				save_env[num_env] = strdup(optarg);
    260 				if (!save_env[num_env++]) {
    261 					com_err("rsh", ENOMEM,
    262 						gettext("in saving env"));
    263 					exit(2);
    264 				}
    265 			} else {
    266 				(void) fprintf(stderr, gettext("rshd: Only %d"
    267 						" -L arguments allowed\n"),
    268 						MAXENV);
    269 				exit(2);
    270 			}
    271 			break;
    272 
    273 		case '?':
    274 		default:
    275 			usage();
    276 			exit(1);
    277 			break;
    278 		}
    279 
    280 	if (optind == 0) {
    281 		usage();
    282 		exit(1);
    283 	}
    284 	argc -= optind;
    285 	argv += optind;
    286 
    287 	if (krb5auth_flag > 0) {
    288 		status = krb5_init_context(&bsd_context);
    289 		if (status) {
    290 			syslog(LOG_ERR, "Error initializing krb5: %s",
    291 			    error_message(status));
    292 			exit(1);
    293 		}
    294 	}
    295 
    296 	if (!checksum_required && !checksum_ignored)
    297 		checksum_ignored = 1;
    298 
    299 	if (checksum_required && checksum_ignored) {
    300 		syslog(LOG_CRIT, gettext("Checksums are required and ignored."
    301 		"These options are mutually exclusive"
    302 		"--check the documentation."));
    303 		error("Configuration error: mutually exclusive "
    304 				"options specified.\n");
    305 		exit(1);
    306 	}
    307 
    308 #ifdef DEBUG
    309 	if (debug_port) {
    310 		int s;
    311 		struct sockaddr_in sin;
    312 
    313 		if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
    314 			fprintf(stderr, gettext("Error in socket: %s\n"),
    315 					strerror(errno));
    316 			exit(2);
    317 		}
    318 		(void) memset((char *)&sin, 0, sizeof (sin));
    319 		sin.sin_family = AF_INET;
    320 		sin.sin_port = htons(debug_port);
    321 		sin.sin_addr.s_addr = INADDR_ANY;
    322 
    323 		(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
    324 			(char *)&on, sizeof (on));
    325 
    326 		if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) {
    327 			(void) fprintf(stderr, gettext("Error in bind: %s\n"),
    328 					strerror(errno));
    329 			exit(2);
    330 		}
    331 		if ((listen(s, 5)) < 0) {
    332 			(void) fprintf(stderr, gettext("Error in listen: %s\n"),
    333 					strerror(errno));
    334 			exit(2);
    335 		}
    336 		if ((fd = accept(s, (struct sockaddr *)&from,
    337 					&fromlen)) < 0) {
    338 			(void) fprintf(stderr, gettext("Error in accept: %s\n"),
    339 					strerror(errno));
    340 			exit(2);
    341 		}
    342 		(void) close(s);
    343 	}
    344 	else
    345 #endif /* DEBUG */
    346 	{
    347 		if (getpeername(STDIN_FILENO, (struct sockaddr *)&from,
    348 				(socklen_t *)&fromlen) < 0) {
    349 			(void) fprintf(stderr, "rshd: ");
    350 			perror("getpeername");
    351 			_exit(1);
    352 		}
    353 		fd = STDIN_FILENO;
    354 	}
    355 
    356 	if (audit_settid(fd) != 0) {
    357 		perror("settid");
    358 		exit(1);
    359 	}
    360 
    361 	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
    362 	    sizeof (on)) < 0)
    363 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
    364 	linger.l_onoff = 1;
    365 	linger.l_linger = 60;			/* XXX */
    366 	if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger,
    367 	    sizeof (linger)) < 0)
    368 		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
    369 
    370 	if ((tos != -1) && (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos,
    371 				sizeof (tos)) < 0) &&
    372 				(errno != ENOPROTOOPT)) {
    373 		syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m");
    374 	}
    375 
    376 	doit(dup(fd), &from, renvp);
    377 	return (0);
    378 }
    379 
    380 /*
    381  * locale environments to be passed to shells.
    382  */
    383 static char *localeenv[] = {
    384 	"LANG",
    385 	"LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
    386 	"LC_MONETARY", "LC_MESSAGES", "LC_ALL", NULL};
    387 
    388 /*
    389  * The following is for the environment variable list
    390  * used in the call to execle().  envinit is declared here,
    391  * but populated after the call to getpwnam().
    392  */
    393 static char	*homedir;	/* "HOME=" */
    394 static char	*shell;		/* "SHELL=" */
    395 static char	*username;	/* "USER=" */
    396 static char	*tz;		/* "TZ=" */
    397 
    398 static char	homestr[] = "HOME=";
    399 static char	shellstr[] = "SHELL=";
    400 static char	userstr[] = "USER=";
    401 static char	tzstr[] = "TZ=";
    402 
    403 static char	**envinit;
    404 #define	PAM_ENV_ELIM	16	/* allow 16 PAM environment variables */
    405 #define	USERNAME_LEN	16	/* maximum number of characters in user name */
    406 
    407 /*
    408  *	See PSARC opinion 1992/025
    409  */
    410 static char	userpath[] = "PATH=/usr/bin:";
    411 static char	rootpath[] = "PATH=/usr/sbin:/usr/bin";
    412 
    413 static char cmdbuf[NCARGS+1];
    414 static char hostname [MAXHOSTNAMELEN + 1];
    415 static char locuser[USERNAME_LEN + 1];
    416 static char remuser[USERNAME_LEN + 1];
    417 
    418 #define	KRB5_RECVAUTH_V5	5
    419 #define	SIZEOF_INADDR sizeof	(struct in_addr)
    420 
    421 #define	MAX_REPOSITORY_LEN	255
    422 static char repository[MAX_REPOSITORY_LEN];
    423 
    424 static char *kremuser;
    425 static krb5_principal client = NULL;
    426 
    427 static char	remote_addr[64];
    428 static char	local_addr[64];
    429 
    430 static void
    431 doit(int f, struct sockaddr_storage *fromp, char **renvp)
    432 {
    433 	char *cp;
    434 
    435 	struct passwd *pwd;
    436 	char *path;
    437 	char *tzenv;
    438 	struct spwd *shpwd;
    439 	struct stat statb;
    440 	char **lenvp;
    441 
    442 	krb5_error_code status;
    443 	int valid_checksum;
    444 	int cnt;
    445 	int sin_len;
    446 	struct sockaddr_in localaddr;
    447 
    448 	int s;
    449 	in_port_t port;
    450 	pid_t pid;
    451 	int pv[2], pw[2], px[2], cc;
    452 	char buf[RSHD_BUFSIZ];
    453 	char sig;
    454 	int one = 1;
    455 	int v = 0;
    456 	int err = 0;
    457 	int idx = 0;
    458 	char **pam_env;
    459 	char abuf[INET6_ADDRSTRLEN];
    460 	struct sockaddr_in *sin;
    461 	struct sockaddr_in6 *sin6;
    462 	int fromplen;
    463 	int homedir_len, shell_len, username_len, tz_len;
    464 	int no_name;
    465 	boolean_t bad_port;
    466 	int netf = 0;
    467 
    468 	(void) signal(SIGINT, SIG_DFL);
    469 	(void) signal(SIGQUIT, SIG_DFL);
    470 	(void) signal(SIGTERM, SIG_DFL);
    471 	(void) signal(SIGXCPU, SIG_DFL);
    472 	(void) signal(SIGXFSZ, SIG_DFL);
    473 	(void) sigset(SIGCHLD, SIG_IGN);
    474 	(void) signal(SIGPIPE, SIG_DFL);
    475 	(void) signal(SIGHUP, SIG_DFL);
    476 
    477 #ifdef DEBUG
    478 	{ int t = open("/dev/tty", 2);
    479 	    if (t >= 0) {
    480 		(void) setsid();
    481 		(void) close(t);
    482 	    }
    483 	}
    484 #endif
    485 	if (fromp->ss_family == AF_INET) {
    486 		sin = (struct sockaddr_in *)fromp;
    487 		port = ntohs((ushort_t)sin->sin_port);
    488 		fromplen = sizeof (struct sockaddr_in);
    489 	} else if (fromp->ss_family == AF_INET6) {
    490 		sin6 = (struct sockaddr_in6 *)fromp;
    491 		port = ntohs((ushort_t)sin6->sin6_port);
    492 		fromplen = sizeof (struct sockaddr_in6);
    493 	} else {
    494 		syslog(LOG_ERR, "wrong address family\n");
    495 		exit(1);
    496 	}
    497 
    498 	if (fromp->ss_family == AF_INET6) {
    499 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    500 			struct in_addr ipv4_addr;
    501 
    502 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr);
    503 			(void) inet_ntop(AF_INET, &ipv4_addr, abuf,
    504 			    sizeof (abuf));
    505 		} else {
    506 			(void) inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
    507 			    sizeof (abuf));
    508 		}
    509 	} else if (fromp->ss_family == AF_INET) {
    510 		(void) inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof (abuf));
    511 	}
    512 
    513 	sin_len = sizeof (struct sockaddr_in);
    514 	if (getsockname(f, (struct sockaddr *)&localaddr, &sin_len) < 0) {
    515 		perror("getsockname");
    516 		exit(1);
    517 	}
    518 
    519 	netf = f;
    520 
    521 	bad_port = (port >= IPPORT_RESERVED ||
    522 		port < (uint_t)(IPPORT_RESERVED/2));
    523 
    524 	/* Get the name of the client side host to use later */
    525 	no_name = (getnameinfo((const struct sockaddr *) fromp, fromplen,
    526 		hostname, sizeof (hostname), NULL, 0, 0) != 0);
    527 
    528 	if (bad_port || no_name != 0) {
    529 		/*
    530 		 * If there is no host name available then use the
    531 		 * IP address to identify the host in the PAM call
    532 		 * below.  Do the same if a bad port was used, to
    533 		 * prevent untrustworthy authentication.
    534 		 */
    535 		(void) strlcpy(hostname, abuf, sizeof (hostname));
    536 	}
    537 
    538 	if (no_name != 0) {
    539 		/*
    540 		 * If the '-U' option was given on the cmd line,
    541 		 * we must be able to lookup the hostname
    542 		 */
    543 		if (resolve_hostname) {
    544 			syslog(LOG_ERR, "rshd: Couldn't resolve your "
    545 			    "address into a host name.\r\n Please "
    546 			    "contact your net administrator");
    547 			exit(1);
    548 		}
    549 	} else {
    550 		/*
    551 		 * Even if getnameinfo() succeeded, we still have to check
    552 		 * for spoofing.
    553 		 */
    554 		check_address("rshd", fromp, sin, sin6, abuf, hostname,
    555 		    sizeof (hostname));
    556 	}
    557 
    558 	if (!krb5auth_flag && bad_port) {
    559 		if (no_name)
    560 			syslog(LOG_NOTICE, "connection from %s - "
    561 			    "bad port\n", abuf);
    562 		else
    563 			syslog(LOG_NOTICE, "connection from %s (%s) - "
    564 			    "bad port\n", hostname, abuf);
    565 		exit(1);
    566 	}
    567 
    568 	(void) alarm(60);
    569 	port = 0;
    570 	for (;;) {
    571 		char c;
    572 		if ((cc = read(f, &c, 1)) != 1) {
    573 			if (cc < 0)
    574 				syslog(LOG_NOTICE, "read: %m");
    575 			(void) shutdown(f, 1+1);
    576 			exit(1);
    577 		}
    578 		if (c == 0)
    579 			break;
    580 		port = port * 10 + c - '0';
    581 	}
    582 	(void) alarm(0);
    583 	if (port != 0) {
    584 		int lport = 0;
    585 		struct sockaddr_storage ctl_addr;
    586 		int addrlen;
    587 
    588 		(void) memset(&ctl_addr, 0, sizeof (ctl_addr));
    589 		addrlen = sizeof (ctl_addr);
    590 		if (getsockname(f, (struct sockaddr *)&ctl_addr,
    591 			&addrlen) < 0) {
    592 			syslog(LOG_ERR, "getsockname: %m");
    593 			exit(1);
    594 		}
    595 get_port:
    596 		/*
    597 		 * 0 means that rresvport_addr() will bind to a port in
    598 		 * the anonymous priviledged port range.
    599 		 */
    600 		if (krb5auth_flag) {
    601 			/*
    602 			 * Kerberos does not support IPv6 yet.
    603 			 */
    604 			lport = IPPORT_RESERVED - 1;
    605 		}
    606 		s = rresvport_addr(&lport, &ctl_addr);
    607 
    608 		if (s < 0) {
    609 			syslog(LOG_ERR, "can't get stderr port: %m");
    610 			exit(1);
    611 		}
    612 		if (!krb5auth_flag && (port >= IPPORT_RESERVED)) {
    613 			syslog(LOG_ERR, "2nd port not reserved\n");
    614 			exit(1);
    615 		}
    616 		if (fromp->ss_family == AF_INET) {
    617 			sin->sin_port = htons((ushort_t)port);
    618 		} else if (fromp->ss_family == AF_INET6) {
    619 			sin6->sin6_port = htons((ushort_t)port);
    620 		}
    621 		if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) {
    622 			if (errno == EADDRINUSE) {
    623 				(void) close(s);
    624 				goto get_port;
    625 			}
    626 			syslog(LOG_INFO, "connect second port: %m");
    627 			exit(1);
    628 		}
    629 	}
    630 	(void) dup2(f, 0);
    631 	(void) dup2(f, 1);
    632 	(void) dup2(f, 2);
    633 
    634 #ifdef DEBUG
    635 	syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname);
    636 	if (debug_port)
    637 		syslog(LOG_NOTICE, "rshd: Debug port is %d", debug_port);
    638 	if (krb5auth_flag > 0)
    639 		syslog(LOG_NOTICE, "rshd: Kerberos mode is ON");
    640 	else
    641 		syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF");
    642 #endif /* DEBUG */
    643 
    644 	if (krb5auth_flag > 0) {
    645 		if ((status = recvauth(f, &valid_checksum))) {
    646 			syslog(LOG_ERR, gettext("Kerberos Authentication "
    647 					"failed \n"));
    648 			error("Authentication failed: %s\n",
    649 					error_message(status));
    650 			(void) audit_rshd_fail("Kerberos Authentication "
    651 				"failed", hostname, remuser, locuser, cmdbuf);
    652 			exit(1);
    653 		}
    654 
    655 		if (checksum_required && !valid_checksum &&
    656 			kcmd_protocol == KCMD_OLD_PROTOCOL) {
    657 			syslog(LOG_WARNING, "Client did not supply required"
    658 					" checksum--connection rejected.");
    659 			error("Client did not supply required"
    660 				"checksum--connection rejected.\n");
    661 			(void) audit_rshd_fail("Client did not supply required"
    662 				" checksum--connection rejected.", hostname,
    663 				remuser, locuser, cmdbuf);	/* BSM */
    664 			goto signout;
    665 		}
    666 
    667 		/*
    668 		 * Authentication has succeeded, we now need
    669 		 * to check authorization.
    670 		 *
    671 		 * krb5_kuserok returns 1 if OK.
    672 		 */
    673 		if (client && krb5_kuserok(bsd_context, client, locuser)) {
    674 			auth_sent |= AUTH_KRB5;
    675 		} else {
    676 			syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
    677 				"%s failed krb5_kuserok.\n",
    678 				kremuser, remuser, hostname, locuser);
    679 		}
    680 	} else {
    681 		getstr(netf, remuser, sizeof (remuser), "remuser");
    682 		getstr(netf, locuser, sizeof (locuser), "locuser");
    683 		getstr(netf, cmdbuf, sizeof (cmdbuf), "command");
    684 	}
    685 
    686 #ifdef DEBUG
    687 	syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s",
    688 			locuser, remuser, cmdbuf);
    689 #endif /* DEBUG */
    690 
    691 	/*
    692 	 * Note that there is no rsh conv functions at present.
    693 	 */
    694 	if (krb5auth_flag > 0) {
    695 		if ((err = pam_start("krsh", locuser, NULL, &pamh))
    696 				!= PAM_SUCCESS) {
    697 			syslog(LOG_ERR, "pam_start() failed: %s\n",
    698 				pam_strerror(0, err));
    699 			exit(1);
    700 		}
    701 	}
    702 	else
    703 	{
    704 		if ((err = pam_start("rsh", locuser, NULL, &pamh))
    705 				!= PAM_SUCCESS) {
    706 			syslog(LOG_ERR, "pam_start() failed: %s\n",
    707 				pam_strerror(0, err));
    708 			exit(1);
    709 		}
    710 	}
    711 	if ((err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) {
    712 		syslog(LOG_ERR, "pam_set_item() failed: %s\n",
    713 			pam_strerror(pamh, err));
    714 		exit(1);
    715 	}
    716 	if ((err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS) {
    717 		syslog(LOG_ERR, "pam_set_item() failed: %s\n",
    718 			pam_strerror(pamh, err));
    719 		exit(1);
    720 	}
    721 
    722 	pwd = getpwnam(locuser);
    723 	shpwd = getspnam(locuser);
    724 	if ((pwd == NULL) || (shpwd == NULL)) {
    725 		if (krb5auth_flag > 0)
    726 			syslog(LOG_ERR, "Principal %s (%s@%s) for local user "
    727 				"%s has no account.\n", kremuser, remuser,
    728 							hostname, locuser);
    729 		error("permission denied.\n");
    730 		(void) audit_rshd_fail("Login incorrect", hostname,
    731 			remuser, locuser, cmdbuf);	/* BSM */
    732 		exit(1);
    733 	}
    734 
    735 	if (krb5auth_flag > 0) {
    736 		(void) snprintf(repository, sizeof (repository),
    737 					KRB5_REPOSITORY_NAME);
    738 		/*
    739 		 * We currently only support special handling of the
    740 		 * KRB5 PAM repository
    741 		 */
    742 		if (strlen(locuser) != 0) {
    743 			krb5_repository_data_t krb5_data;
    744 			pam_repository_t pam_rep_data;
    745 
    746 			krb5_data.principal = locuser;
    747 			krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED;
    748 
    749 			pam_rep_data.type = repository;
    750 			pam_rep_data.scope = (void *)&krb5_data;
    751 			pam_rep_data.scope_len = sizeof (krb5_data);
    752 
    753 			(void) pam_set_item(pamh, PAM_REPOSITORY,
    754 					(void *)&pam_rep_data);
    755 		}
    756 	}
    757 
    758 	/*
    759 	 * maintain 2.1 and 4.* and BSD semantics with anonymous rshd
    760 	 */
    761 	if (shpwd->sp_pwdp != 0 && *shpwd->sp_pwdp != '\0' &&
    762 	    (v = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
    763 		error("permission denied\n");
    764 		(void) audit_rshd_fail("Permission denied", hostname,
    765 			remuser, locuser, cmdbuf);	/* BSM */
    766 		(void) pam_end(pamh, v);
    767 		exit(1);
    768 	}
    769 
    770 	if (krb5auth_flag > 0) {
    771 		if (require_encrypt && (!do_encrypt)) {
    772 			error("You must use encryption.\n");
    773 			(void) audit_rshd_fail("You must use encryption.",
    774 				hostname, remuser, locuser, cmdbuf); /* BSM */
    775 			goto signout;
    776 		}
    777 
    778 		if (!(auth_ok & auth_sent)) {
    779 			if (auth_sent) {
    780 				error("Another authentication mechanism "
    781 				    "must be used to access this host.\n");
    782 				(void) audit_rshd_fail("Another authentication"
    783 					" mechanism must be used to access"
    784 					" this host.\n", hostname, remuser,
    785 					locuser, cmdbuf); /* BSM */
    786 				goto signout;
    787 			} else {
    788 				error("Permission denied.\n");
    789 				(void) audit_rshd_fail("Permission denied.",
    790 					hostname, remuser, locuser, cmdbuf);
    791 					/* BSM */
    792 				goto signout;
    793 			}
    794 		}
    795 
    796 
    797 		if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
    798 			error("Logins currently disabled.\n");
    799 			(void) audit_rshd_fail("Logins currently disabled.",
    800 				hostname, remuser, locuser, cmdbuf);
    801 			goto signout;
    802 		}
    803 
    804 		/* Log access to account */
    805 		if (pwd && (pwd->pw_uid == 0)) {
    806 			syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)"
    807 			    " as ROOT", cmdbuf,
    808 			    kremuser, remuser, hostname);
    809 		}
    810 	}
    811 
    812 	if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
    813 		switch (v) {
    814 		case PAM_NEW_AUTHTOK_REQD:
    815 			error("password expired\n");
    816 			(void) audit_rshd_fail("Password expired", hostname,
    817 				remuser, locuser, cmdbuf); /* BSM */
    818 			break;
    819 		case PAM_PERM_DENIED:
    820 			error("account expired\n");
    821 			(void) audit_rshd_fail("Account expired", hostname,
    822 				remuser, locuser, cmdbuf); /* BSM */
    823 			break;
    824 		case PAM_AUTHTOK_EXPIRED:
    825 			error("password expired\n");
    826 			(void) audit_rshd_fail("Password expired", hostname,
    827 				remuser, locuser, cmdbuf); /* BSM */
    828 			break;
    829 		default:
    830 			error("login incorrect\n");
    831 			(void) audit_rshd_fail("Permission denied", hostname,
    832 				remuser, locuser, cmdbuf); /* BSM */
    833 			break;
    834 		}
    835 		(void) pam_end(pamh, PAM_ABORT);
    836 		exit(1);
    837 	}
    838 
    839 	if (chdir(pwd->pw_dir) < 0) {
    840 		(void) chdir("/");
    841 #ifdef notdef
    842 		error("No remote directory.\n");
    843 
    844 		exit(1);
    845 #endif
    846 	}
    847 
    848 	/*
    849 	 * XXX There is no session management currently being done
    850 	 */
    851 
    852 	(void) write(STDERR_FILENO, "\0", 1);
    853 	if (port || do_encrypt) {
    854 		if ((pipe(pv) < 0)) {
    855 			error("Can't make pipe.\n");
    856 			(void) pam_end(pamh, PAM_ABORT);
    857 			exit(1);
    858 		}
    859 		if (do_encrypt) {
    860 			if (pipe(pw) < 0) {
    861 				error("Can't make pipe 2.\n");
    862 				(void) pam_end(pamh, PAM_ABORT);
    863 				exit(1);
    864 			}
    865 			if (pipe(px) < 0) {
    866 				error("Can't make pipe 3.\n");
    867 				(void) pam_end(pamh, PAM_ABORT);
    868 				exit(1);
    869 			}
    870 		}
    871 		pid = fork();
    872 		if (pid == (pid_t)-1)  {
    873 			error("Fork (to start shell) failed on server.  "
    874 				"Please try again later.\n");
    875 			(void) pam_end(pamh, PAM_ABORT);
    876 			exit(1);
    877 		}
    878 		if (pid) {
    879 			fd_set ready;
    880 			fd_set readfrom;
    881 
    882 			(void) close(STDIN_FILENO);
    883 			(void) close(STDOUT_FILENO);
    884 			(void) close(STDERR_FILENO);
    885 			(void) close(pv[1]);
    886 			if (do_encrypt) {
    887 				(void) close(pw[1]);
    888 				(void) close(px[0]);
    889 			} else {
    890 				(void) close(f);
    891 			}
    892 
    893 			(void) FD_ZERO(&readfrom);
    894 
    895 			FD_SET(pv[0], &readfrom);
    896 			if (do_encrypt) {
    897 				FD_SET(pw[0], &readfrom);
    898 				FD_SET(f, &readfrom);
    899 			}
    900 			if (port)
    901 				FD_SET(s, &readfrom);
    902 
    903 			/* read f (net), write to px[1] (child stdin) */
    904 			/* read pw[0] (child stdout), write to f (net) */
    905 			/* read s (alt. channel), signal child */
    906 			/* read pv[0] (child stderr), write to s */
    907 			if (ioctl(pv[0], FIONBIO, (char *)&one) == -1)
    908 				syslog(LOG_INFO, "ioctl FIONBIO: %m");
    909 			if (do_encrypt &&
    910 				ioctl(pw[0], FIONBIO, (char *)&one) == -1)
    911 				syslog(LOG_INFO, "ioctl FIONBIO: %m");
    912 			do {
    913 				ready = readfrom;
    914 				if (select(FD_SETSIZE, &ready, NULL,
    915 					NULL, NULL) < 0) {
    916 					if (errno == EINTR) {
    917 						continue;
    918 					} else {
    919 						break;
    920 					}
    921 				}
    922 				/*
    923 				 * Read from child stderr, write to net
    924 				 */
    925 				if (port && FD_ISSET(pv[0], &ready)) {
    926 					errno = 0;
    927 					cc = read(pv[0], buf, sizeof (buf));
    928 					if (cc <= 0) {
    929 						(void) shutdown(s, 2);
    930 						FD_CLR(pv[0], &readfrom);
    931 					} else {
    932 						(void) deswrite(s, buf, cc, 1);
    933 					}
    934 				}
    935 				/*
    936 				 * Read from alternate channel, signal child
    937 				 */
    938 				if (port && FD_ISSET(s, &ready)) {
    939 					if ((int)desread(s, &sig, 1, 1) <= 0)
    940 						FD_CLR(s, &readfrom);
    941 					else
    942 						(void) killpg(pid, sig);
    943 				}
    944 				/*
    945 				 * Read from child stdout, write to net
    946 				 */
    947 				if (do_encrypt && FD_ISSET(pw[0], &ready)) {
    948 					errno = 0;
    949 					cc = read(pw[0], buf, sizeof (buf));
    950 					if (cc <= 0) {
    951 						(void) shutdown(f, 2);
    952 						FD_CLR(pw[0], &readfrom);
    953 					} else {
    954 						(void) deswrite(f, buf, cc, 0);
    955 					}
    956 				}
    957 				/*
    958 				 * Read from the net, write to child stdin
    959 				 */
    960 				if (do_encrypt && FD_ISSET(f, &ready)) {
    961 					errno = 0;
    962 					cc = desread(f, buf, sizeof (buf), 0);
    963 					if (cc <= 0) {
    964 						(void) close(px[1]);
    965 						FD_CLR(f, &readfrom);
    966 					} else {
    967 						int wcc;
    968 						wcc = write(px[1], buf, cc);
    969 						if (wcc == -1) {
    970 							/*
    971 							 * pipe closed,
    972 							 * don't read any
    973 							 * more
    974 							 *
    975 							 * might check for
    976 							 * EPIPE
    977 							 */
    978 						    (void) close(px[1]);
    979 						    FD_CLR(f, &readfrom);
    980 						} else if (wcc != cc) {
    981 						    /* CSTYLED */
    982 						    syslog(LOG_INFO, gettext("only wrote %d/%d to child"),
    983 						    wcc, cc);
    984 						}
    985 					}
    986 				}
    987 			} while ((port && FD_ISSET(s, &readfrom)) ||
    988 				(port && FD_ISSET(pv[0], &readfrom)) ||
    989 				(do_encrypt && FD_ISSET(f, &readfrom)) ||
    990 				(do_encrypt && FD_ISSET(pw[0], &readfrom)));
    991 #ifdef DEBUG
    992 			syslog(LOG_INFO, "Shell process completed.");
    993 #endif /* DEBUG */
    994 			if (ccache)
    995 				(void) pam_close_session(pamh, 0);
    996 			(void) pam_end(pamh, PAM_SUCCESS);
    997 
    998 			exit(0);
    999 		} /* End of Parent block */
   1000 
   1001 		(void) setsid();	/* Should be the same as above. */
   1002 		(void) close(pv[0]);
   1003 		(void) dup2(pv[1], 2);
   1004 		(void) close(pv[1]);
   1005 		if (port)
   1006 			(void) close(s);
   1007 		if (do_encrypt) {
   1008 			(void) close(f);
   1009 			(void) close(pw[0]);
   1010 			(void) close(px[1]);
   1011 
   1012 			(void) dup2(px[0], 0);
   1013 			(void) dup2(pw[1], 1);
   1014 
   1015 			(void) close(px[0]);
   1016 			(void) close(pw[1]);
   1017 		}
   1018 	}
   1019 
   1020 	if (*pwd->pw_shell == '\0')
   1021 		pwd->pw_shell = "/bin/sh";
   1022 	if (!do_encrypt)
   1023 		(void) close(f);
   1024 	/*
   1025 	 * write audit record before making uid switch
   1026 	 */
   1027 	(void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */
   1028 
   1029 	/* set the real (and effective) GID */
   1030 	if (setgid(pwd->pw_gid) == -1) {
   1031 		error("Invalid gid.\n");
   1032 		(void) pam_end(pamh, PAM_ABORT);
   1033 		exit(1);
   1034 	}
   1035 
   1036 	/*
   1037 	 * Initialize the supplementary group access list.
   1038 	 */
   1039 	if (strlen(locuser) == 0) {
   1040 		error("No local user.\n");
   1041 		(void) pam_end(pamh, PAM_ABORT);
   1042 		exit(1);
   1043 	}
   1044 	if (initgroups(locuser, pwd->pw_gid) == -1) {
   1045 		error("Initgroup failed.\n");
   1046 		(void) pam_end(pamh, PAM_ABORT);
   1047 		exit(1);
   1048 	}
   1049 
   1050 	if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
   1051 		error("Insufficient credentials.\n");
   1052 		(void) pam_end(pamh, v);
   1053 		exit(1);
   1054 	}
   1055 
   1056 	/* set the real (and effective) UID */
   1057 	if (setuid(pwd->pw_uid) == -1) {
   1058 		error("Invalid uid.\n");
   1059 		(void) pam_end(pamh, PAM_ABORT);
   1060 		exit(1);
   1061 	}
   1062 
   1063 	/* Change directory only after becoming the appropriate user. */
   1064 	if (chdir(pwd->pw_dir) < 0) {
   1065 		(void) chdir("/");
   1066 		if (krb5auth_flag > 0) {
   1067 			syslog(LOG_ERR, "Principal %s  (%s@%s) for local user"
   1068 				" %s has no home directory.",
   1069 				kremuser, remuser, hostname, locuser);
   1070 			error("No remote directory.\n");
   1071 			goto signout;
   1072 		}
   1073 #ifdef notdef
   1074 		error("No remote directory.\n");
   1075 		exit(1);
   1076 #endif
   1077 	}
   1078 
   1079 	path = (pwd->pw_uid == 0) ? rootpath : userpath;
   1080 
   1081 	/*
   1082 	 * Space for the following environ