rsh.c revision 325474
1/*- 2 * Copyright (c) 1983, 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41#ifndef lint 42static const char copyright[] = 43"@(#) Copyright (c) 1983, 1990, 1993, 1994\n\ 44 The Regents of the University of California. All rights reserved.\n"; 45#endif /* not lint */ 46 47#if 0 48#ifndef lint 49static const char sccsid[] = "From: @(#)rsh.c 8.3 (Berkeley) 4/6/94"; 50#endif /* not lint */ 51#endif 52 53#include <sys/cdefs.h> 54__FBSDID("$FreeBSD: stable/10/usr.bin/rsh/rsh.c 325474 2017-11-06 12:45:51Z eugen $"); 55 56#include <sys/param.h> 57#include <sys/signal.h> 58#include <sys/socket.h> 59#include <sys/ioctl.h> 60#include <sys/file.h> 61#include <sys/time.h> 62 63#include <netinet/in.h> 64#include <netdb.h> 65 66#include <err.h> 67#include <errno.h> 68#include <libutil.h> 69#include <paths.h> 70#include <pwd.h> 71#include <signal.h> 72#include <stdio.h> 73#include <stdlib.h> 74#include <string.h> 75#include <unistd.h> 76 77/* 78 * rsh - remote shell 79 */ 80int rfd2; 81 82int family = PF_UNSPEC; 83char rlogin[] = "rlogin"; 84 85void connect_timeout(int); 86char *copyargs(char * const *); 87void sendsig(int); 88void talk(int, int, long, pid_t, int, int); 89void usage(void); 90 91int 92main(int argc, char *argv[]) 93{ 94 struct passwd const *pw; 95 struct servent const *sp; 96 long omask; 97 int argoff, asrsh, ch, dflag, nflag, Nflag, one, rem; 98 pid_t pid = 0; 99 uid_t uid; 100 char *args, *host, *p, *user; 101 int timeout = 0; 102 103 argoff = asrsh = dflag = nflag = Nflag = 0; 104 one = 1; 105 host = user = NULL; 106 107 /* if called as something other than "rsh", use it as the host name */ 108 if ((p = strrchr(argv[0], '/'))) 109 ++p; 110 else 111 p = argv[0]; 112 if (strcmp(p, "rsh")) 113 host = p; 114 else 115 asrsh = 1; 116 117 /* handle "rsh host flags" */ 118 if (!host && argc > 2 && argv[1][0] != '-') { 119 host = argv[1]; 120 argoff = 1; 121 } 122 123#define OPTIONS "468LNde:l:nt:w" 124 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 125 switch(ch) { 126 case '4': 127 family = PF_INET; 128 break; 129 130 case '6': 131 family = PF_INET6; 132 break; 133 134 case 'N': 135 Nflag = 1; 136 nflag = 0; 137 break; 138 case 'L': /* -8Lew are ignored to allow rlogin aliases */ 139 case 'e': 140 case 'w': 141 case '8': 142 break; 143 case 'd': 144 dflag = 1; 145 break; 146 case 'l': 147 user = optarg; 148 break; 149 case 'n': 150 nflag = 1; 151 Nflag = 0; 152 break; 153 case 't': 154 timeout = atoi(optarg); 155 break; 156 case '?': 157 default: 158 usage(); 159 } 160 optind += argoff; 161 162 /* if haven't gotten a host yet, do so */ 163 if (!host && !(host = argv[optind++])) 164 usage(); 165 166 /* if no further arguments, must have been called as rlogin. */ 167 if (!argv[optind]) { 168 if (asrsh) 169 *argv = rlogin; 170 execv(_PATH_RLOGIN, argv); 171 err(1, "can't exec %s", _PATH_RLOGIN); 172 } 173 174 argc -= optind; 175 argv += optind; 176 177 if (!(pw = getpwuid(uid = getuid()))) 178 errx(1, "unknown user id"); 179 if (!user) 180 user = pw->pw_name; 181 182 args = copyargs(argv); 183 184 sp = NULL; 185 if (sp == NULL) 186 sp = getservbyname("shell", "tcp"); 187 if (sp == NULL) 188 errx(1, "shell/tcp: unknown service"); 189 190 if (timeout) { 191 signal(SIGALRM, connect_timeout); 192 alarm(timeout); 193 } 194 rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2, 195 family); 196 if (timeout) { 197 signal(SIGALRM, SIG_DFL); 198 alarm(0); 199 } 200 201 if (rem < 0) 202 exit(1); 203 204 if (rfd2 < 0) 205 errx(1, "can't establish stderr"); 206 if (dflag) { 207 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, 208 sizeof(one)) < 0) 209 warn("setsockopt"); 210 if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one, 211 sizeof(one)) < 0) 212 warn("setsockopt"); 213 } 214 215 (void)setuid(uid); 216 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM)); 217 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 218 (void)signal(SIGINT, sendsig); 219 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 220 (void)signal(SIGQUIT, sendsig); 221 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 222 (void)signal(SIGTERM, sendsig); 223 224 if (!nflag) { 225 pid = fork(); 226 if (pid < 0) 227 err(1, "fork"); 228 } 229 else 230 (void)shutdown(rem, SHUT_WR); 231 232 (void)ioctl(rfd2, FIONBIO, &one); 233 (void)ioctl(rem, FIONBIO, &one); 234 235 talk(nflag, Nflag, omask, pid, rem, timeout); 236 237 if (!nflag) 238 (void)kill(pid, SIGKILL); 239 exit(0); 240} 241 242void 243talk(int nflag, int Nflag, long omask, pid_t pid, int rem, int timeout) 244{ 245 int cc, wc; 246 fd_set readfrom, ready, rembits; 247 char buf[BUFSIZ]; 248 const char *bp; 249 struct timeval tvtimeout; 250 int nfds, srval; 251 252 if (!nflag && pid == 0) { 253 (void)close(rfd2); 254 255reread: errno = 0; 256 if ((cc = read(0, buf, sizeof buf)) <= 0) 257 goto done; 258 bp = buf; 259 260rewrite: 261 if (rem >= FD_SETSIZE) 262 errx(1, "descriptor too big"); 263 FD_ZERO(&rembits); 264 FD_SET(rem, &rembits); 265 nfds = rem + 1; 266 if (select(nfds, 0, &rembits, 0, 0) < 0) { 267 if (errno != EINTR) 268 err(1, "select"); 269 goto rewrite; 270 } 271 if (!FD_ISSET(rem, &rembits)) 272 goto rewrite; 273 wc = write(rem, bp, cc); 274 if (wc < 0) { 275 if (errno == EWOULDBLOCK) 276 goto rewrite; 277 goto done; 278 } 279 bp += wc; 280 cc -= wc; 281 if (cc == 0) 282 goto reread; 283 goto rewrite; 284done: if (!Nflag) 285 (void)shutdown(rem, SHUT_WR); 286 exit(0); 287 } 288 289 tvtimeout.tv_sec = timeout; 290 tvtimeout.tv_usec = 0; 291 292 (void)sigsetmask(omask); 293 if (rfd2 >= FD_SETSIZE || rem >= FD_SETSIZE) 294 errx(1, "descriptor too big"); 295 FD_ZERO(&readfrom); 296 FD_SET(rfd2, &readfrom); 297 FD_SET(rem, &readfrom); 298 nfds = MAX(rfd2+1, rem+1); 299 do { 300 ready = readfrom; 301 if (timeout) { 302 srval = select(nfds, &ready, 0, 0, &tvtimeout); 303 } else { 304 srval = select(nfds, &ready, 0, 0, 0); 305 } 306 307 if (srval < 0) { 308 if (errno != EINTR) 309 err(1, "select"); 310 continue; 311 } 312 if (srval == 0) 313 errx(1, "timeout reached (%d seconds)", timeout); 314 if (FD_ISSET(rfd2, &ready)) { 315 errno = 0; 316 cc = read(rfd2, buf, sizeof buf); 317 if (cc <= 0) { 318 if (errno != EWOULDBLOCK) 319 FD_CLR(rfd2, &readfrom); 320 } else 321 (void)write(STDERR_FILENO, buf, cc); 322 } 323 if (FD_ISSET(rem, &ready)) { 324 errno = 0; 325 cc = read(rem, buf, sizeof buf); 326 if (cc <= 0) { 327 if (errno != EWOULDBLOCK) 328 FD_CLR(rem, &readfrom); 329 } else 330 (void)write(STDOUT_FILENO, buf, cc); 331 } 332 } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom)); 333} 334 335void 336connect_timeout(int sig __unused) 337{ 338 char message[] = "timeout reached before connection completed.\n"; 339 340 write(STDERR_FILENO, message, sizeof(message) - 1); 341 _exit(1); 342} 343 344void 345sendsig(int sig) 346{ 347 char signo; 348 349 signo = sig; 350 (void)write(rfd2, &signo, 1); 351} 352 353char * 354copyargs(char * const *argv) 355{ 356 int cc; 357 char *args, *p; 358 char * const *ap; 359 360 cc = 0; 361 for (ap = argv; *ap; ++ap) 362 cc += strlen(*ap) + 1; 363 if (!(args = malloc((u_int)cc))) 364 err(1, NULL); 365 for (p = args, ap = argv; *ap; ++ap) { 366 (void)strcpy(p, *ap); 367 for (p = strcpy(p, *ap); *p; ++p); 368 if (ap[1]) 369 *p++ = ' '; 370 } 371 return (args); 372} 373 374void 375usage(void) 376{ 377 378 (void)fprintf(stderr, 379 "usage: rsh [-46Ndn] [-l username] [-t timeout] host [command]\n"); 380 exit(1); 381} 382