xref: /openbsd-src/lib/libc/net/rcmd.c (revision 0e3c6306f9e8c62197134dcb9c6f7fdda47b72b8)
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