COMMAND
kerberos rsh, rcp and rlogin
SYSTEMS AFFECTED
Systems running kerberos kth-krb4
PROBLEM
There has been discovered a security-hole in kerberized rsh, rcp
and rlogin. Every user on a kerberized system has a ticket-file.
Only the owner should be able to read this file. The name of the
ticketfile is stored in the environment-variable KRBTKFILE.
The versions of rsh, rcp and rlogin in the kth-krb4 package are
setuid to work with bsd-style rshd and rlogind. When they attempt
to read the ticketfile, there is no check if the user starting
the program has read access of the file. Thus, a user can use
any other user on the system's ticketfile by simply changing an
environment variable. To activate the bug, it is required that
there are valid tickets for the target user laying around
somewhere on your system (usually in /tmp/).
The hole allows any user on the system to gain privilegies of any
other user including root. Note also that it appears the bsdi
version of su uses kerbose tickets if kerbose is configured, but
the BSDI kerberosIV implementation does not appear to have the
problem (the tf_init() routine which opens the ticket file checks
to see that the real uid of the process is either root or owns
the ticket file).
The hole has been successfully tested on kth-kerberos, but is
suspected to exist on any other versions of kerberos. The bug
was discovered by Mattias Amnefelt.
SOLUTION
Disable the suid-bits on rcp, rsh and rlogin. This will disable
the program's capabilities to fallback to the non-kerberised
protocols if the a user fails to authenticate himself.
Permanent fix should be changing the uid of the program to the
user's uid as early as possible (patches from the development
team are included, plus two other security patches for
kth-kerberos). The enclosed patch to 0.9.6 fixes three security
problems:
1. the tgetent buffer overflow. This is fixed by simply not
calling tgetent.
2. vulnerability of setuid rsh, rlogin, and rcp.
3. missing IP-nummer check in telnetd.
This fix will of course be included in an upcoming version RSN.
Index: appl/bsd/rcp.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rcp.c,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -w -r1.43 -r1.44
--- rcp.c 1997/05/13 09:41:26 1.43
+++ rcp.c 1997/11/03 11:18:02 1.44
@@ -49,6 +49,9 @@
static uid_t userid;
static int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+static int argc_copy;
+static char **argv_copy;
+
#define CMDNEEDS 64
static char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
@@ -403,8 +406,9 @@
kerberos(char **host, char *bp, char *locuser, char *user)
{
int sock = -1, err;
-again:
+
if (use_kerberos) {
+ setuid(getuid());
rem = KSUCCESS;
errno = 0;
if (dest_realm == NULL)
@@ -439,13 +443,11 @@
rem = sock;
#endif
if (rem < 0) {
- use_kerberos = 0;
- port = get_shell_port(use_kerberos, 0);
if (errno == ECONNREFUSED)
oldw("remote host doesn't support Kerberos");
else if (errno == ENOENT)
oldw("can't provide Kerberos authentication data");
- goto again;
+ execv(_PATH_RCP, argv_copy);
}
} else {
if (doencrypt)
@@ -906,8 +908,28 @@
{
int ch, fflag, tflag;
char *targ;
+ int i;
set_progname(argv[0]);
+
+ /*
+ * Prepare for execing ourselves.
+ */
+
+ argc_copy = argc + 1;
+ argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy));
+ if (argv_copy == NULL)
+ err(1, "malloc");
+ argv_copy[0] = argv[0];
+ argv_copy[1] = "-K";
+ for(i = 1; i < argc; ++i) {
+ argv_copy[i + 1] = strdup(argv[i]);
+ if (argv_copy[i + 1] == NULL)
+ errx(1, "strdup: out of memory");
+ }
+ argv_copy[argc + 1] = NULL;
+
+
fflag = tflag = 0;
while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
switch(ch) { /* User-visible flags. */
@@ -951,8 +973,10 @@
* kshell service, pass 0 for no encryption */
port = get_shell_port(use_kerberos, 0);
+ userid = getuid();
+
#ifndef __CYGWIN32__
- if ((pwd = k_getpwuid(userid = getuid())) == NULL)
+ if ((pwd = k_getpwuid(userid)) == NULL)
errx(1, "unknown user %d", (int)userid);
#endif
Index: appl/bsd/rlogin.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rlogin.c,v
retrieving revision 1.61
retrieving revision 1.62
diff -u -w -r1.61 -r1.62
--- rlogin.c 1997/05/25 01:14:47 1.61
+++ rlogin.c 1997/11/03 11:18:09 1.62
@@ -594,14 +594,12 @@
usage();
}
optind += argoff;
- argc -= optind;
- argv += optind;
/* if haven't gotten a host yet, do so */
- if (!host && !(host = *argv++))
+ if (!host && !(host = argv[optind++]))
usage();
- if (*argv)
+ if (argv[optind])
usage();
if (!(pw = k_getpwuid(uid = getuid())))
@@ -609,7 +607,6 @@
if (!user)
user = pw->pw_name;
-
if (user_port)
sv_port = user_port;
else
@@ -636,17 +633,8 @@
get_window_size(0, &winsize);
- 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))) {
- errno = ENOMEM;
- err(1, NULL);
- }
-
+ setuid(getuid());
rem = KSUCCESS;
errno = 0;
if (dest_realm == NULL)
@@ -659,15 +647,22 @@
rem = krcmd(&host, sv_port, user, term, 0,
dest_realm);
if (rem < 0) {
- use_kerberos = 0;
- if (user_port == 0)
- sv_port = get_login_port(use_kerberos,
- doencrypt);
+ int i;
+ char **newargv;
+
if (errno == ECONNREFUSED)
warning("remote host doesn't support Kerberos");
if (errno == ENOENT)
warning("can't provide Kerberos auth data");
- goto try_connect;
+ newargv = malloc((argc + 2) * sizeof(*newargv));
+ if (newargv == NULL)
+ err(1, "malloc");
+ newargv[0] = argv[0];
+ newargv[1] = "-K";
+ for(i = 1; i < argc; ++i)
+ newargv[i + 1] = argv[i];
+ newargv[argc + 1] = NULL;
+ execv(_PATH_RLOGIN, newargv);
}
} else {
if (doencrypt)
Index: appl/bsd/rsh.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rsh.c,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -w -r1.36 -r1.37
--- rsh.c 1997/06/26 13:48:35 1.36
+++ rsh.c 1997/11/03 11:18:14 1.37
@@ -247,9 +247,6 @@
err(1, "can't exec %s", _PATH_RLOGIN);
}
- argc -= optind;
- argv += optind;
-
#ifndef __CYGWIN32__
if (!(pw = k_getpwuid(uid = getuid())))
errx(1, "unknown user id.");
@@ -266,12 +263,12 @@
if (doencrypt)
nfork = 0;
- args = copyargs(argv);
+ args = copyargs(argv+optind);
sv_port=get_shell_port(use_kerberos, doencrypt);
-try_connect:
if (use_kerberos) {
+ setuid(getuid());
rem = KSUCCESS;
errno = 0;
if (dest_realm == NULL)
@@ -284,13 +281,22 @@
rem = krcmd(&host, sv_port, user, args, &rfd2,
dest_realm);
if (rem < 0) {
+ int i;
+ char **newargv;
+
if (errno == ECONNREFUSED)
warning("remote host doesn't support Kerberos");
if (errno == ENOENT)
warning("can't provide Kerberos auth data");
- use_kerberos = 0;
- sv_port=get_shell_port(use_kerberos, doencrypt);
- goto try_connect;
+ newargv = malloc((argc + 2) * sizeof(*newargv));
+ if (newargv == NULL)
+ err(1, "malloc");
+ newargv[0] = argv[0];
+ newargv[1] = "-K";
+ for(i = 1; i < argc; ++i)
+ newargv[i + 1] = argv[i];
+ newargv[argc + 1] = NULL;
+ execv(_PATH_RSH, newargv);
}
} else {
if (doencrypt)
Index: appl/bsd/pathnames.h
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/pathnames.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -w -r1.23 -r1.24
--- pathnames.h 1996/11/17 06:36:42 1.23
+++ pathnames.h 1997/11/03 11:17:19 1.24
@@ -65,6 +65,9 @@
#undef _PATH_RSH /* Redifine rsh */
#define _PATH_RSH BINDIR "/rsh"
+#undef _PATH_RCP /* Redifine rcp */
+#define _PATH_RCP BINDIR "/rcp"
+
#undef _PATH_LOGIN
#define _PATH_LOGIN BINDIR "/login"
@@ -186,6 +189,8 @@
#define _PATH_RLOGIN "/usr/athena/bin/rlogin"
#undef _PATH_RSH
#define _PATH_RSH "/usr/athena/bin/rsh"
+#undef _PATH_RCP
+#define _PATH_RCP "/usr/athena/bin/rcp"
#undef _PATH_LOGIN
#define _PATH_LOGIN "/usr/athena/bin/login"
#endif
Index: appl/telnet/libtelnet/kerberos.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/appl/telnet/libtelnet/kerberos.c,v
retrieving revision 1.34
retrieving revision 1.36
diff -u -w -r1.34 -r1.36
--- kerberos.c 1997/10/21 21:15:24 1.34
+++ kerberos.c 1997/11/03 06:12:14 1.36
@@ -265,9 +267,11 @@
void
kerberos4_is(Authenticator *ap, unsigned char *data, int cnt)
{
+ struct sockaddr_in addr;
char realm[REALM_SZ];
char instance[INST_SZ];
int r;
+ int addr_len;
if (cnt-- < 1)
return;
@@ -288,8 +292,17 @@
printf("\r\n");
}
k_getsockinst(0, instance, sizeof(instance));
- if (r = krb_rd_req(&auth, KRB_SERVICE_NAME,
- instance, 0, &adat, "")) {
+ addr_len = sizeof(addr);
+ if(getpeername(0, (struct sockaddr *)&addr, &addr_len) < 0) {
+ if(auth_debug_mode)
+ printf("getpeername failed\r\n");
+ Data(ap, KRB_REJECT, "getpeername failed", -1);
+ auth_finished(ap, AUTH_REJECT);
+ return;
+ }
+ r = krb_rd_req(&auth, KRB_SERVICE_NAME,
+ instance, addr.sin_addr.s_addr, &adat, "");
+ if (r) {
if (auth_debug_mode)
printf("Kerberos failed him as %s\r\n", name);
Data(ap, KRB_REJECT, (void *)krb_get_err_text(r), -1);
Index: appl/telnet/telnetd/telnetd.c
===================================================================
RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/appl/telnet/telnetd/telnetd.c,v
retrieving revision 1.47
retrieving revision 1.48
diff -u -w -r1.47 -r1.48
--- telnetd.c 1997/10/29 01:26:58 1.47
+++ telnetd.c 1997/11/03 06:08:26 1.48
@@ -647,21 +647,7 @@
int
terminaltypeok(char *s)
{
- char buf[1024];
-
- if (terminaltype == NULL)
- return(1);
-
- /*
- * tgetent() will return 1 if the type is known, and
- * 0 if it is not known. If it returns -1, it couldn't
- * open the database. But if we can't open the database,
- * it won't help to say we failed, because we won't be
- * able to verify anything else. So, we treat -1 like 1.
- */
- if (tgetent(buf, s) == 0)
- return(0);
- return(1);
+ return 1;
}