xref: /netbsd-src/usr.bin/rsh/rsh.c (revision 4472dbe5e3bd91ef2540bada7a7ca7384627ff9b)
1 /*	$NetBSD: rsh.c,v 1.13 2000/01/31 14:19:34 itojun Exp $	*/
2 
3 /*-
4  * Copyright (c) 1983, 1990, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1993, 1994\n\
39 	The Regents of the University of California.  All rights reserved.\n");
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)rsh.c	8.4 (Berkeley) 4/29/95";
45 #else
46 __RCSID("$NetBSD: rsh.c,v 1.13 2000/01/31 14:19:34 itojun Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/ioctl.h>
53 #include <sys/file.h>
54 #include <poll.h>
55 
56 #include <netinet/in.h>
57 #include <netdb.h>
58 
59 #include <err.h>
60 #include <errno.h>
61 #include <pwd.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 #ifdef __STDC__
68 #include <stdarg.h>
69 #else
70 #include <varargs.h>
71 #endif
72 
73 #include "pathnames.h"
74 
75 #ifdef KERBEROS
76 #include <kerberosIV/des.h>
77 #include <kerberosIV/krb.h>
78 
79 CREDENTIALS cred;
80 Key_schedule schedule;
81 int use_kerberos = 1, doencrypt;
82 char dst_realm_buf[REALM_SZ], *dest_realm;
83 
84 void	warning __P((const char *, ...));
85 #endif
86 
87 /*
88  * rsh - remote shell
89  */
90 extern	char *__progname;		/* XXX */
91 int	remerr;
92 
93 static int sigs[] = { SIGINT, SIGTERM, SIGQUIT };
94 
95 char   *copyargs __P((char **));
96 void	sendsig __P((int));
97 int	checkfd __P((struct pollfd *, int));
98 void	talk __P((int, sigset_t *, pid_t, int));
99 void	usage __P((void));
100 int	main __P((int, char **));
101 #ifdef IN_RCMD
102 int	 orcmd __P((char **, int, const char *,
103     const char *, const char *, int *));
104 int	 orcmd_af __P((char **, int, const char *,
105     const char *, const char *, int *, int));
106 #endif
107 
108 int
109 main(argc, argv)
110 	int argc;
111 	char **argv;
112 {
113 	struct passwd *pw;
114 	struct servent *sp;
115 	sigset_t oset, nset;
116 
117 #ifdef IN_RCMD
118 	char	*locuser = 0, *loop;
119 #endif /* IN_RCMD */
120 	int argoff, asrsh, ch, dflag, nflag, one, rem, i;
121 	pid_t pid;
122 	uid_t uid;
123 	char *args, *host, *p, *user, *name;
124 
125 	argoff = asrsh = dflag = nflag = 0;
126 	one = 1;
127 	host = user = NULL;
128 
129 #ifndef IN_RCMD
130 	/*
131 	 * If called as something other than "rsh" use it as the host name,
132 	 * only for rsh.
133 	 */
134 	p = __progname;
135 	if (strcmp(p, "rsh") == 0)
136 		asrsh = 1;
137 	else
138 		host = p;
139 #endif /* IN_RCMD */
140 
141 	/* handle "rsh host flags" */
142 	if (!host && argc > 2 && argv[1][0] != '-') {
143 		host = argv[1];
144 		argoff = 1;
145 	}
146 
147 #ifdef IN_RCMD
148 	if ((loop = getenv("RCMD_LOOP")) && strcmp(loop, "YES") == 0)
149 		warnx("rcmd appears to be looping!");
150 
151 	putenv("RCMD_LOOP=YES");
152 
153 # ifdef KERBEROS
154 #  ifdef CRYPT
155 #   define	OPTIONS	"8KLdek:l:nu:wx"
156 #  else
157 #   define	OPTIONS	"8KLdek:l:nu:w"
158 #  endif
159 # else
160 #  define	OPTIONS	"8KLdel:nu:w"
161 # endif
162 
163 #else /* IN_RCMD */
164 
165 # ifdef KERBEROS
166 #  ifdef CRYPT
167 #   define	OPTIONS	"8KLdek:l:nwx"
168 #  else
169 #   define	OPTIONS	"8KLdek:l:nw"
170 #  endif
171 # else
172 #  define	OPTIONS	"8KLdel:nw"
173 # endif
174 
175 #endif /* IN_RCMD */
176 
177 	if (!(pw = getpwuid(uid = getuid())))
178 		errx(1, "unknown user id");
179 
180 	if ((name = strdup(pw->pw_name)) == NULL)
181 		err(1, "malloc");
182 	while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
183 		switch(ch) {
184 		case 'K':
185 #ifdef KERBEROS
186 			use_kerberos = 0;
187 #endif
188 			break;
189 		case 'L':	/* -8Lew are ignored to allow rlogin aliases */
190 		case 'e':
191 		case 'w':
192 		case '8':
193 			break;
194 		case 'd':
195 			dflag = 1;
196 			break;
197 		case 'l':
198 			user = optarg;
199 			break;
200 #ifdef KERBEROS
201 		case 'k':
202 			dest_realm = dst_realm_buf;
203 			strncpy(dest_realm, optarg, REALM_SZ);
204 			break;
205 #endif
206 		case 'n':
207 			nflag = 1;
208 			break;
209 #ifdef IN_RCMD
210 		case 'u':
211 			if (getuid() != 0 && optarg && name &&
212 			    strcmp(name, optarg) != 0)
213 				errx(1,"only super user can use the -u option");
214 			locuser = optarg;
215 			break;
216 #endif /* IN_RCMD */
217 #ifdef KERBEROS
218 #ifdef CRYPT
219 		case 'x':
220 			doencrypt = 1;
221 			des_set_key((des_cblock *) cred.session, schedule);
222 			break;
223 #endif
224 #endif
225 		case '?':
226 		default:
227 			usage();
228 		}
229 	optind += argoff;
230 
231 	/* if haven't gotten a host yet, do so */
232 	if (!host && !(host = argv[optind++]))
233 		usage();
234 
235 	/* if no further arguments, must have been called as rlogin. */
236 	if (!argv[optind]) {
237 #ifdef IN_RCMD
238 		usage();
239 #else
240 		if (asrsh)
241 			*argv = "rlogin";
242 		execv(_PATH_RLOGIN, argv);
243 		err(1, "can't exec %s", _PATH_RLOGIN);
244 #endif
245 	}
246 
247 	argc -= optind;
248 	argv += optind;
249 
250 	/* Accept user1@host format, though "-l user2" overrides user1 */
251 	p = strchr(host, '@');
252 	if (p) {
253 		*p = '\0';
254 		if (!user && p > host)
255 			user = host;
256 		host = p + 1;
257 		if (*host == '\0')
258 			usage();
259 	}
260 	if (!user)
261 		user = name;
262 
263 #ifdef KERBEROS
264 #ifdef CRYPT
265 	/* -x turns off -n */
266 	if (doencrypt)
267 		nflag = 0;
268 #endif
269 #endif
270 
271 	args = copyargs(argv);
272 
273 	sp = NULL;
274 #ifdef KERBEROS
275 	if (use_kerberos) {
276 		sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp");
277 		if (sp == NULL) {
278 			use_kerberos = 0;
279 			warning("can't get entry for %s/tcp service",
280 			    doencrypt ? "ekshell" : "kshell");
281 		}
282 	}
283 #endif
284 	if (sp == NULL)
285 		sp = getservbyname("shell", "tcp");
286 	if (sp == NULL)
287 		errx(1, "shell/tcp: unknown service");
288 
289 #ifdef KERBEROS
290 try_connect:
291 	if (use_kerberos) {
292 #if 1
293 		struct hostent *hp;
294 
295 		/* fully qualify hostname (needed for krb_realmofhost) */
296 		hp = gethostbyname(host);
297 		if (hp != NULL && !(host = strdup(hp->h_name)))
298 			err(1, "strdup");
299 #endif
300 
301 		rem = KSUCCESS;
302 		errno = 0;
303 		if (dest_realm == NULL)
304 			dest_realm = krb_realmofhost(host);
305 
306 #ifdef CRYPT
307 		if (doencrypt)
308 			rem = krcmd_mutual(&host, sp->s_port, user, args,
309 			    &remerr, dest_realm, &cred, schedule);
310 		else
311 #endif
312 			rem = krcmd(&host, sp->s_port, user, args, &remerr,
313 			    dest_realm);
314 		if (rem < 0) {
315 			use_kerberos = 0;
316 			sp = getservbyname("shell", "tcp");
317 			if (sp == NULL)
318 				errx(1, "shell/tcp: unknown service");
319 			if (errno == ECONNREFUSED)
320 				warning("remote host doesn't support Kerberos");
321 			if (errno == ENOENT)
322 				warning("can't provide Kerberos auth data");
323 			goto try_connect;
324 		}
325 	} else {
326 		if (doencrypt)
327 			errx(1, "the -x flag requires Kerberos authentication.");
328 #ifdef IN_RCMD
329 		rem = orcmd_af(&host, sp->s_port, locuser ? locuser :
330 #else
331 		rem = rcmd_af(&host, sp->s_port,
332 #endif
333 		    name,
334 		    user, args, &remerr, PF_UNSPEC);
335 	}
336 #else /* KERBEROS */
337 
338 #ifdef IN_RCMD
339 	rem = orcmd_af(&host, sp->s_port, locuser ? locuser :
340 #else
341 	rem = rcmd_af(&host, sp->s_port,
342 #endif
343 	    name, user, args, &remerr, PF_UNSPEC);
344 #endif /* KERBEROS */
345 	(void)free(name);
346 
347 	if (rem < 0)
348 		exit(1);
349 
350 	if (remerr < 0)
351 		errx(1, "can't establish stderr");
352 	if (dflag) {
353 		if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
354 		    sizeof(one)) < 0)
355 			warn("setsockopt remote");
356 		if (setsockopt(remerr, SOL_SOCKET, SO_DEBUG, &one,
357 		    sizeof(one)) < 0)
358 			warn("setsockopt stderr");
359 	}
360 
361 	(void) setuid(uid);
362 
363 	(void) sigemptyset(&nset);
364 	for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++)
365 		(void) sigaddset(&nset, sigs[i]);
366 
367 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
368 
369 	for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) {
370 		struct sigaction sa;
371 
372 		if (sa.sa_handler != SIG_IGN) {
373 			sa.sa_handler = sendsig;
374 			(void) sigaction(sigs[i], &sa, NULL);
375 		}
376 	}
377 
378 	if (!nflag) {
379 		pid = fork();
380 		if (pid < 0)
381 			err(1, "fork");
382 	}
383 	else
384 		pid = -1;
385 
386 #if defined(KERBEROS) && defined(CRYPT)
387 	if (!doencrypt)
388 #endif
389 	{
390 		(void)ioctl(remerr, FIONBIO, &one);
391 		(void)ioctl(rem, FIONBIO, &one);
392 	}
393 
394 	talk(nflag, &oset, pid, rem);
395 
396 	if (!nflag)
397 		(void)kill(pid, SIGKILL);
398 	exit(0);
399 }
400 
401 int
402 checkfd(fdp, outfd)
403 	struct pollfd *fdp;
404 	int outfd;
405 {
406 	int nr, nw;
407 	char buf[BUFSIZ];
408 
409 	if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP))
410 		return -1;
411 
412 	if ((fdp->revents & POLLIN) == 0)
413 		return 0;
414 
415 	errno = 0;
416 #if defined(KERBEROS) && defined(CRYPT)
417 	if (doencrypt)
418 		nr = des_read(fdp->fd, buf, sizeof buf);
419 	else
420 #endif
421 		nr = read(fdp->fd, buf, sizeof buf);
422 
423 	if (nr <= 0) {
424 		if (errno != EAGAIN)
425 			return -1;
426 		else
427 			return 0;
428 	}
429 	else {
430 		char *bc = buf;
431 		while (nr) {
432 			if ((nw = write(outfd, bc, nr)) <= 0)
433 				return -1;
434 			nr -= nw;
435 			bc += nw;
436 		}
437 		return 0;
438 	}
439 }
440 
441 void
442 talk(nflag, oset, pid, rem)
443 	int nflag;
444 	sigset_t *oset;
445 	pid_t pid;
446 	int rem;
447 {
448 	int nr, nw, nfds;
449 	struct pollfd fds[2], *fdp = &fds[0];
450 	char *bp, buf[BUFSIZ];
451 
452 
453 	if (!nflag && pid == 0) {
454 		(void)close(remerr);
455 
456 		fdp->events = POLLOUT|POLLNVAL|POLLERR|POLLHUP;
457 		fdp->fd = rem;
458 		nr = 0;
459 		bp = buf;
460 
461 		for (;;) {
462 			errno = 0;
463 
464 			if (nr == 0) {
465 				if ((nr = read(0, buf, sizeof buf)) == 0)
466 					goto done;
467 				if (nr == -1) {
468 					if (errno == EIO)
469 						goto done;
470 					if (errno == EINTR)
471 						continue;
472 					err(1, "read");
473 				}
474 				bp = buf;
475 			}
476 
477 rewrite:		if (poll(fdp, 1, INFTIM) == -1) {
478 				if (errno != EINTR)
479 					err(1, "poll");
480 				goto rewrite;
481 			}
482 
483 			if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP))
484 				err(1, "poll");
485 
486 			if ((fdp->revents & POLLOUT) == 0)
487 				goto rewrite;
488 
489 #if defined(KERBEROS) && defined(CRYPT)
490 			if (doencrypt)
491 				nw = des_write(rem, bp, nr);
492 			else
493 #endif
494 				nw = write(rem, bp, nr);
495 
496 			if (nw < 0) {
497 				if (errno == EAGAIN)
498 					continue;
499 				err(1, "write");
500 			}
501 			bp += nw;
502 			nr -= nw;
503 		}
504 done:
505 		(void)shutdown(rem, 1);
506 		exit(0);
507 	}
508 
509 	(void) sigprocmask(SIG_SETMASK, oset, NULL);
510 	fds[0].events = fds[1].events = POLLIN|POLLNVAL|POLLERR|POLLHUP;
511 	fds[0].fd = remerr;
512 	fds[1].fd = rem;
513 	fdp = &fds[0];
514 	nfds = 2;
515 	do {
516 		if (poll(fdp, nfds, INFTIM) == -1) {
517 			if (errno != EINTR)
518 				err(1, "poll");
519 			continue;
520 		}
521 		if (fds[0].events != 0 && checkfd(&fds[0], 2) == -1) {
522 			nfds--;
523 			fds[0].events = 0;
524 			fdp = &fds[1];
525 		}
526 		if (fds[1].events != 0 && checkfd(&fds[1], 1) == -1) {
527 			nfds--;
528 			fds[1].events = 0;
529 		}
530 	}
531 	while (nfds);
532 }
533 
534 void
535 sendsig(sig)
536 	int sig;
537 {
538 	char signo;
539 
540 	signo = sig;
541 #ifdef KERBEROS
542 #ifdef CRYPT
543 	if (doencrypt)
544 		(void)des_write(remerr, &signo, 1);
545 	else
546 #endif
547 #endif
548 		(void)write(remerr, &signo, 1);
549 }
550 
551 #ifdef KERBEROS
552 /* VARARGS */
553 void
554 #ifdef __STDC__
555 warning(const char *fmt, ...)
556 #else
557 warning(va_alist)
558 	va_dcl
559 #endif
560 {
561 	va_list ap;
562 #ifndef __STDC__
563 	const char *fmt;
564 
565 	va_start(ap);
566 	fmt = va_arg(ap, const char *);
567 #else
568 	va_start(ap, fmt);
569 #endif
570 
571 	(void) fprintf(stderr, "%s: warning, using standard rsh: ", __progname);
572 	(void) vfprintf(stderr, fmt, ap);
573 	va_end(ap);
574 	(void) fprintf(stderr, ".\n");
575 }
576 #endif
577 
578 char *
579 copyargs(argv)
580 	char **argv;
581 {
582 	int cc;
583 	char **ap, *args, *p;
584 
585 	cc = 0;
586 	for (ap = argv; *ap; ++ap)
587 		cc += strlen(*ap) + 1;
588 	if (!(args = malloc((u_int)cc)))
589 		err(1, "malloc");
590 	for (p = args, *p = '\0', ap = argv; *ap; ++ap) {
591 		(void)strcpy(p, *ap);
592 		p += strlen(p);
593 		if (ap[1])
594 			*p++ = ' ';
595 	}
596 	*p = '\0';
597 	return (args);
598 }
599 
600 void
601 usage()
602 {
603 
604 	(void)fprintf(stderr,
605 	    "usage: %s [-nd%s]%s[-l login]%s [login@]host %s\n", __progname,
606 #ifdef KERBEROS
607 #ifdef CRYPT
608 	    "x", " [-k realm] ",
609 #else
610 	    "", " [-k realm] ",
611 #endif
612 #else
613 	    "", " ",
614 #endif
615 #ifdef IN_RCMD
616 	    " [-u locuser]", "command"
617 #else
618 	    "", "[command]"
619 #endif
620 	    );
621 	exit(1);
622 }
623