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