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