rsh.c revision 1.11 1 /* $NetBSD: rsh.c,v 1.11 1997/07/06 19:31:29 christos 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.11 1997/07/06 19:31:29 christos 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
102 int
103 main(argc, argv)
104 int argc;
105 char **argv;
106 {
107 struct passwd *pw;
108 struct servent *sp;
109 sigset_t oset, nset;
110
111 #ifdef IN_RCMD
112 char *locuser = 0, *loop;
113 #endif /* IN_RCMD */
114 int argoff, asrsh, ch, dflag, nflag, one, rem, i;
115 pid_t pid;
116 uid_t uid;
117 char *args, *host, *p, *user, *name;
118
119 argoff = asrsh = dflag = nflag = 0;
120 one = 1;
121 host = user = NULL;
122
123 #ifndef IN_RCMD
124 /*
125 * If called as something other than "rsh" use it as the host name,
126 * only for rsh.
127 */
128 p = __progname;
129 if (strcmp(p, "rsh") == 0)
130 asrsh = 1;
131 else
132 host = p;
133 #endif /* IN_RCMD */
134
135 /* handle "rsh host flags" */
136 if (!host && argc > 2 && argv[1][0] != '-') {
137 host = argv[1];
138 argoff = 1;
139 }
140
141 #ifdef IN_RCMD
142 if ((loop = getenv("RCMD_LOOP")) && strcmp(loop, "YES") == 0)
143 warnx("rcmd appears to be looping!");
144
145 putenv("RCMD_LOOP=YES");
146
147 # ifdef KERBEROS
148 # ifdef CRYPT
149 # define OPTIONS "8KLdek:l:nu:wx"
150 # else
151 # define OPTIONS "8KLdek:l:nu:w"
152 # endif
153 # else
154 # define OPTIONS "8KLdel:nu:w"
155 # endif
156
157 #else /* IN_RCMD */
158
159 # ifdef KERBEROS
160 # ifdef CRYPT
161 # define OPTIONS "8KLdek:l:nwx"
162 # else
163 # define OPTIONS "8KLdek:l:nw"
164 # endif
165 # else
166 # define OPTIONS "8KLdel:nw"
167 # endif
168
169 #endif /* IN_RCMD */
170
171 if (!(pw = getpwuid(uid = getuid())))
172 errx(1, "unknown user id");
173
174 if ((name = strdup(pw->pw_name)) == NULL)
175 err(1, "malloc");
176 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
177 switch(ch) {
178 case 'K':
179 #ifdef KERBEROS
180 use_kerberos = 0;
181 #endif
182 break;
183 case 'L': /* -8Lew are ignored to allow rlogin aliases */
184 case 'e':
185 case 'w':
186 case '8':
187 break;
188 case 'd':
189 dflag = 1;
190 break;
191 case 'l':
192 user = optarg;
193 break;
194 #ifdef KERBEROS
195 case 'k':
196 dest_realm = dst_realm_buf;
197 strncpy(dest_realm, optarg, REALM_SZ);
198 break;
199 #endif
200 case 'n':
201 nflag = 1;
202 break;
203 #ifdef IN_RCMD
204 case 'u':
205 if (getuid() != 0 && optarg && name &&
206 strcmp(name, optarg) != 0)
207 errx(1,"only super user can use the -u option");
208 locuser = optarg;
209 break;
210 #endif /* IN_RCMD */
211 #ifdef KERBEROS
212 #ifdef CRYPT
213 case 'x':
214 doencrypt = 1;
215 des_set_key((des_cblock *) cred.session, schedule);
216 break;
217 #endif
218 #endif
219 case '?':
220 default:
221 usage();
222 }
223 optind += argoff;
224
225 /* if haven't gotten a host yet, do so */
226 if (!host && !(host = argv[optind++]))
227 usage();
228
229 /* if no further arguments, must have been called as rlogin. */
230 if (!argv[optind]) {
231 #ifdef IN_RCMD
232 usage();
233 #else
234 if (asrsh)
235 *argv = "rlogin";
236 execv(_PATH_RLOGIN, argv);
237 err(1, "can't exec %s", _PATH_RLOGIN);
238 #endif
239 }
240
241 argc -= optind;
242 argv += optind;
243
244 /* Accept user1@host format, though "-l user2" overrides user1 */
245 p = strchr(host, '@');
246 if (p) {
247 *p = '\0';
248 if (!user && p > host)
249 user = host;
250 host = p + 1;
251 if (*host == '\0')
252 usage();
253 }
254 if (!user)
255 user = name;
256
257 #ifdef KERBEROS
258 #ifdef CRYPT
259 /* -x turns off -n */
260 if (doencrypt)
261 nflag = 0;
262 #endif
263 #endif
264
265 args = copyargs(argv);
266
267 sp = NULL;
268 #ifdef KERBEROS
269 if (use_kerberos) {
270 sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp");
271 if (sp == NULL) {
272 use_kerberos = 0;
273 warning("can't get entry for %s/tcp service",
274 doencrypt ? "ekshell" : "kshell");
275 }
276 }
277 #endif
278 if (sp == NULL)
279 sp = getservbyname("shell", "tcp");
280 if (sp == NULL)
281 errx(1, "shell/tcp: unknown service");
282
283 #ifdef KERBEROS
284 try_connect:
285 if (use_kerberos) {
286 #if 1
287 struct hostent *hp;
288
289 /* fully qualify hostname (needed for krb_realmofhost) */
290 hp = gethostbyname(host);
291 if (hp != NULL && !(host = strdup(hp->h_name)))
292 err(1, "strdup");
293 #endif
294
295 rem = KSUCCESS;
296 errno = 0;
297 if (dest_realm == NULL)
298 dest_realm = krb_realmofhost(host);
299
300 #ifdef CRYPT
301 if (doencrypt)
302 rem = krcmd_mutual(&host, sp->s_port, user, args,
303 &remerr, dest_realm, &cred, schedule);
304 else
305 #endif
306 rem = krcmd(&host, sp->s_port, user, args, &remerr,
307 dest_realm);
308 if (rem < 0) {
309 use_kerberos = 0;
310 sp = getservbyname("shell", "tcp");
311 if (sp == NULL)
312 errx(1, "shell/tcp: unknown service");
313 if (errno == ECONNREFUSED)
314 warning("remote host doesn't support Kerberos");
315 if (errno == ENOENT)
316 warning("can't provide Kerberos auth data");
317 goto try_connect;
318 }
319 } else {
320 if (doencrypt)
321 errx(1, "the -x flag requires Kerberos authentication.");
322 #ifdef IN_RCMD
323 rem = orcmd(&host, sp->s_port, locuser ? locuser :
324 #else
325 rem = rcmd(&host, sp->s_port,
326 #endif
327 name,
328 user, args, &remerr);
329 }
330 #else /* KERBEROS */
331
332 #ifdef IN_RCMD
333 rem = orcmd(&host, sp->s_port, locuser ? locuser :
334 #else
335 rem = rcmd(&host, sp->s_port,
336 #endif
337 name, user, args, &remerr);
338 #endif /* KERBEROS */
339 (void)free(name);
340
341 if (rem < 0)
342 exit(1);
343
344 if (remerr < 0)
345 errx(1, "can't establish stderr");
346 if (dflag) {
347 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
348 sizeof(one)) < 0)
349 warn("setsockopt remote");
350 if (setsockopt(remerr, SOL_SOCKET, SO_DEBUG, &one,
351 sizeof(one)) < 0)
352 warn("setsockopt stderr");
353 }
354
355 (void) setuid(uid);
356
357 (void) sigemptyset(&nset);
358 for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++)
359 (void) sigaddset(&nset, sigs[i]);
360
361 (void) sigprocmask(SIG_BLOCK, &nset, &oset);
362
363 for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) {
364 struct sigaction sa;
365
366 if (sa.sa_handler != SIG_IGN) {
367 sa.sa_handler = sendsig;
368 (void) sigaction(sigs[i], &sa, NULL);
369 }
370 }
371
372 if (!nflag) {
373 pid = fork();
374 if (pid < 0)
375 err(1, "fork");
376 }
377 else
378 pid = -1;
379
380 #if defined(KERBEROS) && defined(CRYPT)
381 if (!doencrypt)
382 #endif
383 {
384 (void)ioctl(remerr, FIONBIO, &one);
385 (void)ioctl(rem, FIONBIO, &one);
386 }
387
388 talk(nflag, &oset, pid, rem);
389
390 if (!nflag)
391 (void)kill(pid, SIGKILL);
392 exit(0);
393 }
394
395 int
396 checkfd(fdp, outfd)
397 struct pollfd *fdp;
398 int outfd;
399 {
400 int nr, nw;
401 char buf[BUFSIZ];
402
403 if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP))
404 return -1;
405
406 if ((fdp->revents & POLLIN) == 0)
407 return 0;
408
409 errno = 0;
410 #if defined(KERBEROS) && defined(CRYPT)
411 if (doencrypt)
412 nr = des_read(fdp->fd, buf, sizeof buf);
413 else
414 #endif
415 nr = read(fdp->fd, buf, sizeof buf);
416
417 if (nr <= 0) {
418 if (errno != EAGAIN)
419 return -1;
420 else
421 return 0;
422 }
423 else {
424 char *bc = buf;
425 while (nr) {
426 if ((nw = write(outfd, bc, nr)) <= 0)
427 return -1;
428 nr -= nw;
429 bc += nw;
430 }
431 return 0;
432 }
433 }
434
435 void
436 talk(nflag, oset, pid, rem)
437 int nflag;
438 sigset_t *oset;
439 pid_t pid;
440 int rem;
441 {
442 int nr, nw, nfds;
443 struct pollfd fds[2], *fdp = &fds[0];
444 char *bp, buf[BUFSIZ];
445
446
447 if (!nflag && pid == 0) {
448 (void)close(remerr);
449
450 fdp->events = POLLOUT|POLLNVAL|POLLERR|POLLHUP;
451 fdp->fd = rem;
452 nr = 0;
453 bp = buf;
454
455 for (;;) {
456 errno = 0;
457
458 if (nr == 0) {
459 if ((nr = read(0, buf, sizeof buf)) == 0)
460 goto done;
461 if (nr == -1) {
462 if (errno == EIO)
463 goto done;
464 if (errno == EINTR)
465 continue;
466 err(1, "read");
467 }
468 bp = buf;
469 }
470
471 rewrite: if (poll(fdp, 1, INFTIM) == -1) {
472 if (errno != EINTR)
473 err(1, "poll");
474 goto rewrite;
475 }
476
477 if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP))
478 err(1, "poll");
479
480 if ((fdp->revents & POLLOUT) == 0)
481 goto rewrite;
482
483 #if defined(KERBEROS) && defined(CRYPT)
484 if (doencrypt)
485 nw = des_write(rem, bp, nr);
486 else
487 #endif
488 nw = write(rem, bp, nr);
489
490 if (nw < 0) {
491 if (errno == EAGAIN)
492 continue;
493 err(1, "write");
494 }
495 bp += nw;
496 nr -= nw;
497 }
498 done:
499 (void)shutdown(rem, 1);
500 exit(0);
501 }
502
503 (void) sigprocmask(SIG_SETMASK, oset, NULL);
504 fds[0].events = fds[1].events = POLLIN|POLLNVAL|POLLERR|POLLHUP;
505 fds[0].fd = remerr;
506 fds[1].fd = rem;
507 fdp = &fds[0];
508 nfds = 2;
509 do {
510 if (poll(fdp, nfds, INFTIM) == -1) {
511 if (errno != EINTR)
512 err(1, "poll");
513 continue;
514 }
515 if (fds[0].events != 0 && checkfd(&fds[0], 2) == -1) {
516 nfds--;
517 fds[0].events = 0;
518 fdp = &fds[1];
519 }
520 if (fds[1].events != 0 && checkfd(&fds[1], 1) == -1) {
521 nfds--;
522 fds[1].events = 0;
523 }
524 }
525 while (nfds);
526 }
527
528 void
529 sendsig(sig)
530 int sig;
531 {
532 char signo;
533
534 signo = sig;
535 #ifdef KERBEROS
536 #ifdef CRYPT
537 if (doencrypt)
538 (void)des_write(remerr, &signo, 1);
539 else
540 #endif
541 #endif
542 (void)write(remerr, &signo, 1);
543 }
544
545 #ifdef KERBEROS
546 /* VARARGS */
547 void
548 #ifdef __STDC__
549 warning(const char *fmt, ...)
550 #else
551 warning(va_alist)
552 va_dcl
553 #endif
554 {
555 va_list ap;
556 #ifndef __STDC__
557 const char *fmt;
558
559 va_start(ap);
560 fmt = va_arg(ap, const char *);
561 #else
562 va_start(ap, fmt);
563 #endif
564
565 (void) fprintf(stderr, "%s: warning, using standard rsh: ", __progname);
566 (void) vfprintf(stderr, fmt, ap);
567 va_end(ap);
568 (void) fprintf(stderr, ".\n");
569 }
570 #endif
571
572 char *
573 copyargs(argv)
574 char **argv;
575 {
576 int cc;
577 char **ap, *args, *p;
578
579 cc = 0;
580 for (ap = argv; *ap; ++ap)
581 cc += strlen(*ap) + 1;
582 if (!(args = malloc((u_int)cc)))
583 err(1, "malloc");
584 for (p = args, *p = '\0', ap = argv; *ap; ++ap) {
585 (void)strcpy(p, *ap);
586 p += strlen(p);
587 if (ap[1])
588 *p++ = ' ';
589 }
590 *p = '\0';
591 return (args);
592 }
593
594 void
595 usage()
596 {
597
598 (void)fprintf(stderr,
599 "usage: %s [-nd%s]%s[-l login]%s [login@]host %s\n", __progname,
600 #ifdef KERBEROS
601 #ifdef CRYPT
602 "x", " [-k realm] ",
603 #else
604 "", " [-k realm] ",
605 #endif
606 #else
607 "", " ",
608 #endif
609 #ifdef IN_RCMD
610 " [-u locuser]", "command"
611 #else
612 "", "[command]"
613 #endif
614 );
615 exit(1);
616 }
617