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