]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/rsh/rsh.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 #if 0
48 #ifndef lint
49 static const char sccsid[] = "From: @(#)rsh.c   8.3 (Berkeley) 4/6/94";
50 #endif /* not lint */
51 #endif
52
53 #include <sys/cdefs.h>
54 __FBSDID("$FreeBSD$");
55
56 #include <sys/param.h>
57 #include <sys/signal.h>
58 #include <sys/socket.h>
59 #include <sys/ioctl.h>
60 #include <sys/file.h>
61 #include <sys/time.h>
62
63 #include <netinet/in.h>
64 #include <netdb.h>
65
66 #include <err.h>
67 #include <errno.h>
68 #include <libutil.h>
69 #include <paths.h>
70 #include <pwd.h>
71 #include <signal.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <unistd.h>
76
77 /*
78  * rsh - remote shell
79  */
80 int     rfd2;
81
82 int family = PF_UNSPEC;
83 char rlogin[] = "rlogin";
84
85 void    connect_timeout(int);
86 char   *copyargs(char * const *);
87 void    sendsig(int);
88 void    talk(int, long, pid_t, int, int);
89 void    usage(void);
90
91 int
92 main(int argc, char *argv[])
93 {
94         struct passwd const *pw;
95         struct servent const *sp;
96         long omask;
97         int argoff, asrsh, ch, dflag, nflag, one, rem;
98         pid_t pid = 0;
99         uid_t uid;
100         char *args, *host, *p, *user;
101         int timeout = 0;
102
103         argoff = asrsh = dflag = nflag = 0;
104         one = 1;
105         host = user = NULL;
106
107         /* if called as something other than "rsh", use it as the host name */
108         if ((p = strrchr(argv[0], '/')))
109                 ++p;
110         else
111                 p = argv[0];
112         if (strcmp(p, "rsh"))
113                 host = p;
114         else
115                 asrsh = 1;
116
117         /* handle "rsh host flags" */
118         if (!host && argc > 2 && argv[1][0] != '-') {
119                 host = argv[1];
120                 argoff = 1;
121         }
122
123 #define OPTIONS "468Lde:l:nt:w"
124         while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
125                 switch(ch) {
126                 case '4':
127                         family = PF_INET;
128                         break;
129
130                 case '6':
131                         family = PF_INET6;
132                         break;
133
134                 case 'L':       /* -8Lew are ignored to allow rlogin aliases */
135                 case 'e':
136                 case 'w':
137                 case '8':
138                         break;
139                 case 'd':
140                         dflag = 1;
141                         break;
142                 case 'l':
143                         user = optarg;
144                         break;
145                 case 'n':
146                         nflag = 1;
147                         break;
148                 case 't':
149                         timeout = atoi(optarg);
150                         break;
151                 case '?':
152                 default:
153                         usage();
154                 }
155         optind += argoff;
156
157         /* if haven't gotten a host yet, do so */
158         if (!host && !(host = argv[optind++]))
159                 usage();
160
161         /* if no further arguments, must have been called as rlogin. */
162         if (!argv[optind]) {
163                 if (asrsh)
164                         *argv = rlogin;
165                 execv(_PATH_RLOGIN, argv);
166                 err(1, "can't exec %s", _PATH_RLOGIN);
167         }
168
169         argc -= optind;
170         argv += optind;
171
172         if (!(pw = getpwuid(uid = getuid())))
173                 errx(1, "unknown user id");
174         if (!user)
175                 user = pw->pw_name;
176
177         args = copyargs(argv);
178
179         sp = NULL;
180         if (sp == NULL)
181                 sp = getservbyname("shell", "tcp");
182         if (sp == NULL)
183                 errx(1, "shell/tcp: unknown service");
184
185         if (timeout) {
186                 signal(SIGALRM, connect_timeout);
187                 alarm(timeout);
188         }
189         rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2,
190                       family);
191         if (timeout) {
192                 signal(SIGALRM, SIG_DFL);
193                 alarm(0);
194         }
195
196         if (rem < 0)
197                 exit(1);
198
199         if (rfd2 < 0)
200                 errx(1, "can't establish stderr");
201         if (dflag) {
202                 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
203                     sizeof(one)) < 0)
204                         warn("setsockopt");
205                 if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one,
206                     sizeof(one)) < 0)
207                         warn("setsockopt");
208         }
209
210         (void)setuid(uid);
211         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
212         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
213                 (void)signal(SIGINT, sendsig);
214         if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
215                 (void)signal(SIGQUIT, sendsig);
216         if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
217                 (void)signal(SIGTERM, sendsig);
218
219         if (!nflag) {
220                 pid = fork();
221                 if (pid < 0)
222                         err(1, "fork");
223         }
224         else
225                 (void)shutdown(rem, SHUT_WR);
226
227         (void)ioctl(rfd2, FIONBIO, &one);
228         (void)ioctl(rem, FIONBIO, &one);
229
230         talk(nflag, omask, pid, rem, timeout);
231
232         if (!nflag)
233                 (void)kill(pid, SIGKILL);
234         exit(0);
235 }
236
237 void
238 talk(int nflag, long omask, pid_t pid, int rem, int timeout)
239 {
240         int cc, wc;
241         fd_set readfrom, ready, rembits;
242         char buf[BUFSIZ];
243         const char *bp;
244         struct timeval tvtimeout;
245         int nfds, srval;
246
247         if (!nflag && pid == 0) {
248                 (void)close(rfd2);
249
250 reread:         errno = 0;
251                 if ((cc = read(0, buf, sizeof buf)) <= 0)
252                         goto done;
253                 bp = buf;
254
255 rewrite:
256                 if (rem >= FD_SETSIZE)
257                         errx(1, "descriptor too big");
258                 FD_ZERO(&rembits);
259                 FD_SET(rem, &rembits);
260                 nfds = rem + 1;
261                 if (select(nfds, 0, &rembits, 0, 0) < 0) {
262                         if (errno != EINTR)
263                                 err(1, "select");
264                         goto rewrite;
265                 }
266                 if (!FD_ISSET(rem, &rembits))
267                         goto rewrite;
268                 wc = write(rem, bp, cc);
269                 if (wc < 0) {
270                         if (errno == EWOULDBLOCK)
271                                 goto rewrite;
272                         goto done;
273                 }
274                 bp += wc;
275                 cc -= wc;
276                 if (cc == 0)
277                         goto reread;
278                 goto rewrite;
279 done:
280                 (void)shutdown(rem, SHUT_WR);
281                 exit(0);
282         }
283
284         tvtimeout.tv_sec = timeout;
285         tvtimeout.tv_usec = 0;
286
287         (void)sigsetmask(omask);
288         if (rfd2 >= FD_SETSIZE || rem >= FD_SETSIZE)
289                 errx(1, "descriptor too big");
290         FD_ZERO(&readfrom);
291         FD_SET(rfd2, &readfrom);
292         FD_SET(rem, &readfrom);
293         nfds = MAX(rfd2+1, rem+1);
294         do {
295                 ready = readfrom;
296                 if (timeout) {
297                         srval = select(nfds, &ready, 0, 0, &tvtimeout);
298                 } else {
299                         srval = select(nfds, &ready, 0, 0, 0);
300                 }
301
302                 if (srval < 0) {
303                         if (errno != EINTR)
304                                 err(1, "select");
305                         continue;
306                 }
307                 if (srval == 0)
308                         errx(1, "timeout reached (%d seconds)", timeout);
309                 if (FD_ISSET(rfd2, &ready)) {
310                         errno = 0;
311                         cc = read(rfd2, buf, sizeof buf);
312                         if (cc <= 0) {
313                                 if (errno != EWOULDBLOCK)
314                                         FD_CLR(rfd2, &readfrom);
315                         } else
316                                 (void)write(STDERR_FILENO, buf, cc);
317                 }
318                 if (FD_ISSET(rem, &ready)) {
319                         errno = 0;
320                         cc = read(rem, buf, sizeof buf);
321                         if (cc <= 0) {
322                                 if (errno != EWOULDBLOCK)
323                                         FD_CLR(rem, &readfrom);
324                         } else
325                                 (void)write(STDOUT_FILENO, buf, cc);
326                 }
327         } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom));
328 }
329
330 void
331 connect_timeout(int sig __unused)
332 {
333         char message[] = "timeout reached before connection completed.\n";
334
335         write(STDERR_FILENO, message, sizeof(message) - 1);
336         _exit(1);
337 }
338
339 void
340 sendsig(int sig)
341 {
342         char signo;
343
344         signo = sig;
345         (void)write(rfd2, &signo, 1);
346 }
347
348 char *
349 copyargs(char * const *argv)
350 {
351         int cc;
352         char *args, *p;
353         char * const *ap;
354
355         cc = 0;
356         for (ap = argv; *ap; ++ap)
357                 cc += strlen(*ap) + 1;
358         if (!(args = malloc((u_int)cc)))
359                 err(1, NULL);
360         for (p = args, ap = argv; *ap; ++ap) {
361                 (void)strcpy(p, *ap);
362                 for (p = strcpy(p, *ap); *p; ++p);
363                 if (ap[1])
364                         *p++ = ' ';
365         }
366         return (args);
367 }
368
369 void
370 usage(void)
371 {
372
373         (void)fprintf(stderr,
374             "usage: rsh [-46dn] [-l username] [-t timeout] host [command]\n");
375         exit(1);
376 }