/*- * Copyright (c) 1983, 1990, 1993, 1994, 2002 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_SYS_FILIO_H # include #endif #include #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__ # include #else # include #endif #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef KERBEROS #include #include CREDENTIALS cred; Key_schedule schedule; int use_kerberos = 1, doencrypt; char dest_realm_buf[REALM_SZ], *dest_realm; extern char *krb_realmofhost(); #endif /* * rsh - remote shell */ int rfd2; char *copyargs __P ((char **)); void sendsig __P ((int)); void talk __P ((int, sigset_t *, pid_t, int)); void usage __P ((void)); void warning __P ((const char *, ...)); /* basename (argv[0]). NetBSD, linux, & gnu libc all define it. */ extern char *__progname; #ifdef KERBEROS #ifdef ENCRYPTION #define OPTIONS "8Kdek:l:nxVh" #else #define OPTIONS "8Kdek:l:nVh" #endif #else #define OPTIONS "8KLdel:nVh" #endif static const char *short_options = OPTIONS; static struct option long_options[] = { { "debug", no_argument, 0, 'd' }, { "user", required_argument, 0, 'l' }, { "escape", required_argument, 0, 'e' }, { "8-bit", no_argument, 0, '8' }, { "kerberos", no_argument, 0, 'K' }, { "no-input", no_argument, 0, 'n' }, #ifdef KERBEROS { "realm", required_argument, 0, 'k' }, { "encrypt", no_argument, 0, 'x' }, #endif { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { 0, 0, 0, 0 } }; static void pusage (FILE *stream) { fprintf (stream, "Usage: %s [-nd%s]%s[-l USER] [USER@]HOST [COMMAND [ARG...]]\n", __progname, #ifdef KERBEROS #ifdef ENCRYPTION "x", " [-k REALM] " #else "", " [-k REALM] " #endif #else "", " " #endif ); } /* Print a help message describing all options to STDOUT and exit with a status of 0. */ static void help (void) { pusage (stdout); puts ("Execute COMMAND on remote system HOST"); puts ("When use as rlogin:"); puts ("\ -8, --8-bit allows an eight-bit input data path at all times"); puts ("\ -E, --no-escape stops any character from being recognized as an escape\n\ character"); puts ("\ -d, --debug turns on socket debugging (see setsockopt(2))"); puts ("\ -e, --escape=CHAR allows user specification of the escape character,\n\ which is ``~'' by default"); puts ("\ -l, --user USER run as USER on the remote system"); #ifdef KERBEROS puts ("\ -K, --kerberos turns off all Kerberos authentication"); puts ("\ -k, --realm REALM Obtain tickets for the remote host in REALM\n\ instead of the remote host's realm"); # ifdef ENCRYPTION puts ("\ -x, --encrypt encrypt all data using DES"); # endif #endif puts ("\ -n, --no-input use /dev/null as input"); puts ("\ --help give this help list"); puts ("\ -V, --version print program version"); fprintf (stdout, "\nSubmit bug reports to %s.\n", PACKAGE_BUGREPORT); exit (0); } /* Print a message saying to use --help to STDERR, and exit with a status of 1. */ static void try_help (void) { fprintf (stderr, "Try `%s --help' for more information.\n", __progname); exit (1); } void usage() { pusage (stderr); try_help (); } int main (int argc, char **argv) { struct passwd *pw; struct servent *sp; sigset_t sigs, osigs; int asrsh, ch, dflag, nflag, rem; pid_t pid = 0; uid_t uid; char *args, *host, *user; #ifndef HAVE___PROGNAME extern char *__progname; __progname = argv[0]; #endif asrsh = dflag = nflag = 0; host = user = NULL; /* If called as something other than "rsh", use it as the host name */ { char *p = strrchr(argv[0], '/'); if (p) ++p; else p = argv[0]; if (strcmp (p, "rsh")) host = p; else asrsh = 1; } while ((ch = getopt_long (argc, argv, short_options, long_options, 0)) != EOF) { switch (ch) { case 'L': /* -8Lew are ignored to allow rlogin aliases */ case 'e': case 'w': case '8': break; case 'd': dflag = 1; break; case 'l': user = optarg; break; case 'K': #ifdef KERBEROS use_kerberos = 0; #endif break; #ifdef KERBEROS case 'k': strncpy (dest_realm_buf, optarg, sizeof dest_realm_buf); dest_realm_buf [REALM_SZ - 1] = '\0'; dest_realm = dest_realm_buf; break; # ifdef ENCRYPTION case 'x': doencrypt = 1; des_set_key (cred.session, schedule); break; # endif #endif case 'n': nflag = 1; break; case 'h': help (); case 'V': printf ("rsh (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); exit (0); case '?': try_help (); default: usage(); } } if (optind < argc) host = argv[optind++]; /* To few args. */ if (!host) usage (); /* If no further arguments, must have been called as rlogin. */ if (!argv[optind]) { if (asrsh) *argv = (char *)"rlogin"; seteuid (getuid ()); setuid (getuid ()); execv (PATH_RLOGIN, argv); errx (1, "can't exec %s", PATH_RLOGIN); } argc -= optind; argv += optind; /* We must be setuid root. */ if (geteuid ()) errx (1, "must be setuid root.\n"); if (!(pw = getpwuid (uid = getuid ()))) errx(1, "unknown user id"); /* Accept user1@host format, though "-l user2" overrides user1 */ { char *p = strchr (host, '@'); if (p) { *p = '\0'; if (!user && p > host) user = host; host = p + 1; if (*host == '\0') usage (); } if (!user) user = pw->pw_name; } #ifdef KERBEROS #ifdef ENCRYPTION /* -x turns off -n */ if (doencrypt) nflag = 0; #endif #endif args = copyargs (argv); sp = NULL; #ifdef KERBEROS if (use_kerberos) { sp = getservbyname ((doencrypt ? "ekshell" : "kshell"), "tcp"); if (sp == NULL) { use_kerberos = 0; warning ("can't get entry for %s/tcp service", doencrypt ? "ekshell" : "kshell"); } } #endif if (sp == NULL) sp = getservbyname("shell", "tcp"); if (sp == NULL) errx (1, "shell/tcp: unknown service"); #ifdef KERBEROS try_connect: if (use_kerberos) { struct hostent *hp; /* fully qualify hostname (needed for krb_realmofhost) */ hp = gethostbyname(host); if (hp != NULL && !(host = strdup (hp->h_name))) err (1, NULL); rem = KSUCCESS; errno = 0; if (dest_realm == NULL) dest_realm = krb_realmofhost (host); #ifdef ENCRYPTION if (doencrypt) rem = krcmd_mutual (&host, sp->s_port, user, args, &rfd2, dest_realm, &cred, schedule); else #endif rem = krcmd (&host, sp->s_port, user, args, &rfd2, dest_realm); if (rem < 0) { use_kerberos = 0; sp = getservbyname ("shell", "tcp"); if (sp == NULL) errx (1, "shell/tcp: unknown service"); if (errno == ECONNREFUSED) warning ("remote host doesn't support Kerberos"); if (errno == ENOENT) warning ("can't provide Kerberos auth data"); goto try_connect; } } else { if (doencrypt) errx (1, "the -x flag requires Kerberos authentication"); rem = rcmd (&host, sp->s_port, pw->pw_name, user, args, &rfd2); } #else rem = rcmd (&host, sp->s_port, pw->pw_name, user, args, &rfd2); #endif if (rem < 0) exit (1); if (rfd2 < 0) errx (1, "can't establish stderr"); if (dflag) { int one = 1; if (setsockopt (rem, SOL_SOCKET, SO_DEBUG, (char *) &one, sizeof one) < 0) warn ("setsockopt"); if (setsockopt (rfd2, SOL_SOCKET, SO_DEBUG, (char *) &one, sizeof one) < 0) warn ("setsockopt"); } seteuid (uid); setuid (uid); #ifdef HAVE_SIGACTION sigemptyset (&sigs); sigaddset (&sigs, SIGINT); sigaddset (&sigs, SIGQUIT); sigaddset (&sigs, SIGTERM); sigprocmask (SIG_BLOCK, &sigs, &osigs); #else sigs = sigmask (SIGINT) | sigmask (SIGQUIT) | sigmask (SIGTERM); osigs = sigblock (sigs); #endif if (signal (SIGINT, SIG_IGN) != SIG_IGN) signal (SIGINT, sendsig); if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) signal (SIGQUIT, sendsig); if (signal (SIGTERM, SIG_IGN) != SIG_IGN) signal (SIGTERM, sendsig); if (!nflag) { pid = fork (); if (pid < 0) err (1, "fork"); } #ifdef KERBEROS #ifdef ENCRYPTION if (!doencrypt) #endif #endif { int one = 1; ioctl (rfd2, FIONBIO, &one); ioctl (rem, FIONBIO, &one); } talk (nflag, &osigs, pid, rem); if (!nflag) kill (pid, SIGKILL); return 0; } void talk (int nflag, sigset_t *osigs, pid_t pid, int rem) { int cc, wc; fd_set readfrom, ready, rembits; char *bp, buf[BUFSIZ]; if (!nflag && pid == 0) { close (rfd2); reread: errno = 0; if ((cc = read (STDIN_FILENO, buf, sizeof buf)) <= 0) goto done; bp = buf; rewrite: FD_ZERO (&rembits); FD_SET (rem, &rembits); if (select (rem + 1, 0, &rembits, 0, 0) < 0) { if (errno != EINTR) err (1, "select"); goto rewrite; } if (!FD_ISSET (rem, &rembits)) goto rewrite; #ifdef KERBEROS #ifdef ENCRYPTION if (doencrypt) wc = des_write (rem, bp, cc); else #endif #endif wc = write (rem, bp, cc); if (wc < 0) { if (errno == EWOULDBLOCK) goto rewrite; goto done; } bp += wc; cc -= wc; if (cc == 0) goto reread; goto rewrite; done: shutdown (rem, 1); exit (0); } #ifdef HAVE_SIGACTION sigprocmask (SIG_SETMASK, osigs, NULL); #else sigsetmask (*osigs); #endif FD_ZERO (&readfrom); FD_SET (rfd2, &readfrom); FD_SET (rem, &readfrom); do { int maxfd = rem; if (rfd2 > maxfd) maxfd = rfd2; ready = readfrom; if (select (maxfd + 1, &ready, 0, 0, 0) < 0) { if (errno != EINTR) err (1, "select"); continue; } if (FD_ISSET (rfd2, &ready)) { errno = 0; #ifdef KERBEROS #ifdef CRYPT if (doenencryption) cc = des_read (rfd2, buf, sizeof buf); else #endif #endif cc = read (rfd2, buf, sizeof buf); if (cc <= 0) { if (errno != EWOULDBLOCK) FD_CLR (rfd2, &readfrom); } else write (2, buf, cc); } if (FD_ISSET (rem, &ready)) { errno = 0; #ifdef KERBEROS #ifdef ENCRYPTION if (doencrypt) cc = des_read (rem, buf, sizeof buf); else #endif #endif cc = read (rem, buf, sizeof buf); if (cc <= 0) { if (errno != EWOULDBLOCK) FD_CLR (rem, &readfrom); } else write (1, buf, cc); } } while (FD_ISSET (rfd2, &readfrom) || FD_ISSET (rem, &readfrom)); } void sendsig (int sig) { char signo; signo = sig; #ifdef KERBEROS #ifdef ENCRYPTION if (doencrypt) des_write (rfd2, &signo, 1); else #endif #endif write (rfd2, &signo, 1); } #ifdef KERBEROS /* VARARGS */ void #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__ warning (const char * fmt, ...) #else warning (va_alist) va_dcl #endif { va_list ap; #if !(defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__) const char *fmt; #endif fprintf (stderr, "%s: warning, using standard rsh: ", __progname); #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__ va_start (ap, fmt); #else va_start (ap); #endif fmt = va_arg (ap, char *); vfprintf (stderr, fmt, ap); va_end (ap); fprintf (stderr, ".\n"); } #endif char * copyargs (char **argv) { int cc; char **ap, *args, *p; cc = 0; for (ap = argv; *ap; ++ap) cc += strlen (*ap) + 1; if (!(args = malloc ((u_int)cc))) err (1, NULL); for (p = args, ap = argv; *ap; ++ap) { strcpy (p, *ap); for (p = strcpy (p, *ap); *p; ++p); if (ap[1]) *p++ = ' '; } return args; }