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