From f4c6f6a6118684666a683530e0c5fe4869fd96b6 Mon Sep 17 00:00:00 2001 From: Richard Basch Date: Tue, 3 Jan 1995 02:52:31 +0000 Subject: [PATCH] * krsh.c * krshd.c Added encrypted rsh support. It still isn't entirely secure; as the command-line could be spoofed by an active attack, but the data sharing is... git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@4790 dc483132-0cff-0310-8789-dd5450dbe970 --- src/appl/bsd/ChangeLog | 6 + src/appl/bsd/krsh.c | 188 ++++++++++++++++++++++---- src/appl/bsd/krshd.c | 292 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 446 insertions(+), 40 deletions(-) diff --git a/src/appl/bsd/ChangeLog b/src/appl/bsd/ChangeLog index 31d559f8e..4107cb712 100644 --- a/src/appl/bsd/ChangeLog +++ b/src/appl/bsd/ChangeLog @@ -1,5 +1,11 @@ Mon Jan 2 12:35:18 1995 Richard Basch (probe@tardis) + * krsh.c + * krshd.c + Added encrypted rsh support. + It still isn't entirely secure; as the command-line could + be spoofed by an active attack, but the data sharing is... + * krlogind.c Ultrix doesn't have a fully functional POSIX termios. diff --git a/src/appl/bsd/krsh.c b/src/appl/bsd/krsh.c index f863d70e7..c9b7a0ef3 100644 --- a/src/appl/bsd/krsh.c +++ b/src/appl/bsd/krsh.c @@ -75,12 +75,31 @@ int nflag; 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 @@ -118,7 +137,6 @@ main(argc, argv0) krb5_flags authopts; krb5_error_code status; int fflag = 0, Fflag = 0; - int xflag = 0; int debug_port = 0; #endif /* KERBEROS */ @@ -179,7 +197,7 @@ main(argc, argv0) */ if (argc > 0 && !strncmp(*argv, "-x", 2)) { argv++, argc--; - xflag++; + encrypt_flag++; goto another; } if (argc > 0 && !strncmp(*argv, "-f", 2)) { @@ -261,17 +279,6 @@ main(argc, argv0) 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"); @@ -280,7 +287,13 @@ main(argc, argv0) 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) @@ -321,7 +334,7 @@ main(argc, argv0) 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, @@ -334,6 +347,16 @@ main(argc, argv0) 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); @@ -393,8 +416,10 @@ main(argc, argv0) 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; @@ -419,7 +444,7 @@ main(argc, argv0) } 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; @@ -454,7 +479,7 @@ main(argc, argv0) } 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); @@ -463,7 +488,7 @@ main(argc, argv0) } 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); @@ -476,9 +501,9 @@ main(argc, argv0) 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); } @@ -487,7 +512,7 @@ main(argc, argv0) krb5_sigtype sendsig(signo) char signo; { - (void) write(rfd2, &signo, 1); + (void) des_write(rfd2, &signo, 1); } @@ -498,13 +523,15 @@ void try_normal(argv) { 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++; @@ -521,4 +548,115 @@ void try_normal(argv) 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 */ diff --git a/src/appl/bsd/krshd.c b/src/appl/bsd/krshd.c index f63711188..427f3cb0d 100644 --- a/src/appl/bsd/krshd.c +++ b/src/appl/bsd/krshd.c @@ -143,6 +143,10 @@ char copyright[] = #endif /* CRAY */ #include + +#ifdef POSIX_TERMIOS +#include +#endif #ifdef HAVE_SYS_FILIO_H /* get FIONBIO from sys/filio.h, so what if it is a compatibility feature */ @@ -159,11 +163,37 @@ char copyright[] = #include #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 */ @@ -279,6 +309,13 @@ main(argc, argv) 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); @@ -462,7 +499,7 @@ doit(f, fromp) 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; @@ -518,6 +555,9 @@ doit(f, fromp) } #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)) @@ -627,6 +667,8 @@ doit(f, fromp) 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"); @@ -996,13 +1038,21 @@ doit(f, fromp) } #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"); @@ -1029,19 +1079,31 @@ doit(f, fromp) #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 @@ -1054,6 +1116,15 @@ doit(f, fromp) #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)); @@ -1061,9 +1132,21 @@ doit(f, fromp) 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."); @@ -1077,8 +1160,18 @@ doit(f, fromp) #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]); } @@ -1109,8 +1202,13 @@ doit(f, fromp) 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); @@ -1515,9 +1613,17 @@ recvauth(netf, peersin, peeraddr) 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); } @@ -1532,4 +1638,160 @@ recvauth(netf, peersin, peeraddr) 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 */ -- 2.26.2