Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / rsh / rsh.c
1 /*-
2  * Copyright (c) 1983, 1990, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1983, 1990, 1993, 1994\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "From: @(#)rsh.c 8.3 (Berkeley) 4/6/94";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD: src/usr.bin/rsh/rsh.c,v 1.21.2.4 2002/09/17 15:34:41 nectar Exp $";
46 #endif /* not lint */
47
48 #include <sys/param.h>
49 #include <sys/signal.h>
50 #include <sys/socket.h>
51 #include <sys/ioctl.h>
52 #include <sys/file.h>
53 #include <sys/time.h>
54
55 #include <netinet/in.h>
56 #include <netdb.h>
57
58 #include <err.h>
59 #include <errno.h>
60 #include <libutil.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 #include <err.h>
68
69 #include "pathnames.h"
70
71 #ifdef KERBEROS
72 #include <openssl/des.h>
73 #include <krb.h>
74 #include "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 extern char *krb_realmofhost();
81 #endif
82
83 /*
84  * rsh - remote shell
85  */
86 int     rfd2;
87
88 int family = PF_UNSPEC;
89
90 void    connect_timeout __P((int));
91 char   *copyargs __P((char **));
92 void    sendsig __P((int));
93 void    talk __P((int, long, pid_t, int, int));
94 void    usage __P((void));
95
96 int
97 main(argc, argv)
98         int argc;
99         char **argv;
100 {
101         struct passwd *pw;
102         struct servent *sp;
103         long omask;
104         int argoff, asrsh, ch, dflag, nflag, one, rem;
105         pid_t pid = 0;
106         uid_t uid;
107         char *args, *host, *p, *user;
108         int timeout = 0;
109 #ifdef KERBEROS
110         char *k;
111 #endif
112
113         argoff = asrsh = dflag = nflag = 0;
114         one = 1;
115         host = user = NULL;
116
117         /* if called as something other than "rsh", use it as the host name */
118         if ((p = strrchr(argv[0], '/')))
119                 ++p;
120         else
121                 p = argv[0];
122         if (strcmp(p, "rsh"))
123                 host = p;
124         else
125                 asrsh = 1;
126
127         /* handle "rsh host flags" */
128         if (!host && argc > 2 && argv[1][0] != '-') {
129                 host = argv[1];
130                 argoff = 1;
131         }
132
133 #ifdef KERBEROS
134 #ifdef CRYPT
135 #define OPTIONS "468KLde:k:l:nt:wx"
136 #else
137 #define OPTIONS "468KLde:k:l:nt:w"
138 #endif
139 #else
140 #define OPTIONS "468KLde:l:nt:w"
141 #endif
142         while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
143                 switch(ch) {
144                 case '4':
145                         family = PF_INET;
146                         break;
147
148                 case '6':
149                         family = PF_INET6;
150                         break;
151
152                 case 'K':
153 #ifdef KERBEROS
154                         use_kerberos = 0;
155 #endif
156                         break;
157                 case 'L':       /* -8Lew are ignored to allow rlogin aliases */
158                 case 'e':
159                 case 'w':
160                 case '8':
161                         break;
162                 case 'd':
163                         dflag = 1;
164                         break;
165                 case 'l':
166                         user = optarg;
167                         break;
168 #ifdef KERBEROS
169                 case 'k':
170                         dest_realm = dst_realm_buf;
171                         strncpy(dest_realm, optarg, REALM_SZ);
172                         break;
173 #endif
174                 case 'n':
175                         nflag = 1;
176                         break;
177 #ifdef KERBEROS
178 #ifdef CRYPT
179                 case 'x':
180                         doencrypt = 1;
181                         break;
182 #endif
183 #endif
184                 case 't':
185                         timeout = atoi(optarg);
186                         break;
187                 case '?':
188                 default:
189                         usage();
190                 }
191         optind += argoff;
192
193         /* if haven't gotten a host yet, do so */
194         if (!host && !(host = argv[optind++]))
195                 usage();
196
197         /* if no further arguments, must have been called as rlogin. */
198         if (!argv[optind]) {
199                 if (asrsh)
200                         *argv = "rlogin";
201                 execv(_PATH_RLOGIN, argv);
202                 err(1, "can't exec %s", _PATH_RLOGIN);
203         }
204
205         argc -= optind;
206         argv += optind;
207
208         if (!(pw = getpwuid(uid = getuid())))
209                 errx(1, "unknown user id");
210         if (!user)
211                 user = pw->pw_name;
212
213 #ifdef KERBEROS
214 #ifdef CRYPT
215         /* -x turns off -n */
216         if (doencrypt)
217                 nflag = 0;
218 #endif
219 #endif
220
221         args = copyargs(argv);
222
223         sp = NULL;
224 #ifdef KERBEROS
225         k = auth_getval("auth_list");
226         if (k && !strstr(k, "kerberos"))
227             use_kerberos = 0;
228         if (use_kerberos) {
229                 sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp");
230                 if (sp == NULL) {
231                         use_kerberos = 0;
232                         warnx(
233         "warning, using standard rsh: can't get entry for %s/tcp service",
234                             doencrypt ? "ekshell" : "kshell");
235                 }
236         }
237 #endif
238         if (sp == NULL)
239                 sp = getservbyname("shell", "tcp");
240         if (sp == NULL)
241                 errx(1, "shell/tcp: unknown service");
242
243 #ifdef KERBEROS
244 try_connect:
245         if (use_kerberos) {
246                 struct hostent *hp;
247
248                 /* fully qualify hostname (needed for krb_realmofhost) */
249                 hp = gethostbyname(host);
250                 if (hp != NULL && !(host = strdup(hp->h_name)))
251                         err(1, NULL);
252
253                 rem = KSUCCESS;
254                 errno = 0;
255                 if (dest_realm == NULL)
256                         dest_realm = krb_realmofhost(host);
257
258 #ifdef CRYPT
259                 if (doencrypt) {
260                         rem = krcmd_mutual(&host, sp->s_port, user, args,
261                             &rfd2, dest_realm, &cred, schedule);
262                         des_set_key(&cred.session, schedule);
263                 } else
264 #endif
265                         rem = krcmd(&host, sp->s_port, user, args, &rfd2,
266                             dest_realm);
267                 if (rem < 0) {
268                         use_kerberos = 0;
269                         sp = getservbyname("shell", "tcp");
270                         if (sp == NULL)
271                                 errx(1, "shell/tcp: unknown service");
272                         if (errno == ECONNREFUSED)
273                                 warnx(
274                 "warning, using standard rsh: remote host doesn't support Kerberos");
275                         if (errno == ENOENT)
276                                 warnx(
277                 "warning, using standard rsh: can't provide Kerberos auth data");
278                         goto try_connect;
279                 }
280         } else {
281                 if (doencrypt)
282                         errx(1, "the -x flag requires Kerberos authentication");
283                 rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args,
284                               &rfd2, family);
285         }
286 #else
287         if (timeout) {
288                 signal(SIGALRM, connect_timeout);
289                 alarm(timeout);
290         }
291         rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2,
292                       family);
293         if (timeout) {
294                 signal(SIGALRM, SIG_DFL);
295                 alarm(0);
296         }
297 #endif
298
299         if (rem < 0)
300                 exit(1);
301
302         if (rfd2 < 0)
303                 errx(1, "can't establish stderr");
304         if (dflag) {
305                 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
306                     sizeof(one)) < 0)
307                         warn("setsockopt");
308                 if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one,
309                     sizeof(one)) < 0)
310                         warn("setsockopt");
311         }
312
313         (void)setuid(uid);
314         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
315         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
316                 (void)signal(SIGINT, sendsig);
317         if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
318                 (void)signal(SIGQUIT, sendsig);
319         if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
320                 (void)signal(SIGTERM, sendsig);
321
322         if (!nflag) {
323                 pid = fork();
324                 if (pid < 0)
325                         err(1, "fork");
326         }
327         else
328                 (void)shutdown(rem, 1);
329
330 #ifdef KERBEROS
331 #ifdef CRYPT
332         if (!doencrypt)
333 #endif
334 #endif
335         {
336                 (void)ioctl(rfd2, FIONBIO, &one);
337                 (void)ioctl(rem, FIONBIO, &one);
338         }
339
340         talk(nflag, omask, pid, rem, timeout);
341
342         if (!nflag)
343                 (void)kill(pid, SIGKILL);
344         exit(0);
345 }
346
347 void
348 talk(nflag, omask, pid, rem, timeout)
349         int nflag;
350         long omask;
351         pid_t pid;
352         int rem;
353 {
354         int cc, wc;
355         fd_set readfrom, ready, rembits;
356         char *bp, buf[BUFSIZ];
357         struct timeval tvtimeout;
358         int nfds, srval;
359
360         if (!nflag && pid == 0) {
361                 (void)close(rfd2);
362
363 reread:         errno = 0;
364                 if ((cc = read(0, buf, sizeof buf)) <= 0)
365                         goto done;
366                 bp = buf;
367
368 rewrite:
369                 if (rem >= FD_SETSIZE)
370                         errx(1, "descriptor too big");
371                 FD_ZERO(&rembits);
372                 FD_SET(rem, &rembits);
373                 nfds = rem + 1;
374                 if (select(nfds, 0, &rembits, 0, 0) < 0) {
375                         if (errno != EINTR)
376                                 err(1, "select");
377                         goto rewrite;
378                 }
379                 if (!FD_ISSET(rem, &rembits))
380                         goto rewrite;
381 #ifdef KERBEROS
382 #ifdef CRYPT
383                 if (doencrypt)
384                         wc = des_enc_write(rem, bp, cc, schedule, &cred.session);
385                 else
386 #endif
387 #endif
388                         wc = write(rem, bp, cc);
389                 if (wc < 0) {
390                         if (errno == EWOULDBLOCK)
391                                 goto rewrite;
392                         goto done;
393                 }
394                 bp += wc;
395                 cc -= wc;
396                 if (cc == 0)
397                         goto reread;
398                 goto rewrite;
399 done:
400                 (void)shutdown(rem, 1);
401                 exit(0);
402         }
403
404         tvtimeout.tv_sec = timeout;
405         tvtimeout.tv_usec = 0;
406
407         (void)sigsetmask(omask);
408         if (rfd2 >= FD_SETSIZE || rem >= FD_SETSIZE)
409                 errx(1, "descriptor too big");
410         FD_ZERO(&readfrom);
411         FD_SET(rfd2, &readfrom);
412         FD_SET(rem, &readfrom);
413         nfds = MAX(rfd2+1, rem+1);
414         do {
415                 ready = readfrom;
416                 if (timeout) {
417                         srval = select(nfds, &ready, 0, 0, &tvtimeout);
418                 } else {
419                         srval = select(nfds, &ready, 0, 0, 0);
420                 }
421
422                 if (srval < 0) {
423                         if (errno != EINTR)
424                                 err(1, "select");
425                         continue;
426                 }
427                 if (srval == 0)
428                         errx(1, "timeout reached (%d seconds)\n", timeout);
429                 if (FD_ISSET(rfd2, &ready)) {
430                         errno = 0;
431 #ifdef KERBEROS
432 #ifdef CRYPT
433                         if (doencrypt)
434                                 cc = des_enc_read(rfd2, buf, sizeof buf, schedule, &cred.session);
435                         else
436 #endif
437 #endif
438                                 cc = read(rfd2, buf, sizeof buf);
439                         if (cc <= 0) {
440                                 if (errno != EWOULDBLOCK)
441                                         FD_CLR(rfd2, &readfrom);
442                         } else
443                                 (void)write(2, buf, cc);
444                 }
445                 if (FD_ISSET(rem, &ready)) {
446                         errno = 0;
447 #ifdef KERBEROS
448 #ifdef CRYPT
449                         if (doencrypt)
450                                 cc = des_enc_read(rem, buf, sizeof buf, schedule, &cred.session);
451                         else
452 #endif
453 #endif
454                                 cc = read(rem, buf, sizeof buf);
455                         if (cc <= 0) {
456                                 if (errno != EWOULDBLOCK)
457                                         FD_CLR(rem, &readfrom);
458                         } else
459                                 (void)write(1, buf, cc);
460                 }
461         } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom));
462 }
463
464 void
465 connect_timeout(int sig)
466 {
467         char message[] = "timeout reached before connection completed.\n";
468
469         write(STDERR_FILENO, message, sizeof(message) - 1);
470         _exit(1);
471 }
472
473 void
474 sendsig(sig)
475         int sig;
476 {
477         char signo;
478
479         signo = sig;
480 #ifdef KERBEROS
481 #ifdef CRYPT
482         if (doencrypt)
483                 (void)des_enc_write(rfd2, &signo, 1, schedule, &cred.session);
484         else
485 #endif
486 #endif
487                 (void)write(rfd2, &signo, 1);
488 }
489
490 char *
491 copyargs(argv)
492         char **argv;
493 {
494         int cc;
495         char **ap, *args, *p;
496
497         cc = 0;
498         for (ap = argv; *ap; ++ap)
499                 cc += strlen(*ap) + 1;
500         if (!(args = malloc((u_int)cc)))
501                 err(1, NULL);
502         for (p = args, ap = argv; *ap; ++ap) {
503                 (void)strcpy(p, *ap);
504                 for (p = strcpy(p, *ap); *p; ++p);
505                 if (ap[1])
506                         *p++ = ' ';
507         }
508         return (args);
509 }
510
511 void
512 usage()
513 {
514
515         (void)fprintf(stderr,
516             "usage: rsh [-46] [-ndK%s]%s[-l login] [-t timeout] host [command]\n",
517 #ifdef KERBEROS
518 #ifdef CRYPT
519             "x", " [-k realm] ");
520 #else
521             "", " [-k realm] ");
522 #endif
523 #else
524             "", " ");
525 #endif
526         exit(1);
527 }
528