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