]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.bin/rsh/rsh.c
MFC r368207,368607:
[FreeBSD/stable/10.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, 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, 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 = 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 "468LNde: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 'N':
135                         Nflag = 1;
136                         nflag = 0;
137                         break;
138                 case 'L':       /* -8Lew are ignored to allow rlogin aliases */
139                 case 'e':
140                 case 'w':
141                 case '8':
142                         break;
143                 case 'd':
144                         dflag = 1;
145                         break;
146                 case 'l':
147                         user = optarg;
148                         break;
149                 case 'n':
150                         nflag = 1;
151                         Nflag = 0;
152                         break;
153                 case 't':
154                         timeout = atoi(optarg);
155                         break;
156                 case '?':
157                 default:
158                         usage();
159                 }
160         optind += argoff;
161
162         /* if haven't gotten a host yet, do so */
163         if (!host && !(host = argv[optind++]))
164                 usage();
165
166         /* if no further arguments, must have been called as rlogin. */
167         if (!argv[optind]) {
168                 if (asrsh)
169                         *argv = rlogin;
170                 execv(_PATH_RLOGIN, argv);
171                 err(1, "can't exec %s", _PATH_RLOGIN);
172         }
173
174         argc -= optind;
175         argv += optind;
176
177         if (!(pw = getpwuid(uid = getuid())))
178                 errx(1, "unknown user id");
179         if (!user)
180                 user = pw->pw_name;
181
182         args = copyargs(argv);
183
184         sp = NULL;
185         if (sp == NULL)
186                 sp = getservbyname("shell", "tcp");
187         if (sp == NULL)
188                 errx(1, "shell/tcp: unknown service");
189
190         if (timeout) {
191                 signal(SIGALRM, connect_timeout);
192                 alarm(timeout);
193         }
194         rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2,
195                       family);
196         if (timeout) {
197                 signal(SIGALRM, SIG_DFL);
198                 alarm(0);
199         }
200
201         if (rem < 0)
202                 exit(1);
203
204         if (rfd2 < 0)
205                 errx(1, "can't establish stderr");
206         if (dflag) {
207                 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
208                     sizeof(one)) < 0)
209                         warn("setsockopt");
210                 if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one,
211                     sizeof(one)) < 0)
212                         warn("setsockopt");
213         }
214
215         (void)setuid(uid);
216         omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
217         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
218                 (void)signal(SIGINT, sendsig);
219         if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
220                 (void)signal(SIGQUIT, sendsig);
221         if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
222                 (void)signal(SIGTERM, sendsig);
223
224         if (!nflag) {
225                 pid = fork();
226                 if (pid < 0)
227                         err(1, "fork");
228         }
229         else
230                 (void)shutdown(rem, SHUT_WR);
231
232         (void)ioctl(rfd2, FIONBIO, &one);
233         (void)ioctl(rem, FIONBIO, &one);
234
235         talk(nflag, Nflag, omask, pid, rem, timeout);
236
237         if (!nflag)
238                 (void)kill(pid, SIGKILL);
239         exit(0);
240 }
241
242 void
243 talk(int nflag, int Nflag, long omask, pid_t pid, int rem, int timeout)
244 {
245         int cc, wc;
246         fd_set readfrom, ready, rembits;
247         char buf[BUFSIZ];
248         const char *bp;
249         struct timeval tvtimeout;
250         int nfds, srval;
251
252         if (!nflag && pid == 0) {
253                 (void)close(rfd2);
254
255 reread:         errno = 0;
256                 if ((cc = read(0, buf, sizeof buf)) <= 0)
257                         goto done;
258                 bp = buf;
259
260 rewrite:
261                 if (rem >= FD_SETSIZE)
262                         errx(1, "descriptor too big");
263                 FD_ZERO(&rembits);
264                 FD_SET(rem, &rembits);
265                 nfds = rem + 1;
266                 if (select(nfds, 0, &rembits, 0, 0) < 0) {
267                         if (errno != EINTR)
268                                 err(1, "select");
269                         goto rewrite;
270                 }
271                 if (!FD_ISSET(rem, &rembits))
272                         goto rewrite;
273                 wc = write(rem, bp, cc);
274                 if (wc < 0) {
275                         if (errno == EWOULDBLOCK)
276                                 goto rewrite;
277                         goto done;
278                 }
279                 bp += wc;
280                 cc -= wc;
281                 if (cc == 0)
282                         goto reread;
283                 goto rewrite;
284 done:           if (!Nflag)
285                         (void)shutdown(rem, SHUT_WR);
286                 exit(0);
287         }
288
289         tvtimeout.tv_sec = timeout;
290         tvtimeout.tv_usec = 0;
291
292         (void)sigsetmask(omask);
293         if (rfd2 >= FD_SETSIZE || rem >= FD_SETSIZE)
294                 errx(1, "descriptor too big");
295         FD_ZERO(&readfrom);
296         FD_SET(rfd2, &readfrom);
297         FD_SET(rem, &readfrom);
298         nfds = MAX(rfd2+1, rem+1);
299         do {
300                 ready = readfrom;
301                 if (timeout) {
302                         srval = select(nfds, &ready, 0, 0, &tvtimeout);
303                 } else {
304                         srval = select(nfds, &ready, 0, 0, 0);
305                 }
306
307                 if (srval < 0) {
308                         if (errno != EINTR)
309                                 err(1, "select");
310                         continue;
311                 }
312                 if (srval == 0)
313                         errx(1, "timeout reached (%d seconds)", timeout);
314                 if (FD_ISSET(rfd2, &ready)) {
315                         errno = 0;
316                         cc = read(rfd2, buf, sizeof buf);
317                         if (cc <= 0) {
318                                 if (errno != EWOULDBLOCK)
319                                         FD_CLR(rfd2, &readfrom);
320                         } else
321                                 (void)write(STDERR_FILENO, buf, cc);
322                 }
323                 if (FD_ISSET(rem, &ready)) {
324                         errno = 0;
325                         cc = read(rem, buf, sizeof buf);
326                         if (cc <= 0) {
327                                 if (errno != EWOULDBLOCK)
328                                         FD_CLR(rem, &readfrom);
329                         } else
330                                 (void)write(STDOUT_FILENO, buf, cc);
331                 }
332         } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom));
333 }
334
335 void
336 connect_timeout(int sig __unused)
337 {
338         char message[] = "timeout reached before connection completed.\n";
339
340         write(STDERR_FILENO, message, sizeof(message) - 1);
341         _exit(1);
342 }
343
344 void
345 sendsig(int sig)
346 {
347         char signo;
348
349         signo = sig;
350         (void)write(rfd2, &signo, 1);
351 }
352
353 char *
354 copyargs(char * const *argv)
355 {
356         int cc;
357         char *args, *p;
358         char * const *ap;
359
360         cc = 0;
361         for (ap = argv; *ap; ++ap)
362                 cc += strlen(*ap) + 1;
363         if (!(args = malloc((u_int)cc)))
364                 err(1, NULL);
365         for (p = args, ap = argv; *ap; ++ap) {
366                 (void)strcpy(p, *ap);
367                 for (p = strcpy(p, *ap); *p; ++p);
368                 if (ap[1])
369                         *p++ = ' ';
370         }
371         return (args);
372 }
373
374 void
375 usage(void)
376 {
377
378         (void)fprintf(stderr,
379             "usage: rsh [-46Ndn] [-l username] [-t timeout] host [command]\n");
380         exit(1);
381 }