summaryrefslogtreecommitdiff
path: root/src/lib/libc/net/rcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libc/net/rcmd.c')
-rw-r--r--src/lib/libc/net/rcmd.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/lib/libc/net/rcmd.c b/src/lib/libc/net/rcmd.c
new file mode 100644
index 0000000000..d566e0ca4c
--- /dev/null
+++ b/src/lib/libc/net/rcmd.c
@@ -0,0 +1,303 @@
1/*
2 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved.
3 * Copyright (c) 1983, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32#include <sys/socket.h>
33#include <sys/stat.h>
34
35#include <netinet/in.h>
36#include <arpa/inet.h>
37
38#include <signal.h>
39#include <fcntl.h>
40#include <netdb.h>
41#include <unistd.h>
42#include <pwd.h>
43#include <errno.h>
44#include <stdio.h>
45#include <ctype.h>
46#include <string.h>
47#include <syslog.h>
48#include <stdlib.h>
49
50int
51rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
52 const char *cmd, int *fd2p)
53{
54 return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
55}
56
57int
58rcmd_af(char **ahost, int porta, const char *locuser, const char *remuser,
59 const char *cmd, int *fd2p, int af)
60{
61 static char hbuf[MAXHOSTNAMELEN];
62 char pbuf[NI_MAXSERV];
63 struct addrinfo hints, *res, *r;
64 int error;
65 struct sockaddr_storage from;
66 fd_set *readsp = NULL;
67 sigset_t oldmask, mask;
68 pid_t pid;
69 int s, lport, timo;
70 char c, *p;
71 int refused;
72 in_port_t rport = porta;
73
74 /* call rcmdsh() with specified remote shell if appropriate. */
75 if (!issetugid() && (p = getenv("RSH")) && *p) {
76 struct servent *sp = getservbyname("shell", "tcp");
77
78 if (sp && sp->s_port == rport)
79 return (rcmdsh(ahost, rport, locuser, remuser,
80 cmd, p));
81 }
82
83 /* use rsh(1) if non-root and remote port is shell. */
84 if (geteuid()) {
85 struct servent *sp = getservbyname("shell", "tcp");
86
87 if (sp && sp->s_port == rport)
88 return (rcmdsh(ahost, rport, locuser, remuser,
89 cmd, NULL));
90 }
91
92 pid = getpid();
93 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
94 memset(&hints, 0, sizeof(hints));
95 hints.ai_family = af;
96 hints.ai_socktype = SOCK_STREAM;
97 hints.ai_flags = AI_CANONNAME;
98 error = getaddrinfo(*ahost, pbuf, &hints, &res);
99 if (error) {
100#if 0
101 warnx("%s: %s", *ahost, gai_strerror(error));
102#endif
103 return (-1);
104 }
105 if (res->ai_canonname) {
106 strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
107 *ahost = hbuf;
108 } else
109 ; /*XXX*/
110
111 r = res;
112 refused = 0;
113 sigemptyset(&mask);
114 sigaddset(&mask, SIGURG);
115 sigprocmask(SIG_BLOCK, &mask, &oldmask);
116 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
117 s = rresvport_af(&lport, r->ai_family);
118 if (s < 0) {
119 if (errno == EAGAIN)
120 (void)fprintf(stderr,
121 "rcmd: socket: All ports in use\n");
122 else
123 (void)fprintf(stderr, "rcmd: socket: %s\n",
124 strerror(errno));
125 if (r->ai_next) {
126 r = r->ai_next;
127 continue;
128 } else {
129 sigprocmask(SIG_SETMASK, &oldmask, NULL);
130 freeaddrinfo(res);
131 return (-1);
132 }
133 }
134 fcntl(s, F_SETOWN, pid);
135 if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
136 break;
137 (void)close(s);
138 if (errno == EADDRINUSE) {
139 lport--;
140 continue;
141 }
142 if (errno == ECONNREFUSED)
143 refused++;
144 if (r->ai_next) {
145 int oerrno = errno;
146 char hbuf[NI_MAXHOST];
147 const int niflags = NI_NUMERICHOST;
148
149 hbuf[0] = '\0';
150 if (getnameinfo(r->ai_addr, r->ai_addrlen,
151 hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
152 strlcpy(hbuf, "(invalid)", sizeof hbuf);
153 (void)fprintf(stderr, "connect to address %s: ", hbuf);
154 errno = oerrno;
155 perror(0);
156 r = r->ai_next;
157 hbuf[0] = '\0';
158 if (getnameinfo(r->ai_addr, r->ai_addrlen,
159 hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
160 strlcpy(hbuf, "(invalid)", sizeof hbuf);
161 (void)fprintf(stderr, "Trying %s...\n", hbuf);
162 continue;
163 }
164 if (refused && timo <= 16) {
165 (void)sleep(timo);
166 timo *= 2;
167 r = res;
168 refused = 0;
169 continue;
170 }
171 (void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
172 strerror(errno));
173 sigprocmask(SIG_SETMASK, &oldmask, NULL);
174 freeaddrinfo(res);
175 return (-1);
176 }
177 /* given "af" can be PF_UNSPEC, we need the real af for "s" */
178 af = r->ai_family;
179 freeaddrinfo(res);
180#if 0
181 /*
182 * try to rresvport() to the same port. This will make rresvport()
183 * fail it's first bind, resulting in it choosing a random port.
184 */
185 lport--;
186#endif
187 if (fd2p == 0) {
188 write(s, "", 1);
189 lport = 0;
190 } else {
191 char num[8];
192 int s2 = rresvport_af(&lport, af), s3;
193 socklen_t len = sizeof(from);
194 int fdssize = howmany(MAX(s, s2)+1, NFDBITS) * sizeof(fd_mask);
195
196 if (s2 < 0)
197 goto bad;
198 readsp = (fd_set *)malloc(fdssize);
199 if (readsp == NULL) {
200 close(s2);
201 goto bad;
202 }
203 listen(s2, 1);
204 (void)snprintf(num, sizeof(num), "%d", lport);
205 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
206 (void)fprintf(stderr,
207 "rcmd: write (setting up stderr): %s\n",
208 strerror(errno));
209 (void)close(s2);
210 goto bad;
211 }
212again:
213 bzero(readsp, fdssize);
214 FD_SET(s, readsp);
215 FD_SET(s2, readsp);
216 errno = 0;
217 if (select(MAX(s, s2) + 1, readsp, 0, 0, 0) < 1 ||
218 !FD_ISSET(s2, readsp)) {
219 if (errno != 0)
220 (void)fprintf(stderr,
221 "rcmd: select (setting up stderr): %s\n",
222 strerror(errno));
223 else
224 (void)fprintf(stderr,
225 "select: protocol failure in circuit setup\n");
226 (void)close(s2);
227 goto bad;
228 }
229 s3 = accept(s2, (struct sockaddr *)&from, &len);
230 if (s3 < 0) {
231 (void)fprintf(stderr,
232 "rcmd: accept: %s\n", strerror(errno));
233 lport = 0;
234 close(s2);
235 goto bad;
236 }
237
238 /*
239 * XXX careful for ftp bounce attacks. If discovered, shut them
240 * down and check for the real auxiliary channel to connect.
241 */
242 switch (from.ss_family) {
243 case AF_INET:
244 case AF_INET6:
245 if (getnameinfo((struct sockaddr *)&from, len,
246 NULL, 0, num, sizeof(num), NI_NUMERICSERV) == 0 &&
247 atoi(num) != 20) {
248 break;
249 }
250 close(s3);
251 goto again;
252 default:
253 break;
254 }
255 (void)close(s2);
256
257 *fd2p = s3;
258 switch (from.ss_family) {
259 case AF_INET:
260 case AF_INET6:
261 if (getnameinfo((struct sockaddr *)&from, len,
262 NULL, 0, num, sizeof(num), NI_NUMERICSERV) != 0 ||
263 (atoi(num) >= IPPORT_RESERVED ||
264 atoi(num) < IPPORT_RESERVED / 2)) {
265 (void)fprintf(stderr,
266 "socket: protocol failure in circuit setup.\n");
267 goto bad2;
268 }
269 break;
270 default:
271 break;
272 }
273 }
274 (void)write(s, locuser, strlen(locuser)+1);
275 (void)write(s, remuser, strlen(remuser)+1);
276 (void)write(s, cmd, strlen(cmd)+1);
277 if (read(s, &c, 1) != 1) {
278 (void)fprintf(stderr,
279 "rcmd: %s: %s\n", *ahost, strerror(errno));
280 goto bad2;
281 }
282 if (c != 0) {
283 while (read(s, &c, 1) == 1) {
284 (void)write(STDERR_FILENO, &c, 1);
285 if (c == '\n')
286 break;
287 }
288 goto bad2;
289 }
290 sigprocmask(SIG_SETMASK, &oldmask, NULL);
291 free(readsp);
292 return (s);
293bad2:
294 if (lport)
295 (void)close(*fd2p);
296bad:
297 if (readsp)
298 free(readsp);
299 (void)close(s);
300 sigprocmask(SIG_SETMASK, &oldmask, NULL);
301 return (-1);
302}
303