Home | History | Annotate | Download | only in usr.bin
      1     0  stevel /*
      2  8175   Peter  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3     0  stevel  * Use is subject to license terms.
      4     0  stevel  */
      5     0  stevel 
      6     0  stevel /*
      7     0  stevel  * Copyright (c) 1983 Regents of the University of California.
      8     0  stevel  * All rights reserved.  The Berkeley software License Agreement
      9     0  stevel  * specifies the terms and conditions for redistribution.
     10     0  stevel  *
     11     0  stevel  */
     12     0  stevel 
     13     0  stevel #define	_FILE_OFFSET_BITS 64
     14     0  stevel 
     15     0  stevel #include <sys/time.h>
     16     0  stevel #include <sys/types.h>
     17     0  stevel #include <sys/socket.h>
     18     0  stevel #include <sys/ioctl.h>
     19     0  stevel /* just for FIONBIO ... */
     20     0  stevel #include <sys/filio.h>
     21     0  stevel #include <sys/stat.h>
     22     0  stevel #include <sys/select.h>
     23     0  stevel 
     24     0  stevel #include <netinet/in.h>
     25     0  stevel 
     26     0  stevel #include <assert.h>
     27     0  stevel #include <fcntl.h>
     28     0  stevel #include <stdlib.h>
     29     0  stevel #include <string.h>
     30     0  stevel #include <unistd.h>
     31     0  stevel #include <stdio.h>
     32     0  stevel #include <errno.h>
     33     0  stevel #include <signal.h>
     34     0  stevel #include <pwd.h>
     35     0  stevel #include <netdb.h>
     36     0  stevel #include <locale.h>
     37     0  stevel #include <priv_utils.h>
     38     0  stevel 
     39     0  stevel #include <k5-int.h>
     40     0  stevel #include <profile/prof_int.h>
     41     0  stevel #include <com_err.h>
     42     0  stevel #include <kcmd.h>
     43     0  stevel #include <krb5.h>
     44     0  stevel 
     45     0  stevel /* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */
     46     0  stevel typedef void (*sigdisp_t)(int);
     47     0  stevel 
     48     0  stevel extern errcode_t	profile_get_options_boolean(profile_t, char **,
     49     0  stevel     profile_options_boolean *);
     50     0  stevel extern errcode_t	profile_get_options_string(profile_t, char **,
     51     0  stevel     profile_option_strings *);
     52     0  stevel 
     53     0  stevel #define	RSH_BUFSIZ (1024 * 50)
     54     0  stevel 
     55     0  stevel static char des_inbuf[2 * RSH_BUFSIZ];	/* needs to be > largest read size */
     56     0  stevel static char des_outbuf[2 * RSH_BUFSIZ];	/* needs to be > largest write size */
     57     0  stevel static krb5_data desinbuf, desoutbuf;
     58     0  stevel static krb5_encrypt_block eblock;	/* eblock for encrypt/decrypt */
     59  8175   Peter static krb5_context bsd_context = NULL;
     60     0  stevel static krb5_auth_context auth_context;
     61     0  stevel static krb5_creds *cred;
     62     0  stevel static krb5_keyblock *session_key;
     63     0  stevel 
     64     0  stevel static int encrypt_flag;	/* Flag set, when encryption is used */
     65  8175   Peter static int krb5auth_flag;	/* Flag set, when KERBEROS is enabled */
     66  8175   Peter static profile_options_boolean autologin_option[] = {
     67  8175   Peter 	{ "autologin", &krb5auth_flag, 0 },
     68  8175   Peter 	{ NULL, NULL, 0 }
     69  8175   Peter };
     70  8175   Peter 
     71  8175   Peter static int no_krb5auth_flag = 0;
     72     0  stevel static int fflag;	/* Flag set, if creds to be fwd'ed via -f */
     73     0  stevel static int Fflag;	/* Flag set, if fwd'able creds to be fwd'ed via -F */
     74     0  stevel 
     75     0  stevel /* Flag set, if -PN / -PO is specified */
     76     0  stevel static boolean_t rcmdoption_done;
     77     0  stevel 
     78     0  stevel /* Flags set, if corres. cmd line options are turned on */
     79     0  stevel static boolean_t encrypt_done, fwd_done, fwdable_done;
     80     0  stevel 
     81     0  stevel static profile_options_boolean option[] = {
     82     0  stevel 	{ "encrypt", &encrypt_flag, 0 },
     83     0  stevel 	{ "forward", &fflag, 0 },
     84     0  stevel 	{ "forwardable", &Fflag, 0 },
     85     0  stevel 	{ NULL, NULL, 0 }
     86     0  stevel };
     87     0  stevel 
     88     0  stevel static char *rcmdproto;
     89     0  stevel static profile_option_strings rcmdversion[] = {
     90     0  stevel 	{ "rcmd_protocol", &rcmdproto, 0 },
     91     0  stevel 	{ NULL, NULL, 0 }
     92     0  stevel };
     93     0  stevel 
     94     0  stevel static char *realmdef[] = { "realms", NULL, "rsh", NULL };
     95     0  stevel static char *appdef[] = { "appdefaults", "rsh", NULL };
     96     0  stevel 
     97     0  stevel static void sendsig(int);
     98     0  stevel static sigdisp_t sigdisp(int);
     99     0  stevel static boolean_t init_service(boolean_t);
    100     0  stevel static int desrshread(int, char *, int);
    101     0  stevel static int desrshwrite(int, char *, int);
    102     0  stevel 
    103     0  stevel static int		options;
    104     0  stevel static int		rfd2;
    105     0  stevel static int		portnumber;
    106     0  stevel 
    107     0  stevel static const char	rlogin_path[] = "/usr/bin/rlogin";
    108     0  stevel static const char	dash_x[] = "-x ";	/* Note the blank after -x */
    109     0  stevel 
    110     0  stevel static boolean_t readiv, writeiv;
    111     0  stevel 
    112     0  stevel #define	set2mask(setp)	((setp)->__sigbits[0])
    113     0  stevel #define	mask2set(mask, setp) \
    114     0  stevel 	((mask) == -1 ? sigfillset(setp) : (set2mask(setp) = (mask)))
    115     0  stevel 
    116     0  stevel #ifdef DEBUG
    117     0  stevel #define	DEBUGOPTSTRING	"D:"
    118     0  stevel #else
    119     0  stevel #define	DEBUGOPTSTRING	""
    120     0  stevel #endif	/* DEBUG */
    121     0  stevel 
    122     0  stevel static void
    123     0  stevel sigsetmask(int mask)
    124     0  stevel {
    125     0  stevel 	sigset_t	nset;
    126     0  stevel 
    127     0  stevel 	(void) sigprocmask(0, NULL, &nset);
    128     0  stevel 	mask2set(mask, &nset);
    129     0  stevel 	(void) sigprocmask(SIG_SETMASK, &nset, NULL);
    130     0  stevel }
    131     0  stevel 
    132     0  stevel static int
    133     0  stevel sigblock(int mask)
    134     0  stevel {
    135     0  stevel 	sigset_t oset;
    136     0  stevel 	sigset_t nset;
    137     0  stevel 
    138     0  stevel 	(void) sigprocmask(0, NULL, &nset);
    139     0  stevel 	mask2set(mask, &nset);
    140     0  stevel 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
    141     0  stevel 	return (set2mask(&oset));
    142     0  stevel }
    143     0  stevel 
    144     0  stevel /*
    145     0  stevel  * Get signal disposition (or signal handler) for a given signal
    146     0  stevel  */
    147     0  stevel static sigdisp_t
    148     0  stevel sigdisp(int sig)
    149     0  stevel {
    150     0  stevel 	struct sigaction act;
    151     0  stevel 
    152     0  stevel 	act.sa_handler = NULL;
    153     0  stevel 	act.sa_flags = 0;
    154     0  stevel 	(void) sigemptyset(&act.sa_mask);
    155     0  stevel 	(void) sigaction(sig, NULL, &act);
    156     0  stevel 	return (act.sa_handler);
    157     0  stevel }
    158     0  stevel 
    159     0  stevel static pid_t child_pid = -1;
    160     0  stevel 
    161     0  stevel /*
    162     0  stevel  * If you do a command like "rsh host output | wc"
    163     0  stevel  * and wc terminates, then the parent will receive SIGPIPE
    164     0  stevel  * and the child needs to be terminated.
    165     0  stevel  */
    166     0  stevel /* ARGSUSED */
    167     0  stevel static void
    168     0  stevel sigpipehandler(int signal)
    169     0  stevel {
    170     0  stevel 	if (child_pid != -1)
    171     0  stevel 		(void) kill(child_pid, SIGKILL);
    172     0  stevel 	exit(EXIT_SUCCESS);
    173     0  stevel }
    174     0  stevel 
    175     0  stevel #define	mask(s)	(1 << ((s) - 1))
    176     0  stevel 
    177     0  stevel static void
    178     0  stevel usage(void) {
    179     0  stevel 	(void) fprintf(stderr, "%s\n%s\n",
    180     0  stevel 	    gettext("usage: rsh [ -PN / -PO ] [ -l login ] [ -n ] "
    181     0  stevel 		"[ -k realm ] [ -a ] [ -x ] [ -f / -F ] host command"),
    182     0  stevel 	    gettext("       rsh [ -PN / -PO ] [ -l login ] [ -k realm ] "
    183     0  stevel 		"[ -a ] [ -x ] [ -f / -F ] host"));
    184     0  stevel 	exit(EXIT_FAILURE);
    185     0  stevel }
    186     0  stevel 
    187     0  stevel static void
    188     0  stevel die(const char *message)
    189     0  stevel {
    190     0  stevel 	(void) fputs(message, stderr);
    191     0  stevel 	usage();
    192     0  stevel }
    193     0  stevel 
    194     0  stevel static void
    195     0  stevel usage_forward(void)
    196     0  stevel {
    197     0  stevel 	die(gettext("rsh: Only one of -f and -F allowed.\n"));
    198     0  stevel }
    199     0  stevel 
    200     0  stevel /*
    201     0  stevel  * rsh - remote shell
    202     0  stevel  */
    203     0  stevel /* VARARGS */
    204     0  stevel int
    205     0  stevel main(int argc, char **argv)
    206     0  stevel {
    207     0  stevel 	int c, rem;
    208     0  stevel 	char *cmd, *cp, **ap, buf[RSH_BUFSIZ], **argv0, *args, *args_no_x;
    209     0  stevel 	char *host = NULL, *user = NULL;
    210     0  stevel 	int cc;
    211     0  stevel 	boolean_t asrsh = B_FALSE;
    212     0  stevel 	struct passwd *pwd;
    213     0  stevel 	boolean_t readfrom_rem;
    214     0  stevel 	boolean_t readfrom_rfd2;
    215     0  stevel 	int one = 1;
    216     0  stevel 	int omask;
    217     0  stevel 	boolean_t nflag = B_FALSE;
    218     0  stevel 	char *krb_realm = NULL;
    219     0  stevel 	krb5_flags authopts;
    220     0  stevel 	krb5_error_code status;
    221     0  stevel 	enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
    222     0  stevel 	uid_t uid = getuid();
    223     0  stevel 
    224     0  stevel 	c = (argc + 1) * sizeof (char *);
    225     0  stevel 	if ((argv0 = malloc(c)) == NULL) {
    226     0  stevel 		perror("malloc");
    227     0  stevel 		return (EXIT_FAILURE);
    228     0  stevel 	}
    229     0  stevel 	(void) memcpy(argv0, argv, c);
    230     0  stevel 
    231     0  stevel 	(void) setlocale(LC_ALL, "");
    232     0  stevel 
    233     0  stevel 	(void) textdomain(TEXT_DOMAIN);
    234     0  stevel 
    235     0  stevel 	/*
    236     0  stevel 	 * Determine command name used to invoke to rlogin(1). Users can
    237     0  stevel 	 * create links named by a host pointing to the binary and type
    238     0  stevel 	 * "hostname" to log into that host afterwards.
    239     0  stevel 	 */
    240     0  stevel 	cmd = strrchr(argv[0], '/');
    241     0  stevel 	cmd = (cmd != NULL) ? (cmd + 1) : argv[0];
    242     0  stevel 
    243     0  stevel 	/*
    244     0  stevel 	 *	Add "remsh" as an alias for "rsh" (System III, V networking
    245     0  stevel 	 *	add-ons often used this name for the remote shell since rsh
    246     0  stevel 	 *	was already taken for the restricted shell).  Note that this
    247     0  stevel 	 *	usurps the ability to use "remsh" as the name of a host (by
    248     0  stevel 	 *	symlinking it to rsh), so we go one step farther:  if the
    249     0  stevel 	 *	file "/usr/bin/remsh" does not exist, we behave as if "remsh"
    250     0  stevel 	 *	is a host name.  If it does exist, we accept "remsh" as an
    251     0  stevel 	 *	"rsh" alias.
    252     0  stevel 	 */
    253     0  stevel 	if (strcmp(cmd, "remsh") == 0) {
    254     0  stevel 		struct stat sb;
    255     0  stevel 
    256     0  stevel 		if (stat("/usr/bin/remsh", &sb) < 0)
    257     0  stevel 			host = cmd;
    258     0  stevel 	} else if (strcmp(cmd, "rsh") != 0) {
    259     0  stevel 		host = cmd;
    260     0  stevel 	}
    261     0  stevel 
    262     0  stevel 	/* Handle legacy synopsis "rsh hostname options [command]". */
    263     0  stevel 	if (host == NULL) {
    264     0  stevel 		if (argc < 2)
    265     0  stevel 			usage();
    266     0  stevel 		if (*argv[1] != '-') {
    267     0  stevel 			host = argv[1];
    268     0  stevel 			argc--;
    269     0  stevel 			argv[1] = argv[0];
    270     0  stevel 			argv++;
    271     0  stevel 			asrsh = B_TRUE;
    272     0  stevel 		}
    273     0  stevel 	}
    274     0  stevel 
    275     0  stevel 	while ((c = getopt(argc, argv,
    276  8175   Peter 	    DEBUGOPTSTRING "8AFKLP:ade:fk:l:nwx")) != -1) {
    277     0  stevel 		switch (c) {
    278     0  stevel #ifdef DEBUG
    279     0  stevel 		case 'D':
    280     0  stevel 			portnumber = htons(atoi(optarg));
    281  8175   Peter 			krb5auth_flag++;
    282     0  stevel 			break;
    283     0  stevel #endif /* DEBUG */
    284     0  stevel 		case 'F':
    285     0  stevel 			if (fflag)
    286     0  stevel 				usage_forward();
    287     0  stevel 			Fflag = 1;
    288  8175   Peter 			krb5auth_flag++;
    289     0  stevel 			fwdable_done = B_TRUE;
    290     0  stevel 			break;
    291     0  stevel 		case 'f':
    292     0  stevel 			if (Fflag)
    293     0  stevel 				usage_forward();
    294     0  stevel 			fflag = 1;
    295  8175   Peter 			krb5auth_flag++;
    296     0  stevel 			fwd_done = B_TRUE;
    297     0  stevel 			break;
    298     0  stevel 		case 'P':
    299     0  stevel 			if (strcmp(optarg, "N") == 0)
    300     0  stevel 				kcmd_proto = KCMD_NEW_PROTOCOL;
    301     0  stevel 			else if (strcmp(optarg, "O") == 0)
    302     0  stevel 				kcmd_proto = KCMD_OLD_PROTOCOL;
    303     0  stevel 			else
    304     0  stevel 				die(gettext("rsh: Only -PN or -PO "
    305     0  stevel 				    "allowed.\n"));
    306     0  stevel 			if (rcmdoption_done)
    307     0  stevel 				die(gettext("rsh: Only one of -PN and -PO "
    308     0  stevel 				    "allowed.\n"));
    309     0  stevel 			rcmdoption_done = B_TRUE;
    310  8175   Peter 			krb5auth_flag++;
    311     0  stevel 			break;
    312     0  stevel 		case 'a':
    313  8175   Peter 			krb5auth_flag++;
    314  8175   Peter 			break;
    315  8175   Peter 		case 'K':
    316  8175   Peter 			no_krb5auth_flag++;
    317     0  stevel 			break;
    318     0  stevel 		case 'd':
    319     0  stevel 			options |= SO_DEBUG;
    320     0  stevel 			break;
    321     0  stevel 		case 'k':
    322     0  stevel 			krb_realm = optarg;
    323  8175   Peter 			krb5auth_flag++;
    324     0  stevel 			break;
    325     0  stevel 		case 'l':
    326     0  stevel 			user = optarg;
    327     0  stevel 			break;
    328     0  stevel 		case 'n':
    329     0  stevel 			if (!nflag) {
    330     0  stevel 				if (close(STDIN_FILENO) < 0) {
    331     0  stevel 					perror("close");
    332     0  stevel 					return (EXIT_FAILURE);
    333     0  stevel 				}
    334     0  stevel 				/*
    335     0  stevel 				 * "STDION_FILENO" defined to 0 by POSIX
    336     0  stevel 				 * and hence the lowest file descriptor.
    337     0  stevel 				 * So the open(2) below is guaranteed to
    338     0  stevel 				 * reopen it because we closed it above.
    339     0  stevel 				 */
    340     0  stevel 				if (open("/dev/null", O_RDONLY) < 0) {
    341     0  stevel 					perror("open");
    342     0  stevel 					return (EXIT_FAILURE);
    343     0  stevel 				}
    344     0  stevel 				nflag = B_TRUE;
    345     0  stevel 			}
    346     0  stevel 			break;
    347     0  stevel 		case 'x':
    348     0  stevel 			encrypt_flag = 1;
    349  8175   Peter 			krb5auth_flag++;
    350     0  stevel 			encrypt_done = B_TRUE;
    351     0  stevel 			break;
    352     0  stevel 		/*
    353     0  stevel 		 * Ignore the -L, -w, -e and -8 flags to allow aliases with
    354     0  stevel 		 * rlogin to work. Actually rlogin(1) doesn't understand
    355     0  stevel 		 * -w either but because "rsh -w hostname command" used
    356     0  stevel 		 * to work we still accept it.
    357     0  stevel 		 */
    358     0  stevel 		case '8':
    359     0  stevel 		case 'L':
    360     0  stevel 		case 'e':
    361     0  stevel 		case 'w':
    362     0  stevel 		/*
    363     0  stevel 		 * On the lines of the -L, -w, -e and -8 options above, we
    364     0  stevel 		 * ignore the -A option too, in order to allow aliases with
    365     0  stevel 		 * rlogin to work.
    366     0  stevel 		 *
    367     0  stevel 		 * Mind you !, the -a option to trigger Kerberos authentication
    368     0  stevel 		 * in rsh, has a totally different usage in rlogin, its the
    369     0  stevel 		 * -A option (in rlogin) which needs to be used to talk
    370     0  stevel 		 * Kerberos.
    371     0  stevel 		 */
    372     0  stevel 		case 'A':
    373     0  stevel 			break;
    374     0  stevel 		default:
    375     0  stevel 			usage();
    376     0  stevel 		}
    377     0  stevel 	}
    378     0  stevel 
    379     0  stevel 	argc -= optind;
    380     0  stevel 	argv += optind;
    381     0  stevel 
    382     0  stevel 	if (host == NULL) {
    383     0  stevel 		if (argc == 0)
    384     0  stevel 			usage();
    385     0  stevel 		argc--;
    386     0  stevel 		host = *argv++;
    387     0  stevel 		asrsh = B_TRUE;
    388     0  stevel 	}
    389     0  stevel 
    390     0  stevel 	if (argc == 0) {
    391     0  stevel 		(void) setreuid(uid, uid);
    392     0  stevel 		if (nflag)
    393     0  stevel 			usage();
    394     0  stevel 		if (asrsh)
    395     0  stevel 			*argv0 = "rlogin";
    396     0  stevel 		(void) execv(rlogin_path, argv0);
    397     0  stevel 		perror(rlogin_path);
    398     0  stevel 
    399     0  stevel 		(void) fprintf(stderr, gettext("No local rlogin "
    400     0  stevel 				"program found.\n"));
    401     0  stevel 		return (EXIT_FAILURE);
    402     0  stevel 	}
    403     0  stevel 
    404     0  stevel 	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
    405     0  stevel 		(void) fprintf(stderr,
    406     0  stevel 		    gettext("Insufficient privileges, "
    407     0  stevel 			"rsh must be set-uid root\n"));
    408     0  stevel 		return (EXIT_FAILURE);
    409     0  stevel 	}
    410     0  stevel 
    411     0  stevel 	pwd = getpwuid(uid);
    412     0  stevel 	if (pwd == NULL) {
    413     0  stevel 		(void) fprintf(stderr, gettext("who are you?\n"));
    414     0  stevel 		return (EXIT_FAILURE);
    415     0  stevel 	}
    416     0  stevel 	if (user == NULL)
    417     0  stevel 		user = pwd->pw_name;
    418     0  stevel 
    419  8175   Peter 	/*
    420  8175   Peter 	 * if the user disables krb5 on the cmdline (-K), then skip
    421  8175   Peter 	 * all krb5 setup.
    422  8175   Peter 	 *
    423  8175   Peter 	 * if the user does not disable krb5 or enable krb5 on the
    424  8175   Peter 	 * cmdline, check krb5.conf to see if it should be enabled.
    425  8175   Peter 	 */
    426  8175   Peter 
    427  8175   Peter 	if (no_krb5auth_flag) {
    428  8175   Peter 		krb5auth_flag = 0;
    429  8175   Peter 		Fflag = fflag = encrypt_flag = 0;
    430  8175   Peter 	} else if (!krb5auth_flag) {
    431  8175   Peter 		/* is autologin set in krb5.conf? */
    432  8175   Peter 		status = krb5_init_context(&bsd_context);
    433  8175   Peter 		/* don't sweat failure here */
    434  8175   Peter 		if (!status) {
    435  8175   Peter 			/*
    436  8175   Peter 			 * note that the call to profile_get_options_boolean
    437  8175   Peter 			 * with autologin_option can affect value of
    438  8175   Peter 			 * krb5auth_flag
    439  8175   Peter 			 */
    440  8175   Peter 			(void) profile_get_options_boolean(bsd_context->profile,
    441  8175   Peter 							appdef,
    442  8175   Peter 							autologin_option);
    443  8175   Peter 		}
    444  8175   Peter 	}
    445  8175   Peter 
    446     0  stevel 	if (krb5auth_flag) {
    447  8175   Peter 		if (!bsd_context) {
    448  8175   Peter 			status = krb5_init_context(&bsd_context);
    449  8175   Peter 			if (status) {
    450  8175   Peter 				com_err("rsh", status,
    451  8175   Peter 				    "while initializing krb5");
    452  8175   Peter 				return (EXIT_FAILURE);
    453  8175   Peter 
    454  8175   Peter 			}
    455     0  stevel 		}
    456     0  stevel 
    457     0  stevel 		/*
    458     0  stevel 		 * Get our local realm to look up local realm options.
    459     0  stevel 		 */
    460     0  stevel 		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
    461     0  stevel 		if (status) {
    462     0  stevel 			com_err("rsh", status,
    463     0  stevel 				gettext("while getting default realm"));
    464     0  stevel 			return (EXIT_FAILURE);
    465     0  stevel 		}
    466     0  stevel 		/*
    467     0  stevel 		 * Check the realms section in krb5.conf for encryption,
    468     0  stevel 		 * forward & forwardable info
    469     0  stevel 		 */
    470     0  stevel 		profile_get_options_boolean(bsd_context->profile, realmdef,
    471     0  stevel 						option);
    472     0  stevel 		/*
    473     0  stevel 		 * Check the appdefaults section
    474     0  stevel 		 */
    475     0  stevel 		profile_get_options_boolean(bsd_context->profile, appdef,
    476     0  stevel 						option);
    477     0  stevel 		profile_get_options_string(bsd_context->profile, appdef,
    478     0  stevel 						rcmdversion);
    479     0  stevel 		/*
    480     0  stevel 		 * Set the *_flag variables, if the corresponding *_done are
    481     0  stevel 		 * set to 1, because we dont want the config file values
    482     0  stevel 		 * overriding the command line options.
    483     0  stevel 		 */
    484     0  stevel 		if (encrypt_done)
    485     0  stevel 			encrypt_flag = 1;
    486     0  stevel 		if (fwd_done) {
    487     0  stevel 			fflag = 1;
    488     0  stevel 			Fflag = 0;
    489     0  stevel 		} else if (fwdable_done) {
    490     0  stevel 			Fflag = 1;
    491     0  stevel 			fflag = 0;
    492     0  stevel 		}
    493     0  stevel 		if (!rcmdoption_done && (rcmdproto != NULL)) {
    494     0  stevel 			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
    495     0  stevel 				kcmd_proto = KCMD_NEW_PROTOCOL;
    496     0  stevel 			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
    497     0  stevel 				kcmd_proto = KCMD_OLD_PROTOCOL;
    498     0  stevel 			} else {
    499     0  stevel 				(void) fprintf(stderr, gettext("Unrecognized "
    500     0  stevel 					"KCMD protocol (%s)"), rcmdproto);
    501     0  stevel 				return (EXIT_FAILURE);
    502     0  stevel 			}
    503     0  stevel 		}
    504     0  stevel 
    505     0  stevel 
    506     0  stevel 		if (encrypt_flag && (!krb5_privacy_allowed())) {
    507     0  stevel 			(void) fprintf(stderr, gettext("rsh: Encryption not "
    508     0  stevel 				"supported.\n"));
    509     0  stevel 			return (EXIT_FAILURE);
    510     0  stevel 		}
    511     0  stevel 	}
    512     0  stevel 
    513     0  stevel 	/*
    514     0  stevel 	 * Connect with the service (shell/kshell) on the daemon side
    515     0  stevel 	 */
    516     0  stevel 	if (portnumber == 0) {
    517     0  stevel 		while (!init_service(krb5auth_flag)) {
    518     0  stevel 			/*
    519     0  stevel 			 * Connecting to the 'kshell' service failed,
    520     0  stevel 			 * fallback to normal rsh; Reset all KRB5 flags
    521     0  stevel 			 * and connect to 'shell' service on the server
    522     0  stevel 			 */
    523  8175   Peter 			krb5auth_flag = 0;
    524     0  stevel 			encrypt_flag = fflag = Fflag = 0;
    525     0  stevel 		}
    526     0  stevel 	}
    527     0  stevel 
    528     0  stevel 	cc = encrypt_flag ? strlen(dash_x) : 0;
    529     0  stevel 	for (ap = argv; *ap != NULL; ap++)
    530     0  stevel 		cc += strlen(*ap) + 1;
    531     0  stevel 	cp = args = malloc(cc);
    532     0  stevel 	if (cp == NULL)
    533     0  stevel 		perror("malloc");
    534     0  stevel 	if (encrypt_flag) {
    535     0  stevel 		int length;
    536     0  stevel 
    537     0  stevel 		length = strlcpy(args, dash_x, cc);
    538     0  stevel 		cp += length;
    539     0  stevel 		cc -= length;
    540     0  stevel 	}
    541     0  stevel 	args_no_x = args;
    542     0  stevel 
    543     0  stevel 	for (ap = argv; *ap != NULL; ap++) {
    544     0  stevel 		int length;
    545     0  stevel 
    546     0  stevel 		length = strlcpy(cp, *ap, cc);
    547     0  stevel 		assert(length < cc);
    548     0  stevel 		cp += length;
    549     0  stevel 		cc -= length;
    550     0  stevel 		if (ap[1] != NULL) {
    551     0  stevel 			*cp++ = ' ';
    552     0  stevel 			cc--;
    553     0  stevel 		}
    554     0  stevel 	}
    555     0  stevel 
    556     0  stevel 	if (krb5auth_flag) {
    557     0  stevel 		authopts = AP_OPTS_MUTUAL_REQUIRED;
    558     0  stevel 		/*
    559     0  stevel 		 * Piggy-back forwarding flags on top of authopts;
    560     0  stevel 		 * they will be reset in kcmd
    561     0  stevel 		 */
    562     0  stevel 		if (fflag || Fflag)
    563     0  stevel 			authopts |= OPTS_FORWARD_CREDS;
    564     0  stevel 		if (Fflag)
    565     0  stevel 			authopts |= OPTS_FORWARDABLE_CREDS;
    566     0  stevel 
    567     0  stevel 		status = kcmd(&rem, &host, portnumber,
    568     0  stevel 				pwd->pw_name, user,
    569     0  stevel 				args, &rfd2, "host", krb_realm,
    570     0  stevel 				bsd_context, &auth_context, &cred,
    571     0  stevel 				NULL,	/* No need for sequence number */
    572     0  stevel 				NULL,	/* No need for server seq # */
    573     0  stevel 				authopts,
    574     0  stevel 				1,	/* Always set anyport */
    575     0  stevel 				&kcmd_proto);
    576     0  stevel 		if (status != 0) {
    577     0  stevel 			/*
    578     0  stevel 			 * If new protocol requested, we dont fallback to
    579     0  stevel 			 * less secure ones.
    580     0  stevel 			 */
    581     0  stevel 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
    582     0  stevel 				(void) fprintf(stderr, gettext("rsh: kcmdv2 "
    583     0  stevel 					"to host %s failed - %s\n"
    584     0  stevel 					"Fallback to normal rsh denied."),
    585     0  stevel 					host, error_message(status));
    586     0  stevel 				return (EXIT_FAILURE);
    587     0  stevel 			}
    588     0  stevel 			/* check NO_TKT_FILE or equivalent... */
    589     0  stevel 			if (status != -1) {
    590     0  stevel 				(void) fprintf(stderr,
    591     0  stevel 				gettext("rsh: kcmd to host %s failed - %s\n"
    592     0  stevel 				"trying normal rsh...\n\n"),
    593     0  stevel 				host, error_message(status));
    594     0  stevel 			} else {
    595     0  stevel 				(void) fprintf(stderr,
    596     0  stevel 					gettext("trying normal rsh...\n"));
    597     0  stevel 			}
    598     0  stevel 			/*
    599     0  stevel 			 * kcmd() failed, so we now fallback to normal rsh,
    600     0  stevel 			 * after resetting the KRB5 flags and the 'args' array
    601     0  stevel 			 */
    602  8175   Peter 			krb5auth_flag = 0;
    603     0  stevel 			encrypt_flag = fflag = Fflag = 0;
    604     0  stevel 			args = args_no_x;
    605     0  stevel 			(void) init_service(B_FALSE);
    606     0  stevel 		} else {
    607     0  stevel 			/*
    608     0  stevel 			 * Set up buffers for desread and deswrite.
    609     0  stevel 			 */
    610     0  stevel 			desinbuf.data = des_inbuf;
    611     0  stevel 			desoutbuf.data = des_outbuf;
    612     0  stevel 			desinbuf.length = sizeof (des_inbuf);
    613     0  stevel 			desoutbuf.length = sizeof (des_outbuf);
    614     0  stevel 
    615     0  stevel 			session_key = &cred->keyblock;
    616     0  stevel 
    617     0  stevel 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
    618     0  stevel 				status = krb5_auth_con_getlocalsubkey(
    619     0  stevel 				    bsd_context,
    620     0  stevel 				    auth_context,
    621     0  stevel 				    &session_key);
    622     0  stevel 				if (status) {
    623     0  stevel 					com_err("rsh", status,
    624     0  stevel 					    "determining subkey for session");
    625     0  stevel 					return (EXIT_FAILURE);
    626     0  stevel 				}
    627     0  stevel 				if (session_key == NULL) {
    628     0  stevel 					com_err("rsh", 0, "no subkey "
    629     0  stevel 					    "negotiated for connection");
    630     0  stevel 					return (EXIT_FAILURE);
    631     0  stevel 				}
    632     0  stevel 			}
    633     0  stevel 
    634     0  stevel 			eblock.crypto_entry = session_key->enctype;
    635     0  stevel 			eblock.key = (krb5_keyblock *)session_key;
    636     0  stevel 
    637     0  stevel 			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
    638     0  stevel 			    &desinbuf, &desoutbuf, CLIENT, &eblock);
    639     0  stevel 			if (encrypt_flag) {
    640     0  stevel 				char *s = gettext("This rsh session is using "
    641     0  stevel 				    "encryption for all data transmissions.");
    642     0  stevel 				(void) write(STDERR_FILENO, s, strlen(s));
    643     0  stevel 				(void) write(STDERR_FILENO, "\r\n", 2);
    644     0  stevel 			}
    645     0  stevel 		}
    646     0  stevel 	}
    647     0  stevel 
    648     0  stevel 	/*
    649     0  stevel 	 * Don't merge this with the "if" statement above because
    650     0  stevel 	 * "krb5auth_flag" might be set to false inside it.
    651     0  stevel 	 */
    652     0  stevel 	if (!krb5auth_flag) {
    653     0  stevel 		rem = rcmd_af(&host, portnumber, pwd->pw_name, user, args,
    654     0  stevel 		    &rfd2, AF_INET6);
    655     0  stevel 		if (rem < 0)
    656     0  stevel 			return (EXIT_FAILURE);
    657     0  stevel 	}
    658     0  stevel 	__priv_relinquish();
    659     0  stevel 
    660     0  stevel 	if (rfd2 < 0) {
    661     0  stevel 		(void) fprintf(stderr, gettext("rsh: can't establish "
    662     0  stevel 				"stderr\n"));
    663     0  stevel 		return (EXIT_FAILURE);
    664     0  stevel 	}
    665     0  stevel 	if (options & SO_DEBUG) {
    666     0  stevel 		if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&one,
    667     0  stevel 		    sizeof (one)) < 0)
    668     0  stevel 			perror("rsh: setsockopt (stdin)");
    669     0  stevel 		if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, (char *)&one,
    670     0  stevel 		    sizeof (one)) < 0)
    671     0  stevel 			perror("rsh: setsockopt (stderr)");
    672     0  stevel 	}
    673     0  stevel 	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
    674     0  stevel 
    675     0  stevel 	if (sigdisp(SIGINT) != SIG_IGN)
    676     0  stevel 		(void) sigset(SIGINT, sendsig);
    677     0  stevel 	if (sigdisp(SIGQUIT) != SIG_IGN)
    678     0  stevel 		(void) sigset(SIGQUIT, sendsig);
    679     0  stevel 	if (sigdisp(SIGTERM) != SIG_IGN)
    680     0  stevel 		(void) sigset(SIGTERM, sendsig);
    681     0  stevel 
    682     0  stevel 	if (nflag) {
    683     0  stevel 		(void) shutdown(rem, SHUT_WR);
    684     0  stevel 	} else {
    685     0  stevel 		child_pid = fork();
    686     0  stevel 		if (child_pid < 0) {
    687     0  stevel 			perror("rsh: fork");
    688     0  stevel 			return (EXIT_FAILURE);
    689     0  stevel 		}
    690     0  stevel 
    691     0  stevel 		if (!encrypt_flag) {
    692     0  stevel 			(void) ioctl(rfd2, FIONBIO, &one);
    693     0  stevel 			(void) ioctl(rem, FIONBIO, &one);
    694     0  stevel 		}
    695     0  stevel 
    696     0  stevel 		if (child_pid == 0) {
    697     0  stevel 			/* Child */
    698     0  stevel 			fd_set remset;
    699     0  stevel 			char *bp;
    700     0  stevel 			int  wc;
    701     0  stevel 			(void) close(rfd2);
    702     0  stevel 		reread:
    703     0  stevel 			errno = 0;
    704     0  stevel 			cc = read(0, buf, sizeof (buf));
    705     0  stevel 			if (cc <= 0)
    706     0  stevel 				goto done;
    707     0  stevel 			bp = buf;
    708     0  stevel 		rewrite:
    709     0  stevel 			FD_ZERO(&remset);
    710     0  stevel 			FD_SET(rem, &remset);
    711     0  stevel 			if (select(rem + 1, NULL, &remset, NULL, NULL) < 0) {
    712     0  stevel 				if (errno != EINTR) {
    713     0  stevel 					perror("rsh: select");
    714     0  stevel 					return (EXIT_FAILURE);
    715     0  stevel 				}
    716     0  stevel 				goto rewrite;
    717     0  stevel 			}
    718     0  stevel 			if (!FD_ISSET(rem, &remset))
    719     0  stevel 				goto rewrite;
    720     0  stevel 			writeiv = B_FALSE;
    721     0  stevel 			wc = desrshwrite(rem, bp, cc);
    722     0  stevel 			if (wc < 0) {
    723     0  stevel 				if (errno == EWOULDBLOCK)
    724     0  stevel 					goto rewrite;
    725     0  stevel 				goto done;
    726     0  stevel 			}
    727     0  stevel 			cc -= wc; bp += wc;
    728     0  stevel 			if (cc == 0)
    729     0  stevel 				goto reread;
    730     0  stevel 			goto rewrite;
    731     0  stevel 		done:
    732     0  stevel 			(void) shutdown(rem, SHUT_WR);
    733     0  stevel 			return (EXIT_SUCCESS);
    734     0  stevel 		}
    735     0  stevel 	}
    736     0  stevel 
    737     0  stevel #define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
    738     0  stevel 
    739     0  stevel 	sigsetmask(omask);
    740     0  stevel 	readfrom_rem = B_TRUE;
    741     0  stevel 	readfrom_rfd2 = B_TRUE;
    742     0  stevel 	(void) sigset(SIGPIPE, sigpipehandler);
    743     0  stevel 	do {
    744     0  stevel 		fd_set readyset;
    745     0  stevel 
    746     0  stevel 		FD_ZERO(&readyset);
    747     0  stevel 		if (readfrom_rem)
    748     0  stevel 			FD_SET(rem, &readyset);
    749     0  stevel 		if (readfrom_rfd2)
    750     0  stevel 			FD_SET(rfd2, &readyset);
    751     0  stevel 		if (select(MAX(rem, rfd2) + 1, &readyset, NULL, NULL,
    752     0  stevel 		    NULL) < 0) {
    753     0  stevel 			if (errno != EINTR) {
    754     0  stevel 				perror("rsh: select");
    755     0  stevel 				return (EXIT_FAILURE);
    756     0  stevel 			}
    757     0  stevel 			continue;
    758     0  stevel 		}
    759     0  stevel 		if (FD_ISSET(rfd2, &readyset)) {
    760     0  stevel 			errno = 0;
    761     0  stevel 			readiv = B_TRUE;
    762     0  stevel 			cc = desrshread(rfd2, buf, sizeof (buf));
    763     0  stevel 			if (cc <= 0) {
    764     0  stevel 				if (errno != EWOULDBLOCK)
    765     0  stevel 					readfrom_rfd2 = B_FALSE;
    766     0  stevel 			} else {
    767     0  stevel 				(void) write(STDERR_FILENO, buf, cc);
    768     0  stevel 			}
    769     0  stevel 		}
    770     0  stevel 		if (FD_ISSET(rem, &readyset)) {
    771     0  stevel 			errno = 0;
    772     0  stevel 			readiv = B_FALSE;
    773     0  stevel 			cc = desrshread(rem, buf, sizeof (buf));
    774     0  stevel 			if (cc <= 0) {
    775     0  stevel 				if (errno != EWOULDBLOCK)
    776     0  stevel 					readfrom_rem = B_FALSE;
    777     0  stevel 			} else
    778     0  stevel 				(void) write(STDOUT_FILENO, buf, cc);
    779     0  stevel 		}
    780     0  stevel 	} while (readfrom_rem || readfrom_rfd2);
    781     0  stevel 
    782     0  stevel 	if (!nflag)
    783     0  stevel 		(void) kill(child_pid, SIGKILL);
    784     0  stevel 	return (EXIT_SUCCESS);
    785     0  stevel }
    786     0  stevel 
    787     0  stevel static void
    788     0  stevel sendsig(int signum)
    789     0  stevel {
    790     0  stevel 	char	buffer;
    791     0  stevel 
    792     0  stevel 	writeiv = B_TRUE;
    793     0  stevel 	buffer = (char)signum;
    794     0  stevel 	(void) desrshwrite(rfd2, &buffer, 1);
    795     0  stevel }
    796     0  stevel 
    797     0  stevel static boolean_t
    798     0  stevel init_service(boolean_t krb5flag)
    799     0  stevel {
    800     0  stevel 	struct servent *sp;
    801     0  stevel 
    802     0  stevel 	if (krb5flag) {
    803     0  stevel 		sp = getservbyname("kshell", "tcp");
    804     0  stevel 		if (sp == NULL) {
    805     0  stevel 			(void) fprintf(stderr,
    806     0  stevel 				gettext("rsh: kshell/tcp: unknown service.\n"
    807     0  stevel 				"trying normal shell/tcp service\n"));
    808     0  stevel 			return (B_FALSE);
    809     0  stevel 		}
    810     0  stevel 	} else {
    811     0  stevel 		sp = getservbyname("shell", "tcp");
    812     0  stevel 		if (sp == NULL) {
    813     0  stevel 			portnumber = htons(IPPORT_CMDSERVER);
    814     0  stevel 			return (B_TRUE);
    815     0  stevel 		}
    816     0  stevel 	}
    817     0  stevel 
    818     0  stevel 	portnumber = sp->s_port;
    819     0  stevel 	return (B_TRUE);
    820     0  stevel }
    821     0  stevel 
    822     0  stevel static int
    823     0  stevel desrshread(int fd, char *buf, int len)
    824     0  stevel {
    825     0  stevel 	return (desread(fd, buf, len, readiv ? 1 : 0));
    826     0  stevel }
    827     0  stevel 
    828     0  stevel static int
    829     0  stevel desrshwrite(int fd, char *buf, int len)
    830     0  stevel {
    831     0  stevel 	return (deswrite(fd, buf, len, writeiv ? 1 : 0));
    832     0  stevel }
    833