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