1df930be7Sderaadt /*
2f2ff748bSderaadt * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved.
3df930be7Sderaadt * Copyright (c) 1983, 1993, 1994
4df930be7Sderaadt * The Regents of the University of California. All rights reserved.
5df930be7Sderaadt *
6df930be7Sderaadt * Redistribution and use in source and binary forms, with or without
7df930be7Sderaadt * modification, are permitted provided that the following conditions
8df930be7Sderaadt * are met:
9df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright
10df930be7Sderaadt * notice, this list of conditions and the following disclaimer.
11df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
12df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the
13df930be7Sderaadt * documentation and/or other materials provided with the distribution.
14fe9c916fSderaadt * 3. Neither the name of the University nor the names of its contributors
15fe9c916fSderaadt * may be used to endorse or promote products derived from this software
16fe9c916fSderaadt * without specific prior written permission.
17df930be7Sderaadt *
18df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28df930be7Sderaadt * SUCH DAMAGE.
29df930be7Sderaadt */
30df930be7Sderaadt
31df930be7Sderaadt #include <sys/socket.h>
32df930be7Sderaadt #include <sys/stat.h>
33df930be7Sderaadt
34df930be7Sderaadt #include <netinet/in.h>
35df930be7Sderaadt #include <arpa/inet.h>
36df930be7Sderaadt
37df930be7Sderaadt #include <signal.h>
38df930be7Sderaadt #include <fcntl.h>
39df930be7Sderaadt #include <netdb.h>
40df930be7Sderaadt #include <unistd.h>
41aea60beeSderaadt #include <limits.h>
42df930be7Sderaadt #include <pwd.h>
43df930be7Sderaadt #include <errno.h>
44df930be7Sderaadt #include <stdio.h>
45df930be7Sderaadt #include <ctype.h>
46df930be7Sderaadt #include <string.h>
476bd11984Sderaadt #include <syslog.h>
4883dc3a58Sguenther #include <time.h>
49d4b79a24Sderaadt #include <stdlib.h>
50b510a672Sdlg #include <poll.h>
51df930be7Sderaadt
52df930be7Sderaadt int
rcmd(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)53db5b349cSotto rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
54db5b349cSotto const char *cmd, int *fd2p)
55df930be7Sderaadt {
5616582178Sitojun return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
5716582178Sitojun }
5816582178Sitojun
5916582178Sitojun int
rcmd_af(char ** ahost,int porta,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)60db5b349cSotto rcmd_af(char **ahost, int porta, const char *locuser, const char *remuser,
61db5b349cSotto const char *cmd, int *fd2p, int af)
6216582178Sitojun {
63aea60beeSderaadt static char hbuf[HOST_NAME_MAX+1];
6416582178Sitojun char pbuf[NI_MAXSERV];
6516582178Sitojun struct addrinfo hints, *res, *r;
6616582178Sitojun int error;
6716582178Sitojun struct sockaddr_storage from;
689a7fa6a3Smillert sigset_t oldmask, mask;
69df930be7Sderaadt pid_t pid;
7083dc3a58Sguenther int s, lport;
7183dc3a58Sguenther struct timespec timo;
72542c8643Smillert char c, *p;
7384f8024cSitojun int refused;
74db5b349cSotto in_port_t rport = porta;
75e12686f2Shalex int numread;
76542c8643Smillert
77542c8643Smillert /* call rcmdsh() with specified remote shell if appropriate. */
78865ae75bSmillert if (!issetugid() && (p = getenv("RSH")) && *p) {
79542c8643Smillert struct servent *sp = getservbyname("shell", "tcp");
80542c8643Smillert
81542c8643Smillert if (sp && sp->s_port == rport)
82542c8643Smillert return (rcmdsh(ahost, rport, locuser, remuser,
83542c8643Smillert cmd, p));
84542c8643Smillert }
85df930be7Sderaadt
861437b3aeSmillert /* use rsh(1) if non-root and remote port is shell. */
871437b3aeSmillert if (geteuid()) {
881437b3aeSmillert struct servent *sp = getservbyname("shell", "tcp");
8922c1ec3fSderaadt
901437b3aeSmillert if (sp && sp->s_port == rport)
9122c1ec3fSderaadt return (rcmdsh(ahost, rport, locuser, remuser,
9222c1ec3fSderaadt cmd, NULL));
931437b3aeSmillert }
941437b3aeSmillert
95df930be7Sderaadt pid = getpid();
9616582178Sitojun snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
9716582178Sitojun memset(&hints, 0, sizeof(hints));
9816582178Sitojun hints.ai_family = af;
9916582178Sitojun hints.ai_socktype = SOCK_STREAM;
10016582178Sitojun hints.ai_flags = AI_CANONNAME;
10116582178Sitojun error = getaddrinfo(*ahost, pbuf, &hints, &res);
10216582178Sitojun if (error) {
1030791ac16Shalex (void)fprintf(stderr, "rcmd: %s: %s\n", *ahost,
1040791ac16Shalex gai_strerror(error));
105df930be7Sderaadt return (-1);
106df930be7Sderaadt }
10716582178Sitojun if (res->ai_canonname) {
108f1a075daSlebel strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
10916582178Sitojun *ahost = hbuf;
11016582178Sitojun } else
11116582178Sitojun ; /*XXX*/
1121437b3aeSmillert
11316582178Sitojun r = res;
11484f8024cSitojun refused = 0;
11583dc3a58Sguenther timespecclear(&timo);
1169a7fa6a3Smillert sigemptyset(&mask);
1179a7fa6a3Smillert sigaddset(&mask, SIGURG);
1187581b1ccSderaadt sigprocmask(SIG_BLOCK, &mask, &oldmask);
11983dc3a58Sguenther for (timo.tv_sec = 1, lport = IPPORT_RESERVED - 1;;) {
12016582178Sitojun s = rresvport_af(&lport, r->ai_family);
121df930be7Sderaadt if (s < 0) {
122df930be7Sderaadt if (errno == EAGAIN)
123df930be7Sderaadt (void)fprintf(stderr,
124df930be7Sderaadt "rcmd: socket: All ports in use\n");
125df930be7Sderaadt else
126df930be7Sderaadt (void)fprintf(stderr, "rcmd: socket: %s\n",
127df930be7Sderaadt strerror(errno));
128f5a2605fSitojun if (r->ai_next) {
129f5a2605fSitojun r = r->ai_next;
130f5a2605fSitojun continue;
131f5a2605fSitojun } else {
1329a7fa6a3Smillert sigprocmask(SIG_SETMASK, &oldmask, NULL);
13316582178Sitojun freeaddrinfo(res);
134df930be7Sderaadt return (-1);
135df930be7Sderaadt }
136f5a2605fSitojun }
137df930be7Sderaadt fcntl(s, F_SETOWN, pid);
13816582178Sitojun if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
139df930be7Sderaadt break;
140df930be7Sderaadt (void)close(s);
141df930be7Sderaadt if (errno == EADDRINUSE) {
142df930be7Sderaadt lport--;
143df930be7Sderaadt continue;
144df930be7Sderaadt }
14584f8024cSitojun if (errno == ECONNREFUSED)
14684f8024cSitojun refused++;
14716582178Sitojun if (r->ai_next) {
148df930be7Sderaadt int oerrno = errno;
14916582178Sitojun char hbuf[NI_MAXHOST];
15016582178Sitojun const int niflags = NI_NUMERICHOST;
151df930be7Sderaadt
15216582178Sitojun hbuf[0] = '\0';
15316582178Sitojun if (getnameinfo(r->ai_addr, r->ai_addrlen,
15416582178Sitojun hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
155f27f4495Sderaadt strlcpy(hbuf, "(invalid)", sizeof hbuf);
15616582178Sitojun (void)fprintf(stderr, "connect to address %s: ", hbuf);
157df930be7Sderaadt errno = oerrno;
158df930be7Sderaadt perror(0);
15916582178Sitojun r = r->ai_next;
16016582178Sitojun hbuf[0] = '\0';
16116582178Sitojun if (getnameinfo(r->ai_addr, r->ai_addrlen,
16216582178Sitojun hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
163f27f4495Sderaadt strlcpy(hbuf, "(invalid)", sizeof hbuf);
16416582178Sitojun (void)fprintf(stderr, "Trying %s...\n", hbuf);
165df930be7Sderaadt continue;
166df930be7Sderaadt }
16783dc3a58Sguenther if (refused && timo.tv_sec <= 16) {
16883dc3a58Sguenther (void)nanosleep(&timo, NULL);
16983dc3a58Sguenther timo.tv_sec *= 2;
17084f8024cSitojun r = res;
17184f8024cSitojun refused = 0;
17284f8024cSitojun continue;
17384f8024cSitojun }
17416582178Sitojun (void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
17516582178Sitojun strerror(errno));
1769a7fa6a3Smillert sigprocmask(SIG_SETMASK, &oldmask, NULL);
17716582178Sitojun freeaddrinfo(res);
178df930be7Sderaadt return (-1);
179df930be7Sderaadt }
18016582178Sitojun /* given "af" can be PF_UNSPEC, we need the real af for "s" */
18116582178Sitojun af = r->ai_family;
18216582178Sitojun freeaddrinfo(res);
183df930be7Sderaadt if (fd2p == 0) {
184df930be7Sderaadt write(s, "", 1);
185df930be7Sderaadt lport = 0;
186df930be7Sderaadt } else {
187b510a672Sdlg struct pollfd pfd[2];
188df930be7Sderaadt char num[8];
18916582178Sitojun int s2 = rresvport_af(&lport, af), s3;
190e62fa231Sderaadt socklen_t len = sizeof(from);
191df930be7Sderaadt
192df930be7Sderaadt if (s2 < 0)
193df930be7Sderaadt goto bad;
194b510a672Sdlg
195df930be7Sderaadt listen(s2, 1);
196df930be7Sderaadt (void)snprintf(num, sizeof(num), "%d", lport);
197df930be7Sderaadt if (write(s, num, strlen(num)+1) != strlen(num)+1) {
198df930be7Sderaadt (void)fprintf(stderr,
199df930be7Sderaadt "rcmd: write (setting up stderr): %s\n",
200df930be7Sderaadt strerror(errno));
201df930be7Sderaadt (void)close(s2);
202df930be7Sderaadt goto bad;
203df930be7Sderaadt }
204efa7ea8eSderaadt again:
205b510a672Sdlg pfd[0].fd = s;
206b510a672Sdlg pfd[0].events = POLLIN;
207b510a672Sdlg pfd[1].fd = s2;
208b510a672Sdlg pfd[1].events = POLLIN;
209b510a672Sdlg
210df930be7Sderaadt errno = 0;
211b510a672Sdlg if (poll(pfd, 2, INFTIM) < 1 ||
212b510a672Sdlg (pfd[1].revents & (POLLIN|POLLHUP)) == 0) {
213df930be7Sderaadt if (errno != 0)
214df930be7Sderaadt (void)fprintf(stderr,
215b510a672Sdlg "rcmd: poll (setting up stderr): %s\n",
216df930be7Sderaadt strerror(errno));
217df930be7Sderaadt else
218df930be7Sderaadt (void)fprintf(stderr,
219b510a672Sdlg "poll: protocol failure in circuit setup\n");
220df930be7Sderaadt (void)close(s2);
221df930be7Sderaadt goto bad;
222df930be7Sderaadt }
223df930be7Sderaadt s3 = accept(s2, (struct sockaddr *)&from, &len);
224f8ab9db3Sderaadt if (s3 < 0) {
225f8ab9db3Sderaadt (void)fprintf(stderr,
226f8ab9db3Sderaadt "rcmd: accept: %s\n", strerror(errno));
227f8ab9db3Sderaadt lport = 0;
228f8ab9db3Sderaadt close(s2);
229f8ab9db3Sderaadt goto bad;
230f8ab9db3Sderaadt }
231f8ab9db3Sderaadt
232efa7ea8eSderaadt /*
233efa7ea8eSderaadt * XXX careful for ftp bounce attacks. If discovered, shut them
234efa7ea8eSderaadt * down and check for the real auxiliary channel to connect.
235efa7ea8eSderaadt */
23616582178Sitojun switch (from.ss_family) {
23716582178Sitojun case AF_INET:
23816582178Sitojun case AF_INET6:
23916582178Sitojun if (getnameinfo((struct sockaddr *)&from, len,
24016582178Sitojun NULL, 0, num, sizeof(num), NI_NUMERICSERV) == 0 &&
24116582178Sitojun atoi(num) != 20) {
24216582178Sitojun break;
24316582178Sitojun }
244efa7ea8eSderaadt close(s3);
245efa7ea8eSderaadt goto again;
24616582178Sitojun default:
24716582178Sitojun break;
248efa7ea8eSderaadt }
249df930be7Sderaadt (void)close(s2);
250f8ab9db3Sderaadt
251df930be7Sderaadt *fd2p = s3;
25216582178Sitojun switch (from.ss_family) {
25316582178Sitojun case AF_INET:
25416582178Sitojun case AF_INET6:
25516582178Sitojun if (getnameinfo((struct sockaddr *)&from, len,
25616582178Sitojun NULL, 0, num, sizeof(num), NI_NUMERICSERV) != 0 ||
25716582178Sitojun (atoi(num) >= IPPORT_RESERVED ||
25816582178Sitojun atoi(num) < IPPORT_RESERVED / 2)) {
259df930be7Sderaadt (void)fprintf(stderr,
260df930be7Sderaadt "socket: protocol failure in circuit setup.\n");
261df930be7Sderaadt goto bad2;
262df930be7Sderaadt }
26316582178Sitojun break;
26416582178Sitojun default:
26516582178Sitojun break;
26616582178Sitojun }
267df930be7Sderaadt }
268df930be7Sderaadt (void)write(s, locuser, strlen(locuser)+1);
269df930be7Sderaadt (void)write(s, remuser, strlen(remuser)+1);
270df930be7Sderaadt (void)write(s, cmd, strlen(cmd)+1);
271e12686f2Shalex if ((numread = read(s, &c, 1)) != 1) {
272df930be7Sderaadt (void)fprintf(stderr,
273e12686f2Shalex "rcmd: %s: %s\n", *ahost,
274e12686f2Shalex numread == -1 ? strerror(errno) : "Short read");
275df930be7Sderaadt goto bad2;
276df930be7Sderaadt }
277df930be7Sderaadt if (c != 0) {
278df930be7Sderaadt while (read(s, &c, 1) == 1) {
279df930be7Sderaadt (void)write(STDERR_FILENO, &c, 1);
280df930be7Sderaadt if (c == '\n')
281df930be7Sderaadt break;
282df930be7Sderaadt }
283df930be7Sderaadt goto bad2;
284df930be7Sderaadt }
2859a7fa6a3Smillert sigprocmask(SIG_SETMASK, &oldmask, NULL);
286df930be7Sderaadt return (s);
287df930be7Sderaadt bad2:
288df930be7Sderaadt if (lport)
289df930be7Sderaadt (void)close(*fd2p);
290df930be7Sderaadt bad:
291df930be7Sderaadt (void)close(s);
2929a7fa6a3Smillert sigprocmask(SIG_SETMASK, &oldmask, NULL);
293df930be7Sderaadt return (-1);
294df930be7Sderaadt }
295*0e3c6306Sguenther DEF_WEAK(rcmd_af);
296df930be7Sderaadt
297