[683] in Kerberos-V5-bugs
security hole in replay cache code
daemon@ATHENA.MIT.EDU (Jonathan I. Kamens)
Tue Aug 30 16:02:31 1994
Date: Tue, 30 Aug 1994 16:03:43 -0400
From: "Jonathan I. Kamens" <jik@cam.ov.com>
To: krb5-bugs@MIT.EDU
Because the replay cache code doesn't do locking (at least, it doesn't
as far as I can tell) on the replay cache files that it writes, it's
possible for two processes sharing a replay cache to cause entries in
it to be lost. For example, if two people krlogin to a machine in
quick succession and the two krlogind's put their sendauth messages
into the rlogin replay cache at the same time, it's possible that one
of the sendauth blocks will be overwritten by the other.
This is a security hole which makes it possible (with a little
fiddling; I'd be glad to describe in E-mail to anyone who's interested
(with a need to know :-) how I think the hole can be exploited) to
reuse a stolen ticket for a service that uses a replay cache.
The fix is to put file locking calls into the replay cache code, so
that two processes don't write to the same replay cache at the same
time.
The patches below do this. They're against beta 2, and they may not
apply completely cleanly both because of changes between beta 2 and
beta 4 and because of other fixes we've made locally to the replay
cache code.
Jonathan Kamens | OpenVision Technologies, Inc. | jik@cam.ov.com
*** rc_dfl.c 1994/03/07 20:05:27 1.5
--- rc_dfl.c 1994/08/30 19:27:45
***************
*** 248,254 ****
int i;
/* allocate id? no */
! if (!(t = (struct dfl_data *) malloc(sizeof(struct dfl_data))))
return KRB5_RC_MALLOC;
id->data = (krb5_pointer) t;
if(name != NULL) {
--- 248,254 ----
int i;
/* allocate id? no */
! if (!(t = (struct dfl_data *) calloc(1, sizeof(struct dfl_data))))
return KRB5_RC_MALLOC;
id->data = (krb5_pointer) t;
if(name != NULL) {
*** rc_io.h 1993/09/20 20:21:27 1.1
--- rc_io.h 1994/08/30 19:26:16
***************
*** 15,24 ****
--- 15,26 ----
#ifndef KRB5_RC_IO_H
#define KRB5_RC_IO_H
#include <krb5/osconf.h>
+ #include <stdio.h> /* for P_tmpdir, FILE * */
typedef struct krb5_rc_iostuff
{
int fd;
+ FILE *file; /* Used for file locking, not for I/O */
int mark; /* on newer systems, should be pos_t */
char *fn;
}
*** rc_io.c 1994/03/29 21:45:30 1.3
--- rc_io.c 1994/08/30 19:25:48
***************
*** 18,26 ****
#define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */
- #include <stdio.h> /* for P_tmpdir */
-
#include "rc_base.h"
#include "rc_dfl.h"
#include "rc_io.h"
--- 18,25 ----
#define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */
+ #define KRB5_RC_LOCK_TRIES 30 /* # of times to try locking */
#include "rc_base.h"
#include "rc_dfl.h"
#include "rc_io.h"
***************
*** 27,32 ****
--- 26,32 ----
#include <krb5/sysincl.h>
#include <krb5/osconf.h>
#include <krb5/ext-proto.h>
+ #include <krb5/libos.h> /* for KRB5_LOCKMODE_EXCLUSIVE */
#include <unistd.h>
#ifdef KRB5_USE_INET
***************
*** 71,76 ****
--- 71,77 ----
char *c;
krb5_int16 rc_vno = htons(KRB5_RC_VNO);
krb5_error_code retval;
+ int lock_count = 0;
GETDIR;
if (fn && *fn)
***************
*** 134,149 ****
retval = KRB5_RC_IO_UNKNOWN; goto fail;
}
}
! if ((retval = krb5_rc_io_write(d, (krb5_pointer)&rc_vno, sizeof(rc_vno))) ||
! (retval = krb5_rc_io_sync(d)))
! {
! fail:
! (void) unlink(d->fn);
! FREE(d->fn);
! d->fn = NULL;
! (void) close(d->fd);
! return retval;
}
return 0;
}
--- 135,165 ----
retval = KRB5_RC_IO_UNKNOWN; goto fail;
}
}
! if (! (d->file = fdopen(d->fd, "w")))
! goto fail;
!
! while (((retval = krb5_lock_file(d->file, d->fn, KRB5_LOCKMODE_EXCLUSIVE)) ==
! EAGAIN) && (++lock_count < KRB5_RC_LOCK_TRIES))
! sleep(1);
! if (retval) {
! retval = KRB5_RC_IO_UNKNOWN;
! goto fail;
}
+
+ if ((retval = krb5_rc_io_write(d, (krb5_pointer)&rc_vno, sizeof(rc_vno))) ||
+ (retval = krb5_rc_io_sync(d)))
+ {
+ fail:
+ (void) unlink(d->fn);
+ FREE(d->fn);
+ d->fn = NULL;
+ if (d->file)
+ (void) fclose(d->file);
+ /* This is probably unnecessary because the fclose will close the
+ file descriptor too. */
+ (void) close(d->fd);
+ return retval;
+ }
return 0;
}
***************
*** 154,159 ****
--- 170,176 ----
krb5_int16 rc_vno;
krb5_error_code retval;
struct stat statb;
+ int lock_count = 0;
GETDIR;
if (!(d->fn = malloc(strlen(fn) + dirlen + 1)))
***************
*** 199,219 ****
goto fail;
}
}
! if (retval = krb5_rc_io_read(d, (krb5_pointer) &rc_vno, sizeof(rc_vno)))
! goto unlk;
!
! if (ntohs(rc_vno) != KRB5_RC_VNO)
! {
! retval = KRB5_RCACHE_BADVNO;
! unlk:
! unlink(d->fn);
! fail:
! (void) close(d->fd);
! FREE(d->fn);
! d->fn = NULL;
! return retval;
}
return 0;
}
--- 216,248 ----
goto fail;
}
}
! if (! (d->file = fdopen(d->fd, "r+")))
! goto unlk;
! while (((retval = krb5_lock_file(d->file, d->fn, KRB5_LOCKMODE_EXCLUSIVE)) ==
! EAGAIN) && (++lock_count < KRB5_RC_LOCK_TRIES))
! sleep(1);
! if (retval) {
! retval = KRB5_RC_IO_UNKNOWN;
! goto unlk;
}
+
+ if (retval = krb5_rc_io_read(d, (krb5_pointer) &rc_vno, sizeof(rc_vno)))
+ goto unlk;
+
+ if (ntohs(rc_vno) != KRB5_RC_VNO)
+ {
+ retval = KRB5_RCACHE_BADVNO;
+ unlk:
+ unlink(d->fn);
+ fail:
+ if (d->file)
+ (void) fclose(d->file);
+ (void) close(d->fd);
+ FREE(d->fn);
+ d->fn = NULL;
+ return retval;
+ }
return 0;
}
***************
*** 226,231 ****
--- 255,261 ----
(void) krb5_rc_io_close(new);
new->fn = old->fn;
new->fd = old->fd;
+ new->file = old->file;
return 0;
}
***************
*** 284,294 ****
krb5_error_code krb5_rc_io_close (d)
krb5_rc_iostuff *d;
{
FREE(d->fn);
d->fn = NULL;
! if (close(d->fd) == -1) /* can't happen */
! return KRB5_RC_IO_UNKNOWN;
! return 0;
}
krb5_error_code krb5_rc_io_destroy (d)
--- 314,328 ----
krb5_error_code krb5_rc_io_close (d)
krb5_rc_iostuff *d;
{
+ krb5_error_code retval;
+
+ retval = krb5_unlock_file(d->file, d->fn);
FREE(d->fn);
d->fn = NULL;
! if (fclose(d->file) && (! retval))
! retval = KRB5_RC_IO_UNKNOWN;
! (void) close(d->fd);
! return retval;
}
krb5_error_code krb5_rc_io_destroy (d)