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 42__attribute__((__used__)) 43static const char copyright[] = 44"@(#) Copyright (c) 1983, 1990, 1993, 1994\n\ 45 The Regents of the University of California. All rights reserved.\n"; 46#endif /* not lint */ 47 48#if 0 49#ifndef lint 50static const char sccsid[] = "From: @(#)rsh.c 8.3 (Berkeley) 4/6/94"; 51#endif /* not lint */ 52#endif 53 54#include <sys/cdefs.h> 55__FBSDID("$FreeBSD: src/usr.bin/rsh/rsh.c,v 1.35 2005/05/21 09:55:07 ru Exp $"); 56 57#include <sys/param.h> 58#include <sys/signal.h> 59#include <sys/socket.h> 60#include <sys/ioctl.h> 61#include <sys/file.h> 62#include <sys/time.h> 63 64#include <netinet/in.h> 65#include <netdb.h> 66 67#include <err.h> 68#include <errno.h> 69#ifndef __APPLE__ 70#include <libutil.h> 71#endif 72#include <paths.h> 73#include <pwd.h> 74#include <signal.h> 75#include <stdio.h> 76#include <stdlib.h> 77#include <string.h> 78#include <unistd.h> 79 80#ifdef __APPLE__ 81#define _PATH_RLOGIN "/usr/bin/rlogin" 82#endif 83 84/* 85 * rsh - remote shell 86 */ 87int rfd2; 88 89int family = PF_UNSPEC; 90char rlogin[] = "rlogin"; 91 92void connect_timeout(int); 93char *copyargs(char * const *); 94void sendsig(int); 95void talk(int, long, pid_t, int, int); 96void usage(void); 97 98int 99main(int argc, char *argv[]) 100{ 101 struct passwd const *pw; 102 struct servent const *sp; 103 long omask; 104 int argoff, asrsh, ch, dflag, nflag, one, rem; 105 pid_t pid = 0; 106 uid_t uid; 107 char *args, *host, *p, *user; 108 int timeout = 0; 109 110 argoff = asrsh = dflag = nflag = 0; 111 one = 1; 112 host = user = NULL; 113 114 if (argv[0] == NULL) 115 usage(); 116 117 /* if called as something other than "rsh", use it as the host name */ 118 if ((p = strrchr(argv[0], '/'))) 119 ++p; 120 else 121 p = argv[0]; 122 if (strcmp(p, "rsh")) 123 host = p; 124 else 125 asrsh = 1; 126 127 /* handle "rsh host flags" */ 128 if (!host && argc > 2 && argv[1][0] != '-') { 129 host = argv[1]; 130 argoff = 1; 131 } 132 133#define OPTIONS "468Lde:l:nt:w" 134 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 135 switch(ch) { 136 case '4': 137 family = PF_INET; 138 break; 139 140 case '6': 141 family = PF_INET6; 142 break; 143 144 case 'L': /* -8Lew are ignored to allow rlogin aliases */ 145 case 'e': 146 case 'w': 147 case '8': 148 break; 149 case 'd': 150 dflag = 1; 151 break; 152 case 'l': 153 user = optarg; 154 break; 155 case 'n': 156 nflag = 1; 157 break; 158 case 't': 159 timeout = atoi(optarg); 160 break; 161 case '?': 162 default: 163 usage(); 164 } 165 optind += argoff; 166 167 /* if haven't gotten a host yet, do so */ 168 if (!host && !(host = argv[optind++])) 169 usage(); 170 171 /* if no further arguments, must have been called as rlogin. */ 172 if (!argv[optind]) { 173 if (asrsh) 174 *argv = rlogin; 175 execv(_PATH_RLOGIN, argv); 176 err(1, "can't exec %s", _PATH_RLOGIN); 177 } 178 179 argc -= optind; 180 argv += optind; 181 182 if (!(pw = getpwuid(uid = getuid()))) 183 errx(1, "unknown user id"); 184 if (!user) 185 user = pw->pw_name; 186 187 args = copyargs(argv); 188 189 sp = NULL; 190 if (sp == NULL) 191 sp = getservbyname("shell", "tcp"); 192 if (sp == NULL) 193 errx(1, "shell/tcp: unknown service"); 194 195 if (timeout) { 196 signal(SIGALRM, connect_timeout); 197 alarm(timeout); 198 } 199 rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2, 200 family); 201 if (timeout) { 202 signal(SIGALRM, SIG_DFL); 203 alarm(0); 204 } 205 206 if (rem < 0) 207 exit(1); 208 209 if (rfd2 < 0) 210 errx(1, "can't establish stderr"); 211 if (dflag) { 212 if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, 213 sizeof(one)) < 0) 214 warn("setsockopt"); 215 if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one, 216 sizeof(one)) < 0) 217 warn("setsockopt"); 218 } 219 220 (void)setuid(uid); 221 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM)); 222 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 223 (void)signal(SIGINT, sendsig); 224 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 225 (void)signal(SIGQUIT, sendsig); 226 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 227 (void)signal(SIGTERM, sendsig); 228 229 if (!nflag) { 230 pid = fork(); 231 if (pid < 0) 232 err(1, "fork"); 233 } 234 else 235 (void)shutdown(rem, SHUT_WR); 236 237 (void)ioctl(rfd2, FIONBIO, &one); 238 (void)ioctl(rem, FIONBIO, &one); 239 240 talk(nflag, omask, pid, rem, timeout); 241 242 if (!nflag) 243 (void)kill(pid, SIGKILL); 244 exit(0); 245} 246 247void 248talk(int nflag, long omask, pid_t pid, int rem, int timeout) 249{ 250 int cc, wc; 251 fd_set readfrom, ready, rembits; 252 char buf[BUFSIZ]; 253 const char *bp; 254 struct timeval tvtimeout; 255 int nfds, srval; 256 257 if (!nflag && pid == 0) { 258 (void)close(rfd2); 259 260reread: errno = 0; 261 if ((cc = read(0, buf, sizeof buf)) <= 0) 262 goto done; 263 bp = buf; 264 265rewrite: 266 if (rem >= FD_SETSIZE) 267 errx(1, "descriptor too big"); 268 FD_ZERO(&rembits); 269 FD_SET(rem, &rembits); 270 nfds = rem + 1; 271 if (select(nfds, 0, &rembits, 0, 0) < 0) { 272 if (errno != EINTR) 273 err(1, "select"); 274 goto rewrite; 275 } 276 if (!FD_ISSET(rem, &rembits)) 277 goto rewrite; 278 wc = write(rem, bp, cc); 279 if (wc < 0) { 280 if (errno == EWOULDBLOCK) 281 goto rewrite; 282 goto done; 283 } 284 bp += wc; 285 cc -= wc; 286 if (cc == 0) 287 goto reread; 288 goto rewrite; 289done: 290 (void)shutdown(rem, SHUT_WR); 291 exit(0); 292 } 293 294 tvtimeout.tv_sec = timeout; 295 tvtimeout.tv_usec = 0; 296 297 (void)sigsetmask(omask); 298 if (rfd2 >= FD_SETSIZE || rem >= FD_SETSIZE) 299 errx(1, "descriptor too big"); 300 FD_ZERO(&readfrom); 301 FD_SET(rfd2, &readfrom); 302 FD_SET(rem, &readfrom); 303 nfds = MAX(rfd2+1, rem+1); 304 do { 305 ready = readfrom; 306 if (timeout) { 307 srval = select(nfds, &ready, 0, 0, &tvtimeout); 308 } else { 309 srval = select(nfds, &ready, 0, 0, 0); 310 } 311 312 if (srval < 0) { 313 if (errno != EINTR) 314 err(1, "select"); 315 continue; 316 } 317 if (srval == 0) 318 errx(1, "timeout reached (%d seconds)", timeout); 319 if (FD_ISSET(rfd2, &ready)) { 320 errno = 0; 321 cc = read(rfd2, buf, sizeof buf); 322 if (cc <= 0) { 323 if (errno != EWOULDBLOCK) 324 FD_CLR(rfd2, &readfrom); 325 } else 326 (void)write(STDERR_FILENO, buf, cc); 327 } 328 if (FD_ISSET(rem, &ready)) { 329 errno = 0; 330 cc = read(rem, buf, sizeof buf); 331 if (cc <= 0) { 332 if (errno != EWOULDBLOCK) 333 FD_CLR(rem, &readfrom); 334 } else 335 (void)write(STDOUT_FILENO, buf, cc); 336 } 337 } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom)); 338} 339 340void 341connect_timeout(int sig) 342{ 343 char message[] = "timeout reached before connection completed.\n"; 344 345 write(STDERR_FILENO, message, sizeof(message) - 1); 346 _exit(1); 347} 348 349void 350sendsig(int sig) 351{ 352 char signo; 353 354 signo = sig; 355 (void)write(rfd2, &signo, 1); 356} 357 358char * 359copyargs(char * const *argv) 360{ 361 int cc; 362 char *args, *p; 363 char * const *ap; 364 365 cc = 0; 366 for (ap = argv; *ap; ++ap) 367 cc += strlen(*ap) + 1; 368 if (!(args = malloc((u_int)cc))) 369 err(1, NULL); 370 for (p = args, ap = argv; *ap; ++ap) { 371 (void)strcpy(p, *ap); 372 for (p = strcpy(p, *ap); *p; ++p); 373 if (ap[1]) 374 *p++ = ' '; 375 } 376 return (args); 377} 378 379void 380usage(void) 381{ 382 383 (void)fprintf(stderr, 384 "usage: rsh [-46dn] [-l username] [-t timeout] host [command]\n"); 385 exit(1); 386} 387