[2930] in Kerberos-V5-bugs
pending/598: PATCH: krb5-1.0.5 appl/bsd/krlogin.c patches
daemon@ATHENA.MIT.EDU (Larry Schwimmer)
Thu May 21 18:38:53 1998
Resent-From: gnats@rt-11.MIT.EDU (GNATS Management)
Resent-To: gnats-admin@rt-11.MIT.EDU
Resent-Reply-To: krb5-bugs@MIT.EDU,
Larry Schwimmer <schwim@whatmore.Stanford.EDU>
Date: Thu, 21 May 1998 15:30:32 -0700 (PDT)
From: Larry Schwimmer <schwim@whatmore.Stanford.EDU>
To: krb5-bugs@MIT.EDU
>Number: 598
>Category: pending
>Synopsis: PATCH: krb5-1.0.5 appl/bsd/krlogin.c patches
>Confidential: yes
>Severity: serious
>Priority: medium
>Responsible: gnats-admin
>State: open
>Class: sw-bug
>Submitter-Id: unknown
>Arrival-Date: Thu May 21 18:31:00 EDT 1998
>Last-Modified:
>Originator:
>Organization:
>Release:
>Environment:
>Description:
>How-To-Repeat:
>Fix:
>Audit-Trail:
>Unformatted:
Submitter-Id: net
Originator: Larry Schwimmer
Organization: Stanford University
Confidential: no
Synopsis: multipe rlogin patches
Severity: serious
Priority: high
Category: krb5-appl
Class: sw-bug, change-request
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 <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
@@ -148,6 +149,17 @@
* data, the output buffer represents the entire packet.
*/
+#ifdef KRB5_KRB4_COMPAT
+#include <kerberosIV/krb.h>
+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