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