From schwim@whatmore.Stanford.EDU Thu May 21 18:30:37 1998 Received: from MIT.EDU (SOUTH-STATION-ANNEX.MIT.EDU [18.72.1.2]) by rt-11.MIT.EDU (8.7.5/8.7.3) with SMTP id SAA24323 for ; Thu, 21 May 1998 18:30:36 -0400 Received: from whatmore.Stanford.EDU by MIT.EDU with SMTP id AA06399; Thu, 21 May 98 18:30:27 EDT Received: (from schwim@localhost) by whatmore.Stanford.EDU (8.8.8/8.8.8) id PAA29933; Thu, 21 May 1998 15:30:32 -0700 (PDT) Message-Id: <199805212230.PAA29933@whatmore.Stanford.EDU> Date: Thu, 21 May 1998 15:30:32 -0700 (PDT) From: Larry Schwimmer To: krb5-bugs@MIT.EDU Subject: PATCH: krb5-1.0.5 appl/bsd/krlogin.c patches >Number: 598 >Category: krb5-appl >Synopsis: multipe rlogin patches >Confidential: no >Severity: serious >Priority: high >Responsible: krb5-unassigned >State: open >Class: sw-bug >Submitter-Id: unknown >Arrival-Date: Thu May 21 18:31:00 EDT 1998 >Last-Modified: Fri Sep 14 11:49:11 EDT 2001 >Originator: Larry Schwimmer >Organization: Stanford University >Release: krb5-1.0.5 >Environment: HP-UX/Linux, ALL >Description: This note addresses five issues. Since the patches for these issues are not always seperable, I am including it as a single report. 1) connections to Solaris from HP-UX and Linux fail due to oob timing rlogin fails from HP-UX and Linux to Solaris 2.6. If the oob window size negotiation message is received too closely to the arrival of the login data stream, the rlogin child reader process receives confirmation of oob data but not the data, presumably because of differing implementations of OOB data due to the original ambiguity in the OOB protocol. I was able to fix the problem for Linux by reverting the code to use the standard rlogin handling of SIGURG. I fixed the problem on HP-UX by setting the SO_OOBINLINE socket option, which ensured the oob data was in the regular data stream and thus forced the read to block properly. (I am unsure if just the SO_OOBINLINE fix would also work for Linux, but this code has been tested on Solaris 2.5.1, Solaris 2.6, Solaris x86 2.5.1, HP-UX 10.20, Linux 2.0.33 (libc5 and glibc6), AIX 4.1, AIX 4.2, IRIX 5.3, IRIX 6.2, IRIX 6.4, DUNIX 3.2c, and DUNIX 4.0). This problem was difficult to track down and part of the reason I added krb4 fallback code to the krb5 rlogin in hopes that krb5 fixed the problem. The problem was that the krb5 code had the same problem as the krb4 code. Connections from Linux and HP-UX machines to Solaris 2.6 failed in some circumstances. The problem was timing related, which is why the problem is hidden by login.krb5, which performs a two second sleep to workaround an unrelated and supposedly fixed bug. 2) terminal speed segfault problem If the terminal speed is greater than 38400 baud, rlogin segfaults. rlogin takes an offset into an array which did not know about higher terminal speeds. Rather than apply the bandaid of adding the additional speeds, I used the rlogin netkit approach and added a static getspeedstr function which handles the known cases and has a sensible default fallback. This also avoids the problem of special handling of systems like HP-UX, which have different speed settings and indexes. The bug report this patch addresses came from a user of an Ultra 10 system running Solaris 2.6. 3) krb4 fallback This part of the patch relies on the patch to kcmd.c I sent in a seperate note. Added support to fallback to krb4 if compiled under KRB5_KRB4_COMPAT and if krb5 fails or if the (added) -4 flag is specified. Added a -k4 flag to specify the k4 realm. 4) try_normal revisions (recursion, runtime binding, commandline compat) If the krb5 rlogin is installed in the vendor location and the kerberos connection fails and encryption is not specified, the program will call itself infinitely. I revised the try_normal code to stat itself and the target file to be run to try to avoid this case. In the process of redoing that code, I removed the hard-coded default and changed it to do runtime lookup since the hard-coded defaults are wrong on some systems (Linux, for example). I also revised the code to handle rlogin [options] hostname probably by pushing the hostname back to the first argument. While Solaris rlogin honors a proper UNIX commandline, most systems only honor the archaic convention of the hostname as the zeroth or first argument. 5) Support for username@hostname As with my patch for rsh, rlogin username@hostname also seems intuitive and saves three keystrokes. 6) code formatting In the process of editing the code, I ended up reformatting some of the lines with the standard tab conventions that the code uses. 7) Man page updates to document the features. yours, Larry Schwimmer schwim@leland.stanford.edu Leland Systems Group --- appl/bsd/krlogin.c.orig Fri Feb 6 19:41:17 1998 +++ appl/bsd/krlogin.c Wed May 20 16:12:33 1998 @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -148,6 +149,17 @@ * data, the output buffer represents the entire packet. */ +#ifdef KRB5_KRB4_COMPAT +#include +char krb4_realm[REALM_SZ] = ""; +CREDENTIALS v4_cred; +MSG_DAT msg_data; +Key_schedule v4_schedule; +int v4_des_read(), v4_des_write(); +#endif + +int (*des_read)(), (*des_write)(); +int v5_des_read(), v5_des_write(); char des_inbuf[2*RLOGIN_BUFSIZ]; /* needs to be > largest read size */ char des_outpkt[2*RLOGIN_BUFSIZ+4]; /* needs to be > largest write size */ krb5_data desinbuf,desoutbuf; @@ -161,14 +173,10 @@ struct sockaddr_in local, foreign; krb5_context bsd_context; -#ifndef UCB_RLOGIN -#define UCB_RLOGIN "/usr/ucb/rlogin" -#endif - #include "rpaths.h" #else /* !KERBEROS */ -#define des_read read -#define des_write write +#define (*des_read) read +#define (*des_write) write #endif /* KERBEROS */ # ifndef TIOCPKT_WINDOW @@ -185,10 +193,13 @@ char *getenv(); -char *name; +char *name = (char *)0; int rem = -1; /* Remote socket fd */ char cmdchar = '~'; int eight = 1; /* Default to 8 bit transmission */ +#if defined(KERBEROS) && defined(KRB5_KRB4_COMPAT) +int do_krb4 = 0; /* Default to krb5 */ +#endif int no_local_escape = 0; int null_local_username = 0; int flow = 1; /* Default is to allow flow @@ -200,16 +211,58 @@ the original characteristics */ int confirm = 0; /* ask if ~. is given before dying. */ int litout; -#if defined(hpux) || defined(__hpux) -char *speeds[] = -{ "0", "50", "75", "110", "134", "150", "200", "300", "600", - "900", "1200", "1800", "2400", "3600", "4800", "7200", "9600", - "19200", "38400", "EXTA", "EXTB" }; -#else -char *speeds[] = -{ "0", "50", "75", "110", "134", "150", "200", "300", - "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; + +/* Terminal speed consists of non-standard, non-portable indexes + * into arrays. Use a function instead of an array to ensure that + * a reasonable value is returned rather than an offset into an + * array which may or may not be correct or even legal. Default + * to 9600 for unknown speeds. + */ +static char *getspeedstr(speed) + speed_t speed; +{ + switch(speed) { + case B0: return "0"; /* this should never happen */ + case B50: return "50"; + case B75: return "75"; + case B110: return "110"; + case B134: return "134"; + case B150: return "150"; + case B200: return "200"; + case B300: return "300"; + case B600: return "600"; +#ifdef B900 + case B900: return "900"; +#endif + case B1200: return "1200"; + case B1800: return "1800"; + case B2400: return "2400"; +#ifdef B3600 + case B3600: return "3600"; +#endif +#ifdef B7200 + case B7200: return "7200"; +#endif + case B4800: return "4800"; + case B9600: return "9600"; + case B19200: return "19200"; + case B38400: return "38400"; +#if defined(B57600) + case B57600: return "57600"; +#endif +#if defined(B115200) + case B115200: return "115200"; +#endif +#if defined(B230400) + case B230400: return "230400"; +#endif +#if defined(B460800) + case B460800: return "460800"; #endif + default: return "9600"; + } +} + char term[256] = "network"; #ifndef POSIX_SIGNALS @@ -335,10 +388,16 @@ int on = 1; #ifdef KERBEROS char **orig_argv = argv; + int host_offset = argc; int sock; krb5_flags authopts; krb5_error_code status; #endif +#ifdef KRB5_KRB4_COMPAT + KRB4_32 v4_status; + KTEXT_ST v4_ticket; + KRB4_32 v4_authopts = 0; +#endif int debug_port = 0; memset(&defaultservent, 0, sizeof(struct servent)); @@ -352,6 +411,7 @@ another: if (argc > 0 && host == 0 && strncmp(*argv, "-", 1)) { host = *argv; + host_offset -= argc; argv++, argc--; goto another; } @@ -461,9 +521,38 @@ argv++, argc--; goto another; } +#ifdef KRB5_KRB4_COMPAT + if (argc > 0 && !strcmp(*argv, "-4")) { + do_krb4 = 1; + argv++, argc--; + goto another; + } + if (argc > 0 && !strcmp(*argv, "-k4")) { + argv++, argc--; + if (argc == 0) { + fprintf(stderr, + "rlogin: -k4 flag must be followed with a realm name.\n"); + exit (1); + } + strncpy(krb4_realm, *argv, REALM_SZ); + argv++, argc--; + goto another; + } +#endif /* KRB5_KRB4_COMPAT */ #endif /* KERBEROS */ if (host == 0) goto usage; + /* Honor username@hostname */ + { + char *np; + + if ((np = strchr(host,'@')) != (char *)0 && + (np != host) && (*(np+1) != '\0')) { + name = host; + host = np+1; + *np = '\0'; + } + } if (argc > 0) goto usage; pwd = getpwuid(getuid()); @@ -522,21 +611,13 @@ not a table index. */ sprintf (term + strlen (term), "%d", ospeed); else { - (void) strcat(term, speeds[ospeed]); -#if 0 - /* XXX - Not used, since the above code was - * not ifdef'd and it relied on cfget... */ - - /* some "posix" systems don't have cfget... - * so used CBAUD if it's there */ - (void) strcat(term, speeds[ttyb.c_cflag & CBAUD]); -#endif + (void) strcat(term, getspeedstr(ospeed)); } } #else if (ioctl(0, TIOCGETP, &ttyb) == 0) { (void) strcat(term, "/"); - (void) strcat(term, speeds[ttyb.sg_ospeed]); + (void) strcat(term, getspeedstr(ttyb.sg_ospeed)); } #endif (void) get_window_size(0, &winsize); @@ -562,6 +643,7 @@ (void) sigaction(SIGPIPE, &sa, (struct sigaction *)0); (void) sigemptyset(&urgmask); + (void) sigaddset(&urgmask, SIGURG); (void) sigaddset(&urgmask, SIGUSR1); oldmask = &omask; (void) sigprocmask(SIG_BLOCK, &urgmask, oldmask); @@ -576,6 +658,10 @@ #ifdef KERBEROS authopts = AP_OPTS_MUTUAL_REQUIRED; +#ifdef KRB5_KRB4_COMPAT + if (encrypt_flag) + v4_authopts = KOPT_DO_MUTUAL; +#endif /* Piggy-back forwarding flags on top of authopts; */ /* they will be reset in kcmd */ @@ -584,27 +670,67 @@ if (Fflag) authopts |= OPTS_FORWARDABLE_CREDS; - status = kcmd(&sock, &host, debug_port, - null_local_username ? NULL : pwd->pw_name, - name ? name : pwd->pw_name, term, - 0, "host", krb_realm, - &cred, - 0, /* No need for sequence number */ - 0, /* No need for server seq # */ - &local, &foreign, - authopts, - 0); /* Not any port # */ - if (status) { +#ifdef KRB5_KRB4_COMPAT + if (!do_krb4) +#endif + status = kcmd(&sock, &host, debug_port, + null_local_username ? NULL : pwd->pw_name, + name ? name : pwd->pw_name, term, + 0, "host", krb_realm, + &cred, + 0, /* No need for sequence number */ + 0, /* No need for server seq # */ + &local, &foreign, + authopts, + 0); /* Not any port # */ + if ( +#ifdef KRB5_KRB4_COMPAT + !do_krb4 && +#endif + status == 0) { + des_read = &v5_des_read; + des_write = &v5_des_write; + } else { /* should check for KDC_PR_UNKNOWN, NO_TKT_FILE here -- XXX */ - if (status != -1) - fprintf(stderr, - "%s: kcmd to host %s failed - %s\n",orig_argv[0], host, - error_message(status)); - try_normal(orig_argv); +#ifdef KRB5_KRB4_COMPAT + /* Try V4 kcmd */ + v4_status = k4cmd(&sock, &host, debug_port, + null_local_username ? NULL : pwd->pw_name, + name ? name : pwd->pw_name, term, + 0, &v4_ticket, "rcmd", krb4_realm, + &v4_cred, v4_schedule, &msg_data, + &local, &foreign, + v4_authopts); + if (v4_status == 0) { + des_read = &v4_des_read; + des_write = &v4_des_write; + } else { + des_read = &read; + des_write = &write; + if (do_krb4 || status == KRB5_FCC_NOFILE) + fprintf(stderr, "%s: Kerberos rcmd failed: %s.\n", + orig_argv[0], + (v4_status == -1) ? "rcmd protocol failure" : + krb_err_txt[v4_status]); + else +#endif + fprintf(stderr, + "%s: kcmd to host %s failed - %s\n", + orig_argv[0], host, + (status == -1) ? + "rcmd protocol failure" : + error_message(status)); + try_normal(host_offset,orig_argv); +#ifdef KRB5_KRB4_COMPAT + } +#endif } rem = sock; - + /* setup eblock for des_read and write */ +#ifdef KRB5_KRB4_COMPAT + if (!do_krb4 && status == 0) { +#endif krb5_use_enctype(bsd_context, &eblock,cred->keyblock.enctype); if ( status = krb5_process_key(bsd_context, &eblock,&cred->keyblock)) { fprintf(stderr, @@ -612,6 +738,9 @@ orig_argv[0], error_message(status)); exit(1); } +#ifdef KRB5_KRB4_COMPAT + } +#endif #else rem = rcmd(&host, debug_port, null_local_username ? NULL : pwd->pw_name, @@ -621,6 +750,10 @@ if (rem < 0) exit(1); +#ifdef __hpux + if (setsockopt(rem,SOL_SOCKET,SO_OOBINLINE,(char *)&on, sizeof(on)) < 0) + perror("rlogin: setsockopt (SO_OOBINLINE)"); +#endif if (options & SO_DEBUG && setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char*)&on, sizeof (on)) < 0) perror("rlogin: setsockopt (SO_DEBUG)"); @@ -636,7 +769,11 @@ #ifdef KERBEROS fprintf (stderr, "usage: rlogin host [-option] [-option...] [-k realm ] [-t ttytype] [-l username]\n"); +#ifdef KRB5_KRB4_COMPAT + fprintf (stderr, " where option is e, 4, 7, 8, noflow, n, a, x, f, F, or c\n"); +#else /* KRB5_KRB4_COMPAT */ fprintf (stderr, " where option is e, 7, 8, noflow, n, a, x, f, F, or c\n"); +#endif /* KRB5_KRB4_COMPAT */ #else /* !KERBEROS */ fprintf (stderr, "usage: rlogin host [-option] [-option...] [-t ttytype] [-l username]\n"); @@ -679,6 +816,7 @@ int child; krb5_sigtype catchild KRB5_PROTOTYPE((int)); krb5_sigtype writeroob KRB5_PROTOTYPE((int)); +krb5_sigtype copytochild KRB5_PROTOTYPE((int)); int defflags, tabflag; int deflflags; @@ -810,7 +948,9 @@ sa.sa_handler = writeroob; (void) sigaction(SIGUSR1, &sa, (struct sigaction *)0); - + sa.sa_handler = copytochild; + (void) sigaction(SIGURG, &sa, (struct sigaction *)0); + sigprocmask(SIG_SETMASK, oldmask, (sigset_t*)0); sa.sa_handler = catchild; @@ -948,26 +1088,26 @@ union wait status; #endif int pid; - - again: + + for(;;) { #ifdef HAVE_WAITPID - pid = waitpid(-1, &status, WNOHANG|WUNTRACED); + pid = waitpid(-1, &status, WNOHANG|WUNTRACED); #else - pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0); + pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0); #endif - if (pid == 0) - return; - /* - * if the child (reader) dies, just quit - */ + if (pid == 0) + return; + /* + * if the child (reader) dies, just quit + */ #ifdef WAIT_USES_INT - if (pid < 0 || (pid == child && !WIFSTOPPED(status))) - done(status); + if (pid < 0 || (pid == child && !WIFSTOPPED(status))) + done(status); #else - if ((pid < 0) || ((pid == child) && (!WIFSTOPPED(status)))) - done((int)(status.w_termsig | status.w_retcode)); + if ((pid < 0) || ((pid == child) && (!WIFSTOPPED(status)))) + done((int)(status.w_termsig | status.w_retcode)); #endif - goto again; + } } @@ -1010,8 +1150,11 @@ for (;;) { n = read(0, &c, 1); if (n <= 0) { - if (n < 0 && errno == EINTR) - continue; + if (n == 0) + break; + if (errno == EINTR) + continue; + prf("read error: %s",strerror(errno)); break; } /* @@ -1067,9 +1210,9 @@ #endif if (c != cmdchar) - (void) des_write(rem, &cmdchar, 1); + (void) (*des_write)(rem, &cmdchar, 1); } - if (des_write(rem, &c, 1) == 0) { + if ((*des_write)(rem, &c, 1) == 0) { prf("line gone"); break; } @@ -1184,7 +1327,7 @@ wp->ws_col = htons(winsize.ws_col); wp->ws_xpixel = htons(winsize.ws_xpixel); wp->ws_ypixel = htons(winsize.ws_ypixel); - (void) des_write(rem, obuf, sizeof(obuf)); + (void) (*des_write)(rem, obuf, sizeof(obuf)); } @@ -1195,6 +1338,7 @@ #define READING 1 #define WRITING 2 +static sigjmp_buf rcvtop; char rcvbuf[8 * 1024]; int rcvcnt; int rcvstate; @@ -1219,8 +1363,31 @@ #endif #endif mark = 0; - - recv(rem, &mark, 1, MSG_OOB); + + while (recv(rem, &mark, 1, MSG_OOB) < 0) { + switch(errno) { + case EWOULDBLOCK: + /* Urgent data not here yet. It may not be possible + * to send it yet if we are blocked for output and our + * inputbuffer is full. + */ + if (rcvcnt < sizeof(rcvbuf)) { + n = (*des_read)(rem, rcvbuf + rcvcnt, + sizeof(rcvbuf) - rcvcnt); + if (n <= 0) + return; + rcvd += n; + } + else { + n = (*des_read)(rem, waste, sizeof(waste)); + if (n <= 0) + return; + } + continue; + default: + return; + } + } if (mark & TIOCPKT_WINDOW) { /* * Let server know about window size changes @@ -1276,6 +1443,7 @@ (void) ioctl(1, TIOCFLUSH, (char *)&out); #else (void) ioctl(1, TCFLSH, 1); + #endif #endif for (;;) { @@ -1284,15 +1452,31 @@ break; } if (atmark) - break; + break; n = read(rem, waste, sizeof (waste)); if (n <= 0) - break; -return; + break; } + /* + * Don't want any pending data to be output, so clear the recv + * buffer. If we were hanging on a write when interrupted, + * don't want it to restart. If we were reading, restart + * anyway. + */ + rcvcnt = 0; + siglongjmp(rcvtop, 1); } - + /* oob does not do FLUSHREAD */ + + /* + * If we filled the receive buffer while a read was pending, longjmp + * to the top to restart appropriately. Don't abort a pending write, + * however, or we won't know how much was written. + */ + if (rcvd && rcvstate == READING) { + siglongjmp(rcvtop, 1); + } } @@ -1307,14 +1491,11 @@ int oldmask; #endif { -#if (defined(BSD) && BSD+0 >= 43) || defined(ultrix) - int pid = getpid(); -#else - int pid = -getpid(); -#endif -fd_set readset, excset, writeset; + fd_set readset, excset, writeset; int n, remaining; - char *bufp = rcvbuf; + char *volatile bufp = rcvbuf; + int firsttime = 0; + pid_t pid = getpid(); #ifdef POSIX_SIGNALS struct sigaction sa; @@ -1323,13 +1504,22 @@ sa.sa_flags = 0; sa.sa_handler = SIG_IGN; (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + sa.sa_handler = oob; + (void) sigaction(SIGURG, &sa, (struct sigaction *)0); #else (void) signal(SIGTTOU, SIG_IGN); + (void) signal(SIGURG, oob); #endif ppid = getppid(); -FD_ZERO(&readset); +#ifdef SIOCSPGRP + ioctl(rem, SIOCSPGRP, &pid); /* @@@ */ +#else + fcntl(rem, F_SETOWN, pid); +#endif + sigsetjmp(rcvtop, 1); + FD_ZERO(&readset); FD_ZERO(&excset); FD_ZERO(&writeset); #ifdef POSIX_SIGNALS @@ -1341,42 +1531,27 @@ #endif /* POSIX_SIGNALS */ for (;;) { - if ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) - { - FD_SET(1,&writeset); + while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { rcvstate = WRITING; - FD_CLR(rem, &readset); - } - else { - - bufp = rcvbuf; - rcvcnt = 0; - rcvstate = READING; -FD_SET(rem,&readset); - FD_CLR(1,&writeset); - } - FD_SET(rem,&excset); - if (select(rem+1, &readset, &writeset, &excset, 0) > 0 ) { - if (FD_ISSET(rem, &excset)) - oob(); - if (FD_ISSET(1,&writeset)) { - n = write(1, bufp, remaining); + n = write(1, bufp, remaining); if (n < 0) { if (errno != EINTR) - return (-1); + return (-1); continue; } bufp += n; -} -if (FD_ISSET(rem, &readset)) { - rcvcnt = des_read(rem, rcvbuf, sizeof (rcvbuf)); + } + + bufp = rcvbuf; + rcvcnt = 0; + rcvstate = READING; + + rcvcnt = (*des_read)(rem, rcvbuf, sizeof (rcvbuf)); + if (rcvcnt == 0) - return (0); - if (rcvcnt < 0) - goto error; -} - } else - error: { + return(0); + + if (rcvcnt < 0) { if (errno == EINTR) continue; perror("read"); @@ -1566,29 +1741,60 @@ #ifdef KERBEROS -void try_normal(argv) + +static char *ucb_rlogin[] = { + "/usr/bin/rlogin", + "/usr/ucb/rlogin", + "/usr/bsd/rlogin", + "" +}; + +void try_normal(host_offset, argv) + int host_offset; char **argv; { - register char *host; - -#ifndef KRB5_ATHENA_COMPAT + char **rlogin; + struct stat statbuf; + ino_t my_inode = 0; + dev_t my_device = 0; + if (encrypt_flag) exit(1); -#endif - fprintf(stderr,"trying normal rlogin (%s)\n", - UCB_RLOGIN); - fflush(stderr); - - host = strrchr(argv[0], '/'); - if (host) - host++; - else - host = argv[0]; - if (!strcmp(host, "rlogin")) - argv++; - - execv(UCB_RLOGIN, argv); - perror("exec"); + + /* Try to prevent recursion in case kerberos rlogin is installed + * as the default rlogin. + */ + if (stat(argv[0],&statbuf) == 0) { + my_inode = statbuf.st_ino; + my_device = statbuf.st_dev; + } + + /* Ensure hostname is first argument to be compatible with + * vendor version. + */ + if (host_offset > 1) { + int i; + /* Push it back to the first argument. */ + for (i = host_offset; i > 1; i--) { + argv[i] = argv[i-1]; + } + argv[1] = host; + } + + /* Search standard paths to the vendor version. */ + for (rlogin = ucb_rlogin; **rlogin; rlogin++) { + if (access(*rlogin,X_OK) == 0) { + if (stat(*rlogin,&statbuf) == 0 && + (statbuf.st_ino != my_inode || + statbuf.st_dev != my_device)) { + fprintf(stderr,"trying normal rlogin (%s)\n",*rlogin); + fflush(stderr); + execv(*rlogin, argv); + perror("exec"); + exit(1); + } + } + } exit(1); } @@ -1600,7 +1806,7 @@ #ifndef OLD_VERSION -int des_read(fd, buf, len) +int v5_des_read(fd, buf, len) int fd; register char *buf; int len; @@ -1698,16 +1904,15 @@ -int des_write(fd, buf, len) +int v5_des_write(fd, buf, len) int fd; char *buf; int len; { - unsigned char *len_buf = (unsigned char *) des_outpkt; - - if (!encrypt_flag) - return(write(fd, buf, len)); + unsigned char *len_buf = (unsigned char *) des_outpkt; + if (!encrypt_flag) + return(write(fd, buf, len)); desoutbuf.length = krb5_encrypt_size(len,eblock.crypto_entry); if (desoutbuf.length > sizeof(des_outpkt)-4){ @@ -1869,7 +2074,7 @@ } (void) mit_des_cbc_encrypt((len < 8) ? garbage_buf : buf, - des_outbuf, + des_outpkt, (len < 8) ? 8 : len, eblock.priv, eblock.key->contents, @@ -1883,14 +2088,184 @@ len_buf[3] = (len & 0xff); (void) write(fd, len_buf, 4); #ifdef NOROUNDUP - (void) write(fd, des_outbuf, ((((len)+((8)-1))/(8))*(8))); + (void) write(fd, des_outpkt, ((((len)+((8)-1))/(8))*(8))); #else - (void) write(fd, des_outbuf, roundup(len,8)); + (void) write(fd, des_outpkt, roundup(len,8)); #endif return(len); } #endif /* OLD_VERSION */ +#ifdef KRB5_KRB4_COMPAT +int +v4_des_read(fd, buf, len) +int fd; +register char *buf; +int len; +{ + int nreturned = 0; + krb5_ui_4 net_len, rd_len; + int cc; +#if 0 + unsigned char len_buf[4]; +#endif + + 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 0 + if ((cc = krb_net_read(fd, (char *)len_buf, 4)) != 4) { + /* XXX can't read enough, pipe + must have closed */ + return(0); + } + net_len = (((krb5_ui_4)len_buf[0]<<24) | + ((krb5_ui_4)len_buf[1]<<16) | + ((krb5_ui_4)len_buf[2]<<8) | + (krb5_ui_4)len_buf[3]); +#else + { + unsigned char c; + int gotzero = 0; + + /* We're fetching the length which is MSB first, and the MSB + has to be zero unless the client is sending more than 2^24 + (16M) bytes in a single write (which is why this code is in + rlogin but not rcp or rsh.) The only reasons we'd get something + other than zero are: + -- corruption of the tcp stream (which will show up when + everything else is out of sync too) + -- un-caught Berkeley-style "pseudo out-of-band data" which + happens any time the user hits ^C twice. + The latter is *very* common, as shown by an 'rlogin -x -d' + using the CNS V4 rlogin. Mark EIchin 1/95 + */ + do { + cc = krb_net_read(fd, &c, 1); + if (cc <= 0) return 0; /* read error */ + if (cc == 1) { + if (c == 0) gotzero = 1; + } + } while (!gotzero); + + if ((cc = krb_net_read(fd, &c, 1)) != 1) return 0; + net_len = c; + if ((cc = krb_net_read(fd, &c, 1)) != 1) return 0; + net_len = (net_len << 8) | c; + if ((cc = krb_net_read(fd, &c, 1)) != 1) return 0; + net_len = (net_len << 8) | c; + } + +#endif + /* Note: net_len is unsigned */ + if (net_len > sizeof(des_inbuf)) { + /* XXX preposterous length, probably out of sync. + act as if pipe closed */ + return(0); + } + /* the writer tells us how much real data we are getting, but + we need to read the pad bytes (8-byte boundary) */ + rd_len = roundup(net_len, 8); + if ((cc = krb_net_read(fd, des_inbuf, rd_len)) != rd_len) { + /* XXX can't read enough, pipe + must have closed */ + return(0); + } + (void) pcbc_encrypt(des_inbuf, + storage, + (net_len < 8) ? 8 : net_len, + v4_schedule, + v4_cred.session, + DECRYPT); + /* + * when the cleartext block is < 8 bytes, it is "right-justified" + * in the block, so we need to adjust the pointer to the data + */ + if (net_len < 8) + store_ptr = storage + 8 - net_len; + else + store_ptr = storage; + nstored = net_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 +v4_des_write(fd, buf, len) +int fd; +char *buf; +int len; +{ + static char garbage_buf[8]; + unsigned char *len_buf = (unsigned char *) des_outpkt; + + if (!encrypt_flag) + return(write(fd, buf, len)); + + /* + * pcbc_encrypt outputs in 8-byte (64 bit) increments + * + * it zero-fills the cleartext to 8-byte padding, + * so if we have cleartext of < 8 bytes, we want + * to insert random garbage before it so that the ciphertext + * differs for each transmission of the same cleartext. + * if len < 8 - sizeof(long), sizeof(long) bytes of random + * garbage should be sufficient; leave the rest as-is in the buffer. + * if len > 8 - sizeof(long), just garbage fill the rest. + */ + +#ifdef min +#undef min +#endif +#define min(a,b) ((a < b) ? a : b) + + if (len < 8) { + krb5_random_confounder(8 - len, garbage_buf); + /* this "right-justifies" the data in the buffer */ + (void) memcpy(garbage_buf + 8 - len, buf, len); + } + (void) pcbc_encrypt((len < 8) ? garbage_buf : buf, + des_outpkt+4, + (len < 8) ? 8 : len, + v4_schedule, + v4_cred.session, + ENCRYPT); + + /* tell the other end the real amount, but send an 8-byte padded + packet */ + 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, des_outpkt, roundup(len,8)+4); + return(len); +} + +#endif /* KRB5_KRB4_COMPAT */ + #endif /* KERBEROS */ @@ -1911,4 +2286,11 @@ prf("\007Connection closed."); done(1); +} + +krb5_sigtype copytochild(signo) + int signo; + +{ + kill(child, SIGURG); } --- appl/bsd/rlogin.M.orig Fri Feb 6 19:41:18 1998 +++ appl/bsd/rlogin.M Thu May 21 15:17:42 1998 @@ -25,8 +25,16 @@ .I rhost [\fB\-e\fP\fI\|c\fP] [\fB\-8\fP] [\fB\-c\fP] [ \fB\-a\fP] [\fB\-f\fP] [\fB\-F\fP] [\fB\-t\fP \fItermtype\fP] [\fB\-n\fP] [\fB\-7\fP] -[\fB\-d\fP] [\fB\-k\fP \fIrealm\fP] [\fB\-x\fP] [\fB\-L\fP] [\fB\-l\fP -\fIusername\fP] +[\fB\-d\fP] [\fB\-k\fP \fIrealm\fP] [\fB\-k4\fP \fIkrb4realm\fP] +[\fB\-4\fP] [\fB\-x\fP] [\fB\-L\fP] [\fB\-l\fP \fIusername\fP] +.br +.B rlogin +[\fBoptions\fP] +.I rhost +.br +.B rlogin +.I username@rhost +[\fBoptions\fP] .PP .SH DESCRIPTION .I Rlogin @@ -49,7 +57,10 @@ /.k5login file, the principal will be granted access to the account according to the aname\->lname mapping rules. (See .IR krb5_anadd(8) -for more details.) Otherwise a login and password will be prompted for +for more details.) If rlogin and klogind were compiled with kerberos +IV support, the \&.klogin authorization list may be used, instead. +.PP +Otherwise a login and password will be prompted for on the remote machine as in .IR login (1). To avoid some security problems, the \&.k5login file must be owned by @@ -102,11 +113,13 @@ .TP \fB\-f\fP forward a copy of the local credentials to the remote system. +Forwardable tickets are not supported for kerberos IV connections. .TP \fB\-F\fP forward a .I forwardable -copy of the local credentials to the remote system. +copy of the local credentials to the remote system. Forwardable +tickets are not supported for kerberos IV connections. .TP \fB\-t\fP \fItermtype\fP replace the terminal type passed to the remote host with @@ -128,6 +141,19 @@ .I realm instead of the remote host's realm as determined by .IR krb_realmofhost (3). +.TP +\fB\-k4\fP \fIkrb4realm\fP +causes +.I rlogin +to obtain kerberos IV tickets for the remote host in +.I krb4realm +instead of the remote host's realm as determined by +.IR krb_realmofhost (3). +.TP +.B \-4 +Default to kerberos IV. This option is only available if rlogin was +compiled with kerberos IV compatibility. The rlogin default is to +attempt a Version 5 connection and fallback to kerberos IV on failure. .TP \fB\-x\fP turn on DES encryption for all data passed via the rlogin session. This >How-To-Repeat: >Fix: >Audit-Trail: Responsible-Changed-From-To: gnats-admin->krb5-unassigned Responsible-Changed-By: raeburn Responsible-Changed-When: Fri Sep 14 11:49:03 2001 Responsible-Changed-Why: reformat/refile >Unformatted: