GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/net/rcmd.c Lines: 0 156 0.0 %
Date: 2017-11-07 Branches: 0 92 0.0 %

Line Branch Exec Source
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/socket.h>
32
#include <sys/stat.h>
33
34
#include <netinet/in.h>
35
#include <arpa/inet.h>
36
37
#include <signal.h>
38
#include <fcntl.h>
39
#include <netdb.h>
40
#include <unistd.h>
41
#include <limits.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 <time.h>
49
#include <stdlib.h>
50
#include <poll.h>
51
52
int
53
rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
54
    const char *cmd, int *fd2p)
55
{
56
	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
57
}
58
59
int
60
rcmd_af(char **ahost, int porta, const char *locuser, const char *remuser,
61
    const char *cmd, int *fd2p, int af)
62
{
63
	static char hbuf[HOST_NAME_MAX+1];
64
	char pbuf[NI_MAXSERV];
65
	struct addrinfo hints, *res, *r;
66
	int error;
67
	struct sockaddr_storage from;
68
	sigset_t oldmask, mask;
69
	pid_t pid;
70
	int s, lport;
71
	struct timespec timo;
72
	char c, *p;
73
	int refused;
74
	in_port_t rport = porta;
75
	int numread;
76
77
	/* call rcmdsh() with specified remote shell if appropriate. */
78
	if (!issetugid() && (p = getenv("RSH")) && *p) {
79
		struct servent *sp = getservbyname("shell", "tcp");
80
81
		if (sp && sp->s_port == rport)
82
			return (rcmdsh(ahost, rport, locuser, remuser,
83
			    cmd, p));
84
	}
85
86
	/* use rsh(1) if non-root and remote port is shell. */
87
	if (geteuid()) {
88
		struct servent *sp = getservbyname("shell", "tcp");
89
90
		if (sp && sp->s_port == rport)
91
			return (rcmdsh(ahost, rport, locuser, remuser,
92
			    cmd, NULL));
93
	}
94
95
	pid = getpid();
96
	snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
97
	memset(&hints, 0, sizeof(hints));
98
	hints.ai_family = af;
99
	hints.ai_socktype = SOCK_STREAM;
100
	hints.ai_flags = AI_CANONNAME;
101
	error = getaddrinfo(*ahost, pbuf, &hints, &res);
102
	if (error) {
103
		(void)fprintf(stderr, "rcmd: %s: %s\n", *ahost,
104
		    gai_strerror(error));
105
		return (-1);
106
	}
107
	if (res->ai_canonname) {
108
		strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
109
		*ahost = hbuf;
110
	} else
111
		; /*XXX*/
112
113
	r = res;
114
	refused = 0;
115
	timespecclear(&timo);
116
	sigemptyset(&mask);
117
	sigaddset(&mask, SIGURG);
118
	sigprocmask(SIG_BLOCK, &mask, &oldmask);
119
	for (timo.tv_sec = 1, lport = IPPORT_RESERVED - 1;;) {
120
		s = rresvport_af(&lport, r->ai_family);
121
		if (s < 0) {
122
			if (errno == EAGAIN)
123
				(void)fprintf(stderr,
124
				    "rcmd: socket: All ports in use\n");
125
			else
126
				(void)fprintf(stderr, "rcmd: socket: %s\n",
127
				    strerror(errno));
128
			if (r->ai_next) {
129
				r = r->ai_next;
130
				continue;
131
			} else {
132
				sigprocmask(SIG_SETMASK, &oldmask, NULL);
133
				freeaddrinfo(res);
134
				return (-1);
135
			}
136
		}
137
		fcntl(s, F_SETOWN, pid);
138
		if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
139
			break;
140
		(void)close(s);
141
		if (errno == EADDRINUSE) {
142
			lport--;
143
			continue;
144
		}
145
		if (errno == ECONNREFUSED)
146
			refused++;
147
		if (r->ai_next) {
148
			int oerrno = errno;
149
			char hbuf[NI_MAXHOST];
150
			const int niflags = NI_NUMERICHOST;
151
152
			hbuf[0] = '\0';
153
			if (getnameinfo(r->ai_addr, r->ai_addrlen,
154
			    hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
155
				strlcpy(hbuf, "(invalid)", sizeof hbuf);
156
			(void)fprintf(stderr, "connect to address %s: ", hbuf);
157
			errno = oerrno;
158
			perror(0);
159
			r = r->ai_next;
160
			hbuf[0] = '\0';
161
			if (getnameinfo(r->ai_addr, r->ai_addrlen,
162
			    hbuf, sizeof(hbuf), NULL, 0, niflags) != 0)
163
				strlcpy(hbuf, "(invalid)", sizeof hbuf);
164
			(void)fprintf(stderr, "Trying %s...\n", hbuf);
165
			continue;
166
		}
167
		if (refused && timo.tv_sec <= 16) {
168
			(void)nanosleep(&timo, NULL);
169
			timo.tv_sec *= 2;
170
			r = res;
171
			refused = 0;
172
			continue;
173
		}
174
		(void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
175
		    strerror(errno));
176
		sigprocmask(SIG_SETMASK, &oldmask, NULL);
177
		freeaddrinfo(res);
178
		return (-1);
179
	}
180
	/* given "af" can be PF_UNSPEC, we need the real af for "s" */
181
	af = r->ai_family;
182
	freeaddrinfo(res);
183
	if (fd2p == 0) {
184
		write(s, "", 1);
185
		lport = 0;
186
	} else {
187
		struct pollfd pfd[2];
188
		char num[8];
189
		int s2 = rresvport_af(&lport, af), s3;
190
		socklen_t len = sizeof(from);
191
192
		if (s2 < 0)
193
			goto bad;
194
195
		listen(s2, 1);
196
		(void)snprintf(num, sizeof(num), "%d", lport);
197
		if (write(s, num, strlen(num)+1) != strlen(num)+1) {
198
			(void)fprintf(stderr,
199
			    "rcmd: write (setting up stderr): %s\n",
200
			    strerror(errno));
201
			(void)close(s2);
202
			goto bad;
203
		}
204
again:
205
		pfd[0].fd = s;
206
		pfd[0].events = POLLIN;
207
		pfd[1].fd = s2;
208
		pfd[1].events = POLLIN;
209
210
		errno = 0;
211
		if (poll(pfd, 2, INFTIM) < 1 ||
212
		    (pfd[1].revents & (POLLIN|POLLHUP)) == 0) {
213
			if (errno != 0)
214
				(void)fprintf(stderr,
215
				    "rcmd: poll (setting up stderr): %s\n",
216
				    strerror(errno));
217
			else
218
				(void)fprintf(stderr,
219
				"poll: protocol failure in circuit setup\n");
220
			(void)close(s2);
221
			goto bad;
222
		}
223
		s3 = accept(s2, (struct sockaddr *)&from, &len);
224
		if (s3 < 0) {
225
			(void)fprintf(stderr,
226
			    "rcmd: accept: %s\n", strerror(errno));
227
			lport = 0;
228
			close(s2);
229
			goto bad;
230
		}
231
232
		/*
233
		 * XXX careful for ftp bounce attacks. If discovered, shut them
234
		 * down and check for the real auxiliary channel to connect.
235
		 */
236
		switch (from.ss_family) {
237
		case AF_INET:
238
		case AF_INET6:
239
			if (getnameinfo((struct sockaddr *)&from, len,
240
			    NULL, 0, num, sizeof(num), NI_NUMERICSERV) == 0 &&
241
			    atoi(num) != 20) {
242
				break;
243
			}
244
			close(s3);
245
			goto again;
246
		default:
247
			break;
248
		}
249
		(void)close(s2);
250
251
		*fd2p = s3;
252
		switch (from.ss_family) {
253
		case AF_INET:
254
		case AF_INET6:
255
			if (getnameinfo((struct sockaddr *)&from, len,
256
			    NULL, 0, num, sizeof(num), NI_NUMERICSERV) != 0 ||
257
			    (atoi(num) >= IPPORT_RESERVED ||
258
			     atoi(num) < IPPORT_RESERVED / 2)) {
259
				(void)fprintf(stderr,
260
				    "socket: protocol failure in circuit setup.\n");
261
				goto bad2;
262
			}
263
			break;
264
		default:
265
			break;
266
		}
267
	}
268
	(void)write(s, locuser, strlen(locuser)+1);
269
	(void)write(s, remuser, strlen(remuser)+1);
270
	(void)write(s, cmd, strlen(cmd)+1);
271
	if ((numread = read(s, &c, 1)) != 1) {
272
		(void)fprintf(stderr,
273
		    "rcmd: %s: %s\n", *ahost,
274
		    numread == -1 ? strerror(errno) : "Short read");
275
		goto bad2;
276
	}
277
	if (c != 0) {
278
		while (read(s, &c, 1) == 1) {
279
			(void)write(STDERR_FILENO, &c, 1);
280
			if (c == '\n')
281
				break;
282
		}
283
		goto bad2;
284
	}
285
	sigprocmask(SIG_SETMASK, &oldmask, NULL);
286
	return (s);
287
bad2:
288
	if (lport)
289
		(void)close(*fd2p);
290
bad:
291
	(void)close(s);
292
	sigprocmask(SIG_SETMASK, &oldmask, NULL);
293
	return (-1);
294
}
295
DEF_WEAK(rcmd_af);
296