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 <stdlib.h> 49 #include <poll.h> 50 51 int 52 rcmd(char **ahost, int rport, const char *locuser, const char *remuser, 53 const char *cmd, int *fd2p) 54 { 55 return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); 56 } 57 58 int 59 rcmd_af(char **ahost, int porta, const char *locuser, const char *remuser, 60 const char *cmd, int *fd2p, int af) 61 { 62 static char hbuf[HOST_NAME_MAX+1]; 63 char pbuf[NI_MAXSERV]; 64 struct addrinfo hints, *res, *r; 65 int error; 66 struct sockaddr_storage from; 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 int numread; 74 75 /* call rcmdsh() with specified remote shell if appropriate. */ 76 if (!issetugid() && (p = getenv("RSH")) && *p) { 77 struct servent *sp = getservbyname("shell", "tcp"); 78 79 if (sp && sp->s_port == rport) 80 return (rcmdsh(ahost, rport, locuser, remuser, 81 cmd, p)); 82 } 83 84 /* use rsh(1) if non-root and remote port is shell. */ 85 if (geteuid()) { 86 struct servent *sp = getservbyname("shell", "tcp"); 87 88 if (sp && sp->s_port == rport) 89 return (rcmdsh(ahost, rport, locuser, remuser, 90 cmd, NULL)); 91 } 92 93 pid = getpid(); 94 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport)); 95 memset(&hints, 0, sizeof(hints)); 96 hints.ai_family = af; 97 hints.ai_socktype = SOCK_STREAM; 98 hints.ai_flags = AI_CANONNAME; 99 error = getaddrinfo(*ahost, pbuf, &hints, &res); 100 if (error) { 101 (void)fprintf(stderr, "rcmd: %s: %s\n", *ahost, 102 gai_strerror(error)); 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 (fd2p == 0) { 181 write(s, "", 1); 182 lport = 0; 183 } else { 184 struct pollfd pfd[2]; 185 char num[8]; 186 int s2 = rresvport_af(&lport, af), s3; 187 socklen_t len = sizeof(from); 188 189 if (s2 < 0) 190 goto bad; 191 192 listen(s2, 1); 193 (void)snprintf(num, sizeof(num), "%d", lport); 194 if (write(s, num, strlen(num)+1) != strlen(num)+1) { 195 (void)fprintf(stderr, 196 "rcmd: write (setting up stderr): %s\n", 197 strerror(errno)); 198 (void)close(s2); 199 goto bad; 200 } 201 again: 202 pfd[0].fd = s; 203 pfd[0].events = POLLIN; 204 pfd[1].fd = s2; 205 pfd[1].events = POLLIN; 206 207 errno = 0; 208 if (poll(pfd, 2, INFTIM) < 1 || 209 (pfd[1].revents & (POLLIN|POLLHUP)) == 0) { 210 if (errno != 0) 211 (void)fprintf(stderr, 212 "rcmd: poll (setting up stderr): %s\n", 213 strerror(errno)); 214 else 215 (void)fprintf(stderr, 216 "poll: protocol failure in circuit setup\n"); 217 (void)close(s2); 218 goto bad; 219 } 220 s3 = accept(s2, (struct sockaddr *)&from, &len); 221 if (s3 < 0) { 222 (void)fprintf(stderr, 223 "rcmd: accept: %s\n", strerror(errno)); 224 lport = 0; 225 close(s2); 226 goto bad; 227 } 228 229 /* 230 * XXX careful for ftp bounce attacks. If discovered, shut them 231 * down and check for the real auxiliary channel to connect. 232 */ 233 switch (from.ss_family) { 234 case AF_INET: 235 case AF_INET6: 236 if (getnameinfo((struct sockaddr *)&from, len, 237 NULL, 0, num, sizeof(num), NI_NUMERICSERV) == 0 && 238 atoi(num) != 20) { 239 break; 240 } 241 close(s3); 242 goto again; 243 default: 244 break; 245 } 246 (void)close(s2); 247 248 *fd2p = s3; 249 switch (from.ss_family) { 250 case AF_INET: 251 case AF_INET6: 252 if (getnameinfo((struct sockaddr *)&from, len, 253 NULL, 0, num, sizeof(num), NI_NUMERICSERV) != 0 || 254 (atoi(num) >= IPPORT_RESERVED || 255 atoi(num) < IPPORT_RESERVED / 2)) { 256 (void)fprintf(stderr, 257 "socket: protocol failure in circuit setup.\n"); 258 goto bad2; 259 } 260 break; 261 default: 262 break; 263 } 264 } 265 (void)write(s, locuser, strlen(locuser)+1); 266 (void)write(s, remuser, strlen(remuser)+1); 267 (void)write(s, cmd, strlen(cmd)+1); 268 if ((numread = read(s, &c, 1)) != 1) { 269 (void)fprintf(stderr, 270 "rcmd: %s: %s\n", *ahost, 271 numread == -1 ? strerror(errno) : "Short read"); 272 goto bad2; 273 } 274 if (c != 0) { 275 while (read(s, &c, 1) == 1) { 276 (void)write(STDERR_FILENO, &c, 1); 277 if (c == '\n') 278 break; 279 } 280 goto bad2; 281 } 282 sigprocmask(SIG_SETMASK, &oldmask, NULL); 283 return (s); 284 bad2: 285 if (lport) 286 (void)close(*fd2p); 287 bad: 288 (void)close(s); 289 sigprocmask(SIG_SETMASK, &oldmask, NULL); 290 return (-1); 291 } 292 293