rsh.c revision 21673
1/*-
2 * Copyright (c) 1983, 1990, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1983, 1990, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "From: @(#)rsh.c	8.3 (Berkeley) 4/6/94";
42static char rcsid[] =
43	"$FreeBSD: head/usr.bin/rsh/rsh.c 21673 1997-01-14 07:20:47Z jkh $";
44#endif /* not lint */
45
46#include <sys/types.h>
47#include <sys/signal.h>
48#include <sys/socket.h>
49#include <sys/ioctl.h>
50#include <sys/file.h>
51#include <sys/time.h>
52
53#include <netinet/in.h>
54#include <netdb.h>
55
56#include <err.h>
57#include <errno.h>
58#include <pwd.h>
59#include <signal.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <unistd.h>
64#include <varargs.h>
65
66#include "pathnames.h"
67
68#ifdef KERBEROS
69#include <des.h>
70#include <kerberosIV/krb.h>
71
72CREDENTIALS cred;
73Key_schedule schedule;
74int use_kerberos = 1, doencrypt;
75char dst_realm_buf[REALM_SZ], *dest_realm;
76extern char *krb_realmofhost();
77#endif
78
79/*
80 * rsh - remote shell
81 */
82int	rfd2;
83
84char   *copyargs __P((char **));
85void	sendsig __P((int));
86void	talk __P((int, long, pid_t, int, int));
87void	usage __P((void));
88void	warning __P(());
89
90int
91main(argc, argv)
92	int argc;
93	char **argv;
94{
95	struct passwd *pw;
96	struct servent *sp;
97	long omask;
98	int argoff, asrsh, ch, dflag, nflag, one, rem;
99	pid_t pid;
100	uid_t uid;
101	char *args, *host, *p, *user;
102	int timeout = 0;
103
104	argoff = asrsh = dflag = nflag = 0;
105	one = 1;
106	host = user = NULL;
107
108	/* if called as something other than "rsh", use it as the host name */
109	if (p = strrchr(argv[0], '/'))
110		++p;
111	else
112		p = argv[0];
113	if (strcmp(p, "rsh"))
114		host = p;
115	else
116		asrsh = 1;
117
118	/* handle "rsh host flags" */
119	if (!host && argc > 2 && argv[1][0] != '-') {
120		host = argv[1];
121		argoff = 1;
122	}
123
124#ifdef KERBEROS
125#ifdef CRYPT
126#define	OPTIONS	"8KLde:k:l:nt:wx"
127#else
128#define	OPTIONS	"8KLde:k:l:nt:w"
129#endif
130#else
131#define	OPTIONS	"8KLde:l:nt:w"
132#endif
133	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
134		switch(ch) {
135		case 'K':
136#ifdef KERBEROS
137			use_kerberos = 0;
138#endif
139			break;
140		case 'L':	/* -8Lew are ignored to allow rlogin aliases */
141		case 'e':
142		case 'w':
143		case '8':
144			break;
145		case 'd':
146			dflag = 1;
147			break;
148		case 'l':
149			user = optarg;
150			break;
151#ifdef KERBEROS
152		case 'k':
153			dest_realm = dst_realm_buf;
154			strncpy(dest_realm, optarg, REALM_SZ);
155			break;
156#endif
157		case 'n':
158			nflag = 1;
159			break;
160#ifdef KERBEROS
161#ifdef CRYPT
162		case 'x':
163			doencrypt = 1;
164			break;
165#endif
166#endif
167		case 't':
168			timeout = atoi(optarg);
169			break;
170		case '?':
171		default:
172			usage();
173		}
174	optind += argoff;
175
176	/* if haven't gotten a host yet, do so */
177	if (!host && !(host = argv[optind++]))
178		usage();
179
180	/* if no further arguments, must have been called as rlogin. */
181	if (!argv[optind]) {
182		if (asrsh)
183			*argv = "rlogin";
184		execv(_PATH_RLOGIN, argv);
185		err(1, "can't exec %s", _PATH_RLOGIN);
186	}
187
188	argc -= optind;
189	argv += optind;
190
191	if (!(pw = getpwuid(uid = getuid())))
192		errx(1, "unknown user id");
193	if (!user)
194		user = pw->pw_name;
195
196#ifdef KERBEROS
197#ifdef CRYPT
198	/* -x turns off -n */
199	if (doencrypt)
200		nflag = 0;
201#endif
202#endif
203
204	args = copyargs(argv);
205
206	sp = NULL;
207#ifdef KERBEROS
208	if (use_kerberos) {
209		sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp");
210		if (sp == NULL) {
211			use_kerberos = 0;
212			warning("can't get entry for %s/tcp service",
213			    doencrypt ? "ekshell" : "kshell");
214		}
215	}
216#endif
217	if (sp == NULL)
218		sp = getservbyname("shell", "tcp");
219	if (sp == NULL)
220		errx(1, "shell/tcp: unknown service");
221
222#ifdef KERBEROS
223try_connect:
224	if (use_kerberos) {
225		struct hostent *hp;
226
227		/* fully qualify hostname (needed for krb_realmofhost) */
228		hp = gethostbyname(host);
229		if (hp != NULL && !(host = strdup(hp->h_name)))
230			err(1, NULL);
231
232		rem = KSUCCESS;
233		errno = 0;
234		if (dest_realm == NULL)
235			dest_realm = krb_realmofhost(host);
236
237#ifdef CRYPT
238		if (doencrypt) {
239			rem = krcmd_mutual(&host, sp->s_port, user, args,
240			    &rfd2, dest_realm, &cred, schedule);
241			des_set_key_krb(&cred.session, schedule);
242		} else
243#endif
244			rem = krcmd(&host, sp->s_port, user, args, &rfd2,
245			    dest_realm);
246		if (rem < 0) {
247			use_kerberos = 0;
248			sp = getservbyname("shell", "tcp");
249			if (sp == NULL)
250				errx(1, "shell/tcp: unknown service");
251			if (errno == ECONNREFUSED)
252				warning("remote host doesn't support Kerberos");
253			if (errno == ENOENT)
254				warning("can't provide Kerberos auth data");
255			goto try_connect;
256		}
257	} else {
258		if (doencrypt)
259			errx(1, "the -x flag requires Kerberos authentication");
260		rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2);
261	}
262#else
263	rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2);
264#endif
265
266	if (rem < 0)
267		exit(1);
268
269	if (rfd2 < 0)
270		errx(1, "can't establish stderr");
271	if (dflag) {
272		if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
273		    sizeof(one)) < 0)
274			warn("setsockopt");
275		if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one,
276		    sizeof(one)) < 0)
277			warn("setsockopt");
278	}
279
280	(void)setuid(uid);
281	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
282	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
283		(void)signal(SIGINT, sendsig);
284	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
285		(void)signal(SIGQUIT, sendsig);
286	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
287		(void)signal(SIGTERM, sendsig);
288
289	if (!nflag) {
290		pid = fork();
291		if (pid < 0)
292			err(1, "fork");
293	}
294
295#ifdef KERBEROS
296#ifdef CRYPT
297	if (!doencrypt)
298#endif
299#endif
300	{
301		(void)ioctl(rfd2, FIONBIO, &one);
302		(void)ioctl(rem, FIONBIO, &one);
303	}
304
305	talk(nflag, omask, pid, rem, timeout);
306
307	if (!nflag)
308		(void)kill(pid, SIGKILL);
309	exit(0);
310}
311
312void
313talk(nflag, omask, pid, rem, timeout)
314	int nflag;
315	long omask;
316	pid_t pid;
317	int rem;
318{
319	int cc, wc;
320	fd_set readfrom, ready, rembits;
321	char *bp, buf[BUFSIZ];
322	struct timeval tvtimeout;
323	int srval;
324
325	if (!nflag && pid == 0) {
326		(void)close(rfd2);
327
328reread:		errno = 0;
329		if ((cc = read(0, buf, sizeof buf)) <= 0)
330			goto done;
331		bp = buf;
332
333rewrite:
334		FD_ZERO(&rembits);
335		FD_SET(rem, &rembits);
336		if (select(16, 0, &rembits, 0, 0) < 0) {
337			if (errno != EINTR)
338				err(1, "select");
339			goto rewrite;
340		}
341		if (!FD_ISSET(rem, &rembits))
342			goto rewrite;
343#ifdef KERBEROS
344#ifdef CRYPT
345		if (doencrypt)
346			wc = des_write(rem, bp, cc);
347		else
348#endif
349#endif
350			wc = write(rem, bp, cc);
351		if (wc < 0) {
352			if (errno == EWOULDBLOCK)
353				goto rewrite;
354			goto done;
355		}
356		bp += wc;
357		cc -= wc;
358		if (cc == 0)
359			goto reread;
360		goto rewrite;
361done:
362		(void)shutdown(rem, 1);
363		exit(0);
364	}
365
366	tvtimeout.tv_sec = timeout;
367	tvtimeout.tv_usec = 0;
368
369	(void)sigsetmask(omask);
370	FD_ZERO(&readfrom);
371	FD_SET(rfd2, &readfrom);
372	FD_SET(rem, &readfrom);
373	do {
374		ready = readfrom;
375		if (timeout) {
376			srval = select(16, &ready, 0, 0, &tvtimeout);
377		} else {
378			srval = select(16, &ready, 0, 0, 0);
379		}
380
381		if (srval < 0) {
382			if (errno != EINTR)
383				err(1, "select");
384			continue;
385		}
386		if (srval == 0)
387			errx(1, "timeout reached (%d seconds)\n", timeout);
388		if (FD_ISSET(rfd2, &ready)) {
389			errno = 0;
390#ifdef KERBEROS
391#ifdef CRYPT
392			if (doencrypt)
393				cc = des_read(rfd2, buf, sizeof buf);
394			else
395#endif
396#endif
397				cc = read(rfd2, buf, sizeof buf);
398			if (cc <= 0) {
399				if (errno != EWOULDBLOCK)
400					FD_CLR(rfd2, &readfrom);
401			} else
402				(void)write(2, buf, cc);
403		}
404		if (FD_ISSET(rem, &ready)) {
405			errno = 0;
406#ifdef KERBEROS
407#ifdef CRYPT
408			if (doencrypt)
409				cc = des_read(rem, buf, sizeof buf);
410			else
411#endif
412#endif
413				cc = read(rem, buf, sizeof buf);
414			if (cc <= 0) {
415				if (errno != EWOULDBLOCK)
416					FD_CLR(rem, &readfrom);
417			} else
418				(void)write(1, buf, cc);
419		}
420	} while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom));
421}
422
423void
424sendsig(sig)
425	int sig;
426{
427	char signo;
428
429	signo = sig;
430#ifdef KERBEROS
431#ifdef CRYPT
432	if (doencrypt)
433		(void)des_write(rfd2, &signo, 1);
434	else
435#endif
436#endif
437		(void)write(rfd2, &signo, 1);
438}
439
440#ifdef KERBEROS
441/* VARARGS */
442void
443warning(va_alist)
444va_dcl
445{
446	va_list ap;
447	char *fmt;
448
449	(void)fprintf(stderr, "rsh: warning, using standard rsh: ");
450	va_start(ap);
451	fmt = va_arg(ap, char *);
452	vfprintf(stderr, fmt, ap);
453	va_end(ap);
454	(void)fprintf(stderr, ".\n");
455}
456#endif
457
458char *
459copyargs(argv)
460	char **argv;
461{
462	int cc;
463	char **ap, *args, *p;
464
465	cc = 0;
466	for (ap = argv; *ap; ++ap)
467		cc += strlen(*ap) + 1;
468	if (!(args = malloc((u_int)cc)))
469		err(1, NULL);
470	for (p = args, ap = argv; *ap; ++ap) {
471		(void)strcpy(p, *ap);
472		for (p = strcpy(p, *ap); *p; ++p);
473		if (ap[1])
474			*p++ = ' ';
475	}
476	return (args);
477}
478
479void
480usage()
481{
482
483	(void)fprintf(stderr,
484	    "usage: rsh [-nd%s]%s[-l login] [-t timeout] host [command]\n",
485#ifdef KERBEROS
486#ifdef CRYPT
487	    "x", " [-k realm] ");
488#else
489	    "", " [-k realm] ");
490#endif
491#else
492	    "", " ");
493#endif
494	exit(1);
495}
496
497