krb5_sigtype sendsig();
#ifdef KERBEROS
-char *krb_realm = (char *)0;
-void try_normal();
+
#ifndef UCB_RSH
#define UCB_RSH "/usr/ucb/rsh"
#endif
+#ifdef BUFSIZ
+#undef BUFSIZ
#endif
+#define BUFSIZ 4096
+
+char des_inbuf[2*BUFSIZ]; /* needs to be > largest read size */
+char des_outbuf[2*BUFSIZ]; /* needs to be > largest write size */
+krb5_data desinbuf,desoutbuf;
+krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+krb5_creds *cred;
+
+int encrypt_flag = 0;
+char *krb_realm = (char *)0;
+void try_normal();
+
+#else /* KERBEROS */
+
+#define des_read read
+#define des_write write
+
+#endif /* KERBEROS */
#ifndef RLOGIN_PROGRAM
#ifdef KERBEROS
krb5_flags authopts;
krb5_error_code status;
int fflag = 0, Fflag = 0;
- int xflag = 0;
int debug_port = 0;
#endif /* KERBEROS */
*/
if (argc > 0 && !strncmp(*argv, "-x", 2)) {
argv++, argc--;
- xflag++;
+ encrypt_flag++;
goto another;
}
if (argc > 0 && !strncmp(*argv, "-f", 2)) {
exit(1);
}
- /* Unlike the other rlogin flags, we should warn if `-x' is
- given and rlogin is not run, because the user could be
- dangerously confused otherwise. He might think he's got a
- secure rsh channel, and there's no such thing yet. */
- if (xflag)
- {
- fprintf (stderr, "%s: Encrypted rsh is not yet available.\n",
- argv0[0]);
- return 1;
- }
-
pwd = getpwuid(getuid());
if (pwd == 0) {
fprintf(stderr, "who are you?\n");
cc = 0;
for (ap = argv; *ap; ap++)
cc += strlen(*ap) + 1;
+ if (encrypt_flag)
+ cc += 3;
cp = args = (char *) malloc(cc);
+ if (encrypt_flag) {
+ strcpy(args, "-x ");
+ cp += 3;
+ }
for (ap = argv; *ap; ap++) {
(void) strcpy(cp, *ap);
while (*cp)
pwd->pw_name,
user ? user : pwd->pw_name,
args, &rfd2, "host", krb_realm,
- 0, /* No need for returned credentials */
+ &cred,
0, /* No need for sequence number */
0, /* No need for server seq # */
(struct sockaddr_in *) 0,
error_message(status));
try_normal(argv0);
}
+
+ /* Setup for des_read and write */
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
+ krb5_use_keytype(&eblock,cred->keyblock.keytype);
+ if (status = krb5_process_key(&eblock,&cred->keyblock)) {
+ fprintf(stderr, "%s: Cannot process session key : %s.\n",
+ argv0, error_message(status));
+ exit(1);
+ }
#else /* !KERBEROS */
rem = rcmd(&host, sp->s_port, pwd->pw_name,
user ? user : pwd->pw_name, args, &rfd2);
exit(1);
}
}
- ioctl(rfd2, FIONBIO, &one);
- ioctl(rem, FIONBIO, &one);
+ if (!encrypt_flag) {
+ ioctl(rfd2, FIONBIO, &one);
+ ioctl(rem, FIONBIO, &one);
+ }
if (nflag == 0 && pid == 0) {
char *bp;
int wc;
}
if (FD_ISSET(rem, &rembits) == 0)
goto rewrite;
- wc = write(rem, bp, cc);
+ wc = des_write(rem, bp, cc);
if (wc < 0) {
if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
goto rewrite;
}
if (FD_ISSET(rfd2, &ready)) {
errno = 0;
- cc = read(rfd2, buf, sizeof buf);
+ cc = des_read(rfd2, buf, sizeof buf);
if (cc <= 0) {
if ((errno != EWOULDBLOCK) && (errno != EAGAIN))
FD_CLR(rfd2, &readfrom);
}
if (FD_ISSET(rem, &ready)) {
errno = 0;
- cc = read(rem, buf, sizeof buf);
+ cc = des_read(rem, buf, sizeof buf);
if (cc <= 0) {
if ((errno != EWOULDBLOCK) && (errno != EAGAIN))
FD_CLR(rem, &readfrom);
exit(0);
usage:
fprintf(stderr,
- "usage: \trsh host [ -l login ] [ -n ] [ -f / -F] command\n");
+ "usage: \trsh host [ -l login ] [ -n ] [ -x ] [ -f / -F] command\n");
fprintf(stderr,
- "OR \trsh [ -l login ] [-n ] [ -f / -F ] host command\n");
+ "OR \trsh [ -l login ] [-n ] [ -x ] [ -f / -F ] host command\n");
exit(1);
}
krb5_sigtype sendsig(signo)
char signo;
{
- (void) write(rfd2, &signo, 1);
+ (void) des_write(rfd2, &signo, 1);
}
{
char *host;
+ if (encrypt_flag)
+ exit(1);
+
/*
* if we were invoked as 'rsh host mumble', strip off the rsh
* from arglist.
*
* We always want to call the Berkeley rsh as 'host mumble'
*/
-
host = strrchr(argv[0], '/');
if (host)
host++;
perror("exec");
exit(1);
}
+
+
+char storage[2*BUFSIZ];
+int nstored = 0;
+char *store_ptr = storage;
+
+int des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ long net_len,rd_len;
+ int cc;
+ unsigned char len_buf[4];
+
+ if (!encrypt_flag)
+ return(read(fd, buf, len));
+
+ if (nstored >= len) {
+ memcpy(buf, store_ptr, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+ if ((cc = krb5_net_read(fd, len_buf, 4)) != 4) {
+ /* XXX can't read enough, pipe must have closed */
+ return(0);
+ }
+ rd_len =
+ ((len_buf[0]<<24) | (len_buf[1]<<16) | (len_buf[2]<<8) | len_buf[3]);
+ net_len = krb5_encrypt_size(rd_len,eblock.crypto_entry);
+ if ((net_len <= 0) || (net_len > sizeof(des_inbuf))) {
+ /* preposterous length; assume out-of-sync; only
+ recourse is to close connection, so return 0 */
+ fprintf(stderr,"Read size problem.\n");
+ return(0);
+ }
+ if ((cc = krb5_net_read(fd, desinbuf.data, net_len)) != net_len) {
+ /* pipe must have closed, return 0 */
+ fprintf(stderr, "Read error: length received %d != expected %d.\n",
+ cc, net_len);
+ return(0);
+ }
+ /* decrypt info */
+ if (cc = krb5_decrypt(desinbuf.data, (krb5_pointer) storage,
+ net_len, &eblock, 0)) {
+ fprintf(stderr,"Cannot decrypt data from network\n");
+ fprintf(stderr,"eblock->key = %x\n", *((long *)eblock.key));
+ return(0);
+ }
+ store_ptr = storage;
+ nstored = rd_len;
+ if (nstored > len) {
+ memcpy(buf, store_ptr, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+
+ return(nreturned);
+}
+
+
+
+int des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ unsigned char len_buf[4];
+
+ if (!encrypt_flag)
+ return(write(fd, buf, len));
+
+ desoutbuf.length = krb5_encrypt_size(len,eblock.crypto_entry);
+ if (desoutbuf.length > sizeof(des_outbuf)){
+ fprintf(stderr,"Write size problem.\n");
+ return(-1);
+ }
+ if (( krb5_encrypt((krb5_pointer)buf,
+ desoutbuf.data,
+ len,
+ &eblock,
+ 0))){
+ fprintf(stderr,"Write encrypt problem.\n");
+ return(-1);
+ }
+
+ len_buf[0] = (len & 0xff000000) >> 24;
+ len_buf[1] = (len & 0xff0000) >> 16;
+ len_buf[2] = (len & 0xff00) >> 8;
+ len_buf[3] = (len & 0xff);
+ (void) write(fd, len_buf, 4);
+ if (write(fd, desoutbuf.data,desoutbuf.length) != desoutbuf.length){
+ fprintf(stderr,"Could not write out all data.\n");
+ return(-1);
+ }
+ else return(len);
+}
#endif /* KERBEROS */
#endif /* CRAY */
#include <syslog.h>
+
+#ifdef POSIX_TERMIOS
+#include <termios.h>
+#endif
#ifdef HAVE_SYS_FILIO_H
/* get FIONBIO from sys/filio.h, so what if it is a compatibility feature */
#include <com_err.h>
#include "loginpaths.h"
-#define ARGSTR "rRkKD:?"
+#define ARGSTR "rRxXeEkKD:?"
+
+#define SECURE_MESSAGE "This rsh session is using DES encryption for all data transmissions.\r\n"
+
+#ifdef BUFSIZ
+#undef BUFSIZ
+#endif
+#define BUFSIZ 5120
+
+#define MAXRETRIES 4
+
+char des_inbuf[2*BUFSIZ]; /* needs to be > largest read size */
+krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+char des_outbuf[2*BUFSIZ]; /* needs to be > largest write size */
+krb5_data desinbuf,desoutbuf;
+
+void fatal();
+int v5_des_read();
+int v5_des_write();
+
+int (*des_read)() = v5_des_read;
+int (*des_write)() = v5_des_write;
+
+int do_encrypt = 0;
+int netf;
#else /* !KERBEROS */
#define ARGSTR "rRD:?"
+#define (*des_read) read
+#define (*des_write) write
#endif /* KERBEROS */
if (must_pass_one)
must_pass_one = 0;
break;
+
+ case 'x':
+ case 'X':
+ case 'e':
+ case 'E':
+ do_encrypt = 1;
+ break;
#endif
case 'D':
debug_port = atoi(optarg);
struct hostent *hp;
char *hostname;
short port;
- int pv[2], cc;
+ int pv[2], pw[2], px[2], cc;
fd_set ready, readfrom;
char buf[BUFSIZ], sig;
int one = 1;
}
#ifdef KERBEROS
krb5_init_ets();
+ netf = f;
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf;
if ((must_pass_rhosts || must_pass_one)
&& (fromp->sin_port >= IPPORT_RESERVED ||
fromp->sin_port < IPPORT_RESERVED/2))
error("Authentication failed: %s\n", error_message(status));
exit(1);
}
+ if (!strncmp(cmdbuf, "-x ", 3))
+ do_encrypt = 1;
#else
getstr(f, remuser, sizeof(remuser), "remuser");
getstr(f, locuser, sizeof(locuser), "locuser");
}
#endif
- (void) write(2, "\0", 1);
+ (void) write(2, "", 1);
if (port) {
if (pipe(pv) < 0) {
error("Can't make pipe.\n");
goto signout_please;
}
+ if (pipe(pw) < 0) {
+ error("Can't make pipe 2.\n");
+ goto signout_please;
+ }
+ if (pipe(px) < 0) {
+ error("Can't make pipe 3.\n");
+ goto signout_please;
+ }
pid = fork();
if (pid == -1) {
error("Fork failed.\n");
#endif
(void) close(0); (void) close(1); (void) close(2);
- (void) close(f); (void) close(pv[1]);
+ (void) close(pv[1]);
+ (void) close(pw[1]);
+ (void) close(px[0]);
+
+ ioctl(pv[0], FIONBIO, (char *)&one);
+ ioctl(pw[0], FIONBIO, (char *)&one);
+ /* should set s nbio! */
+
+ if (do_encrypt)
+ if (((*des_write)(s, SECURE_MESSAGE, sizeof(SECURE_MESSAGE))) < 0)
+ fatal(pw[0], "Cannot encrypt-write network.");
+
FD_ZERO(&readfrom);
+ FD_SET(f, &readfrom);
FD_SET(s, &readfrom);
FD_SET(pv[0], &readfrom);
- ioctl(pv[0], FIONBIO, (char *)&one);
- /* should set s nbio! */
+ FD_SET(pw[0], &readfrom);
+
do {
ready = readfrom;
if (select(8*sizeof(ready), &ready, (fd_set *)0,
(fd_set *)0, (struct timeval *)0) < 0)
break;
if (FD_ISSET(s, &ready)) {
- if (read(s, &sig, 1) <= 0)
+ if ((*des_read)(s, &sig, 1) <= 0)
FD_CLR(s, &readfrom);
else {
#ifdef POSIX_SIGNALS
#endif
}
}
+ if (FD_ISSET(f, &ready)) {
+ errno = 0;
+ cc = (*des_read)(f, buf, sizeof(buf));
+ if (cc <= 0) {
+ (void) close(px[1]);
+ FD_CLR(f, &readfrom);
+ } else
+ (void) write(px[1], buf, cc);
+ }
if (FD_ISSET(pv[0], &ready)) {
errno = 0;
cc = read(pv[0], buf, sizeof (buf));
shutdown(s, 1+1);
FD_CLR(pv[0], &readfrom);
} else
- (void) write(s, buf, cc);
+ (void) (*des_write)(s, buf, cc);
+ }
+ if (FD_ISSET(pw[0], &ready)) {
+ errno = 0;
+ cc = read(pw[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ shutdown(f, 1+1);
+ FD_CLR(pw[0], &readfrom);
+ } else
+ (void) (*des_write)(f, buf, cc);
}
- } while (FD_ISSET(s, &readfrom) || FD_ISSET(pv[0], &readfrom));
+ } while (FD_ISSET(s, &readfrom) ||
+ FD_ISSET(f, &readfrom) ||
+ FD_ISSET(pv[0], &readfrom) ||
+ FD_ISSET(pw[0], &readfrom));
#ifdef KERBEROS
syslog(LOG_INFO ,
"Shell process completed.");
#else
setpgrp();
#endif
- (void) close(s); (void) close(pv[0]);
- dup2(pv[1], 2);
+ (void) close(s);
+ (void) close(f);
+ (void) close(pw[0]);
+ (void) close(pv[0]);
+ (void) close(px[1]);
+
+ (void) dup2(px[0], 0);
+ (void) dup2(pw[1], 1);
+ (void) dup2(pv[1], 2);
+
+ (void) close(px[0]);
+ (void) close(pw[1]);
(void) close(pv[1]);
}
cp++;
else
cp = pwd->pw_shell;
-
- execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+
+ if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
+ execl(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3, 0);
+ }
+ else
+ execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+
perror(pwd->pw_shell);
perror(cp);
exit(1);
if (status = krb5_unparse_name(client, &kremuser))
return status;
+ /* Setup eblock for encrypted sessions. */
+ krb5_use_keytype(&eblock, ticket->enc_part2->session->keytype);
+ if (status = krb5_process_key(&eblock, ticket->enc_part2->session))
+ fatal(netf, "Permission denied");
+
+ /* Null out the "session" because eblock.key references the session
+ * key here, and we do not want krb5_free_ticket() to destroy it. */
+ ticket->enc_part2->session = 0;
+
if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) {
- error("Error reading message: %s\n",
- error_message(status));
+ error("Error reading message: %s\n", error_message(status));
exit(1);
}
return 0;
}
+
+char storage[2*BUFSIZ]; /* storage for the decryption */
+int nstored = 0;
+char *store_ptr = storage;
+
+int
+v5_des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ krb5_ui_4 net_len,rd_len;
+ int cc,retry;
+ unsigned char len_buf[4];
+
+ if (!do_encrypt)
+ return(read(fd, buf, len));
+
+ if (nstored >= len) {
+ memcpy(buf, store_ptr, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+ if ((cc = krb5_net_read(fd, (char *)len_buf, 4)) != 4) {
+ if ((cc < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
+ return(cc);
+ /* XXX can't read enough, pipe must have closed */
+ return(0);
+ }
+ rd_len =
+ ((len_buf[0]<<24) |
+ (len_buf[1]<<16) |
+ (len_buf[2]<<8) |
+ len_buf[3]);
+ net_len = krb5_encrypt_size(rd_len, eblock.crypto_entry);
+ if (net_len < 0 || net_len > sizeof(des_inbuf)) {
+ /* XXX preposterous length, probably out of sync.
+ act as if pipe closed */
+ syslog(LOG_ERR,"Read size problem (rd_len=%d, net_len=%d)",
+ rd_len, net_len);
+ return(0);
+ }
+ retry = 0;
+ datard:
+ if ((cc = krb5_net_read(fd, desinbuf.data, net_len)) != net_len) {
+ /* XXX can't read enough, pipe must have closed */
+ if ((cc < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
+ retry++;
+ if (retry > MAXRETRIES){
+ syslog(LOG_ERR, "des_read retry count exceeded %d\n", retry);
+ return(0);
+ }
+ sleep(1);
+ goto datard;
+ }
+ syslog(LOG_ERR,
+ "Read data received %d != expected %d.",
+ cc, net_len);
+ return(0);
+ }
+
+ /* decrypt info */
+ if (krb5_decrypt(desinbuf.data, (krb5_pointer) storage, net_len,
+ &eblock, 0)) {
+ syslog(LOG_ERR,"Read decrypt problem.");
+ return(0);
+ }
+
+ store_ptr = storage;
+ nstored = rd_len;
+ if (nstored > len) {
+ memcpy(buf, store_ptr, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+ return(nreturned);
+}
+
+
+int
+v5_des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ unsigned char len_buf[4];
+
+ if (!do_encrypt)
+ return(write(fd, buf, len));
+
+ desoutbuf.length = krb5_encrypt_size(len, eblock.crypto_entry);
+ if (desoutbuf.length > sizeof(des_outbuf)){
+ syslog(LOG_ERR,"Write size problem (%d > %d)",
+ desoutbuf.length, sizeof(des_outbuf));
+ return(-1);
+ }
+
+ if (krb5_encrypt((krb5_pointer)buf, desoutbuf.data, len, &eblock, 0)) {
+ syslog(LOG_ERR,"Write encrypt problem.");
+ return(-1);
+ }
+
+ len_buf[0] = (len & 0xff000000) >> 24;
+ len_buf[1] = (len & 0xff0000) >> 16;
+ len_buf[2] = (len & 0xff00) >> 8;
+ len_buf[3] = (len & 0xff);
+ (void) write(fd, len_buf, 4);
+
+ if (write(fd, desoutbuf.data, desoutbuf.length) != desoutbuf.length){
+ syslog(LOG_ERR,"Could not write out all data.");
+ return(-1);
+ }
+ else return(len);
+}
+
+
+void fatal(f, msg)
+ int f;
+ char *msg;
+{
+ char buf[512];
+ int out = 1 ; /* Output queue of f */
+
+ buf[0] = '\01'; /* error indicator */
+ (void) sprintf(buf + 1, "%s: %s.\r\n",progname, msg);
+ if ((f == netf) && (pid > 0))
+ (void) (*des_write)(f, buf, strlen(buf));
+ else
+ (void) write(f, buf, strlen(buf));
+ syslog(LOG_ERR,"%s\n",msg);
+ if (pid > 0) {
+ signal(SIGCHLD,SIG_IGN);
+ kill(pid,SIGKILL);
+#ifdef POSIX_TERMIOS
+ (void) tcflush(1, TCOFLUSH);
+#else
+ (void) ioctl(f, TIOCFLUSH, (char *)&out);
+#endif
+ cleanup();
+ }
+ exit(1);
+}
#endif /* KERBEROS */