summaryrefslogtreecommitdiff
path: root/src/lib/krb5/rcache
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
committerCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
commit33a9b234e7087f573ef08cd7318c6497ba08b439 (patch)
treed0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/krb5/rcache
Diffstat (limited to 'src/lib/krb5/rcache')
-rw-r--r--src/lib/krb5/rcache/Makefile.in50
-rw-r--r--src/lib/krb5/rcache/README82
-rw-r--r--src/lib/krb5/rcache/RELEASE17
-rw-r--r--src/lib/krb5/rcache/deps98
-rw-r--r--src/lib/krb5/rcache/rc-int.h91
-rw-r--r--src/lib/krb5/rcache/rc_base.c181
-rw-r--r--src/lib/krb5/rcache/rc_base.h15
-rw-r--r--src/lib/krb5/rcache/rc_conv.c76
-rw-r--r--src/lib/krb5/rcache/rc_dfl.c851
-rw-r--r--src/lib/krb5/rcache/rc_dfl.h48
-rw-r--r--src/lib/krb5/rcache/rc_io.c522
-rw-r--r--src/lib/krb5/rcache/rc_io.h60
-rw-r--r--src/lib/krb5/rcache/rc_none.c96
-rw-r--r--src/lib/krb5/rcache/rcdef.c45
-rw-r--r--src/lib/krb5/rcache/rcfns.c95
-rw-r--r--src/lib/krb5/rcache/ser_rc.c212
-rw-r--r--src/lib/krb5/rcache/t_replay.c265
17 files changed, 2804 insertions, 0 deletions
diff --git a/src/lib/krb5/rcache/Makefile.in b/src/lib/krb5/rcache/Makefile.in
new file mode 100644
index 000000000000..5b800c572fc3
--- /dev/null
+++ b/src/lib/krb5/rcache/Makefile.in
@@ -0,0 +1,50 @@
+mydir=lib$(S)krb5$(S)rcache
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+##DOS##BUILDTOP = ..\..\..
+##DOS##PREFIXDIR=rcache
+##DOS##OBJFILE=..\$(OUTPRE)$(PREFIXDIR).lst
+
+STLIBOBJS = \
+ rc_base.o \
+ rc_dfl.o \
+ rc_io.o \
+ rcdef.o \
+ rc_none.o \
+ rc_conv.o \
+ ser_rc.o \
+ rcfns.o
+
+OBJS= \
+ $(OUTPRE)rc_base.$(OBJEXT) \
+ $(OUTPRE)rc_dfl.$(OBJEXT) \
+ $(OUTPRE)rc_io.$(OBJEXT) \
+ $(OUTPRE)rcdef.$(OBJEXT) \
+ $(OUTPRE)rc_none.$(OBJEXT) \
+ $(OUTPRE)rc_conv.$(OBJEXT) \
+ $(OUTPRE)ser_rc.$(OBJEXT) \
+ $(OUTPRE)rcfns.$(OBJEXT)
+
+SRCS= \
+ $(srcdir)/rc_base.c \
+ $(srcdir)/rc_dfl.c \
+ $(srcdir)/rc_io.c \
+ $(srcdir)/rcdef.c \
+ $(srcdir)/rc_none.c \
+ $(srcdir)/rc_conv.c \
+ $(srcdir)/ser_rc.c \
+ $(srcdir)/rcfns.c \
+ $(srcdir)/t_replay.c
+
+##DOS##LIBOBJS = $(OBJS)
+
+all-unix: all-libobjs
+clean-unix:: clean-libobjs
+
+T_REPLAY_OBJS= t_replay.o
+
+t_replay: $(T_REPLAY_OBJS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o t_replay $(T_REPLAY_OBJS) $(KRB5_BASE_LIBS)
+
+@libobj_frag@
+
diff --git a/src/lib/krb5/rcache/README b/src/lib/krb5/rcache/README
new file mode 100644
index 000000000000..13a45a1d869f
--- /dev/null
+++ b/src/lib/krb5/rcache/README
@@ -0,0 +1,82 @@
+/*
+Copyright 1990, Daniel J. Bernstein. All rights reserved.
+
+Please address any questions or comments to the author at brnstnd@acf10.nyu.edu.
+*/
+
+The #include's should be rewritten.
+
+All functions return 0 on success.
+
+Environment variables: KRB5RCACHETYPE, KRB5RCACHENAME, KRB5RCACHEDIR,
+and TMPDIR. Obsolete: KRB5RCACHE.
+
+All header files are both ANSI-compatible and K&R-compatible. The .c files
+are only ANSI compatible. Everything passes gcc -Wall -ansi -pedantic.
+
+Strings are freed using FREE(), which is defined in terms of free().
+
+The error header files should be redone.
+
+The header files don't use __ because that's reserved.
+
+Each .c file assumes <malloc.h>. rc_io.c assumes fsync() and a gaggle of
+error codes. These assumptions are not as portable as the code itself.
+
+
+rcache.c:
+
+The rcache.c compatibility interface's type registration is a no-op; it
+simply passes the type name on to rc_base.h. rcache.h is obsolete; use
+rc_base.h if possible.
+
+There are some slight differences between rcache.c and the prototypes I
+saw in krb/func-proto.h. Don't look at me, it's your interface.
+
+rcache.c's get_name doesn't fill with zeros unless strncpy does.
+
+
+rc_base.c:
+
+It doesn't take linker magic to preregister types. Just change the
+typehead initialization in rc_base.c, with an appropriate include file
+setting the ops.
+
+
+rc_dfl.c:
+
+If NOIOSTUFF is defined when rc_dfl.c is compiled, all dfl rcaches will
+be per-process. This is untested.
+
+Provided that separate threads use separate rcaches, rc_dfl.c is safe
+for multithreading.
+
+Getting the name of a cache is only valid after it is created and before
+it is closed. Recovering a cache is only valid after it has been created.
+
+krb5_unparse_name had better produce a zero-terminated string.
+
+rc_dfl.c isn't smart enough to try expunge/retry upon a malloc error.
+Then again, such an error indicates that the whole system's about to die;
+without real memory management there's no good solution.
+
+HASHSIZE can be defined at compile time. It defaults to 997 in rc_dfl.c.
+EXCESSREPS can be defined at compile time. It defaults to 30 in rc_dfl.c.
+
+Hopefully adding a deltat to a time to compare to another time cannot
+overflow.
+
+In rc_dfl's struct dfl_data, the name field is never freed, even though
+it may be malloced by io_creat on a generate-name call. This should not
+be a problem: a single process should not be opening and closing many
+rcaches. One fix would be another field to indicate whether the string
+was malloced or not; normally this is an unstated characteristic of a
+char pointer, but here it would have to be explicit.
+
+
+rc_io.c:
+
+rc_io.c assumes that siginterrupt() is not set. If siginterrupt() is set
+and a signal occurs during, say, close(), then the close will fail.
+
+On a machine without fsync() you might as well not use the disk at all.
diff --git a/src/lib/krb5/rcache/RELEASE b/src/lib/krb5/rcache/RELEASE
new file mode 100644
index 000000000000..21a4624726a6
--- /dev/null
+++ b/src/lib/krb5/rcache/RELEASE
@@ -0,0 +1,17 @@
+Text of Mr. Bernstein's release:
+
+"I" henceforth refers to Daniel J. Bernstein.
+
+"rcshar" henceforth refers to the attached document, as sent from Daniel
+J. Bernstein to Project Athena on 11 March 1990
+
+I am the author of and sole copyright holder upon rcshar.
+
+I hereby waive copyright upon rcshar. rcshar is hereby public domain.
+
+I hereby also waive copyright upon any works that are (1) derived from
+rcshar and (2) prepared between 11 March 1990 and 1 January 1991.
+
+Daniel J. Bernstein
+
+<signature>, dated 7 July 1990
diff --git a/src/lib/krb5/rcache/deps b/src/lib/krb5/rcache/deps
new file mode 100644
index 000000000000..0fe8808722c0
--- /dev/null
+++ b/src/lib/krb5/rcache/deps
@@ -0,0 +1,98 @@
+#
+# Generated makefile dependencies follow.
+#
+rc_base.so rc_base.po $(OUTPRE)rc_base.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc-int.h rc_base.c \
+ rc_base.h
+rc_dfl.so rc_dfl.po $(OUTPRE)rc_dfl.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc-int.h rc_base.h \
+ rc_dfl.c rc_dfl.h rc_io.h
+rc_io.so rc_io.po $(OUTPRE)rc_io.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc_base.h rc_dfl.h \
+ rc_io.c rc_io.h
+rcdef.so rcdef.po $(OUTPRE)rcdef.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc-int.h rc_dfl.h \
+ rcdef.c
+rc_none.so rc_none.po $(OUTPRE)rc_none.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc-int.h rc_none.c
+rc_conv.so rc_conv.po $(OUTPRE)rc_conv.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc_base.h rc_conv.c
+ser_rc.so ser_rc.po $(OUTPRE)ser_rc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc-int.h ser_rc.c
+rcfns.so rcfns.po $(OUTPRE)rcfns.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h rc-int.h rcfns.c
+t_replay.so t_replay.po $(OUTPRE)t_replay.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ t_replay.c
diff --git a/src/lib/krb5/rcache/rc-int.h b/src/lib/krb5/rcache/rc-int.h
new file mode 100644
index 000000000000..72a9483c02fe
--- /dev/null
+++ b/src/lib/krb5/rcache/rc-int.h
@@ -0,0 +1,91 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc-int.h */
+/*
+ * Copyright 2004 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/* This file contains constant and function declarations used in the
+ * file-based replay cache routines. */
+
+#ifndef __KRB5_RCACHE_INT_H__
+#define __KRB5_RCACHE_INT_H__
+
+int krb5int_rc_finish_init(void);
+
+void krb5int_rc_terminate(void);
+
+struct krb5_rc_st {
+ krb5_magic magic;
+ const struct _krb5_rc_ops *ops;
+ krb5_pointer data;
+ k5_mutex_t lock;
+};
+
+struct _krb5_rc_ops {
+ krb5_magic magic;
+ char *type;
+ krb5_error_code (KRB5_CALLCONV *init)(
+ krb5_context,
+ krb5_rcache,
+ krb5_deltat); /* create */
+ krb5_error_code (KRB5_CALLCONV *recover)(
+ krb5_context,
+ krb5_rcache); /* open */
+ krb5_error_code (KRB5_CALLCONV *recover_or_init)(
+ krb5_context,
+ krb5_rcache,
+ krb5_deltat);
+ krb5_error_code (KRB5_CALLCONV *destroy)(
+ krb5_context,
+ krb5_rcache);
+ krb5_error_code (KRB5_CALLCONV *close)(
+ krb5_context,
+ krb5_rcache);
+ krb5_error_code (KRB5_CALLCONV *store)(
+ krb5_context,
+ krb5_rcache,
+ krb5_donot_replay *);
+ krb5_error_code (KRB5_CALLCONV *expunge)(
+ krb5_context,
+ krb5_rcache);
+ krb5_error_code (KRB5_CALLCONV *get_span)(
+ krb5_context,
+ krb5_rcache,
+ krb5_deltat *);
+ char *(KRB5_CALLCONV *get_name)(
+ krb5_context,
+ krb5_rcache);
+ krb5_error_code (KRB5_CALLCONV *resolve)(
+ krb5_context,
+ krb5_rcache,
+ char *);
+};
+
+typedef struct _krb5_rc_ops krb5_rc_ops;
+
+krb5_error_code krb5_rc_register_type(krb5_context, const krb5_rc_ops *);
+
+extern const krb5_rc_ops krb5_rc_dfl_ops;
+extern const krb5_rc_ops krb5_rc_none_ops;
+
+#endif /* __KRB5_RCACHE_INT_H__ */
diff --git a/src/lib/krb5/rcache/rc_base.c b/src/lib/krb5/rcache/rc_base.c
new file mode 100644
index 000000000000..373ac3046305
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_base.c
@@ -0,0 +1,181 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_base.c */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+/*
+ * Base "glue" functions for the replay cache.
+ */
+
+#include "rc_base.h"
+#include "rc-int.h"
+#include "k5-thread.h"
+
+struct krb5_rc_typelist {
+ const krb5_rc_ops *ops;
+ struct krb5_rc_typelist *next;
+};
+static struct krb5_rc_typelist none = { &krb5_rc_none_ops, 0 };
+static struct krb5_rc_typelist krb5_rc_typelist_dfl = { &krb5_rc_dfl_ops, &none };
+static struct krb5_rc_typelist *typehead = &krb5_rc_typelist_dfl;
+static k5_mutex_t rc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER;
+
+int
+krb5int_rc_finish_init(void)
+{
+ return k5_mutex_finish_init(&rc_typelist_lock);
+}
+
+void
+krb5int_rc_terminate(void)
+{
+ struct krb5_rc_typelist *t, *t_next;
+ k5_mutex_destroy(&rc_typelist_lock);
+ for (t = typehead; t != &krb5_rc_typelist_dfl; t = t_next) {
+ t_next = t->next;
+ free(t);
+ }
+}
+
+krb5_error_code
+krb5_rc_register_type(krb5_context context, const krb5_rc_ops *ops)
+{
+ struct krb5_rc_typelist *t;
+
+ k5_mutex_lock(&rc_typelist_lock);
+ for (t = typehead;t && strcmp(t->ops->type,ops->type);t = t->next)
+ ;
+ if (t) {
+ k5_mutex_unlock(&rc_typelist_lock);
+ return KRB5_RC_TYPE_EXISTS;
+ }
+ t = (struct krb5_rc_typelist *) malloc(sizeof(struct krb5_rc_typelist));
+ if (t == NULL) {
+ k5_mutex_unlock(&rc_typelist_lock);
+ return KRB5_RC_MALLOC;
+ }
+ t->next = typehead;
+ t->ops = ops;
+ typehead = t;
+ k5_mutex_unlock(&rc_typelist_lock);
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_resolve_type(krb5_context context, krb5_rcache *idptr,
+ const char *type)
+{
+ struct krb5_rc_typelist *t;
+ krb5_error_code err;
+ krb5_rcache id;
+
+ *idptr = NULL;
+
+ /* Find the named type in the list. */
+ k5_mutex_lock(&rc_typelist_lock);
+ for (t = typehead; t && strcmp(t->ops->type, type); t = t->next)
+ ;
+ k5_mutex_unlock(&rc_typelist_lock);
+ if (!t)
+ return KRB5_RC_TYPE_NOTFOUND;
+
+ /* Create and return the rcache structure. */
+ id = malloc(sizeof(*id));
+ if (!id)
+ return KRB5_RC_MALLOC;
+ err = k5_mutex_init(&id->lock);
+ if (err) {
+ free(id);
+ return err;
+ }
+ id->data = NULL; /* Gets real data when resolved */
+ id->magic = 0; /* Gets real magic after resolved */
+ id->ops = t->ops;
+ *idptr = id;
+ return 0;
+}
+
+char * krb5_rc_get_type(krb5_context context, krb5_rcache id)
+{
+ return id->ops->type;
+}
+
+char *
+krb5_rc_default_type(krb5_context context)
+{
+ char *s;
+ if ((s = getenv("KRB5RCACHETYPE")))
+ return s;
+ else
+ return "dfl";
+}
+
+char *
+krb5_rc_default_name(krb5_context context)
+{
+ char *s;
+ if ((s = getenv("KRB5RCACHENAME")))
+ return s;
+ else
+ return (char *) 0;
+}
+
+krb5_error_code
+krb5_rc_default(krb5_context context, krb5_rcache *idptr)
+{
+ krb5_error_code retval;
+ krb5_rcache id;
+
+ *idptr = NULL;
+ retval = krb5_rc_resolve_type(context, &id, krb5_rc_default_type(context));
+ if (retval)
+ return retval;
+ retval = krb5_rc_resolve(context, id, krb5_rc_default_name(context));
+ if (retval) {
+ k5_mutex_destroy(&id->lock);
+ free(id);
+ return retval;
+ }
+ id->magic = KV5M_RCACHE;
+ *idptr = id;
+ return retval;
+}
+
+
+krb5_error_code
+krb5_rc_resolve_full(krb5_context context, krb5_rcache *idptr,
+ const char *string_name)
+{
+ char *type;
+ char *residual;
+ krb5_error_code retval;
+ unsigned int diff;
+ krb5_rcache id;
+
+ *idptr = NULL;
+
+ if (!(residual = strchr(string_name,':')))
+ return KRB5_RC_PARSE;
+
+ diff = residual - string_name;
+ if (!(type = malloc(diff + 1)))
+ return KRB5_RC_MALLOC;
+ (void) strncpy(type, string_name, diff);
+ type[residual - string_name] = '\0';
+
+ retval = krb5_rc_resolve_type(context, &id,type);
+ free(type);
+ if (retval)
+ return retval;
+ if ((retval = krb5_rc_resolve(context, id,residual + 1))) {
+ k5_mutex_destroy(&id->lock);
+ free(id);
+ return retval;
+ }
+ id->magic = KV5M_RCACHE;
+ *idptr = id;
+ return retval;
+}
diff --git a/src/lib/krb5/rcache/rc_base.h b/src/lib/krb5/rcache/rc_base.h
new file mode 100644
index 000000000000..0530b90f8423
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_base.h
@@ -0,0 +1,15 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_base.h */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+#ifndef KRB5_RC_H
+#define KRB5_RC_H
+#include "k5-int.h"
+
+/* all the stuff that was here is now in rcache.h, included by krb5/krb5.h */
+
+#endif
diff --git a/src/lib/krb5/rcache/rc_conv.c b/src/lib/krb5/rcache/rc_conv.c
new file mode 100644
index 000000000000..0e021f5d8e42
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_conv.c
@@ -0,0 +1,76 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_conv.c */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+/*
+ * An implementation for the default replay cache type.
+ */
+
+#include "rc_base.h"
+
+/*
+ Local stuff:
+ krb5_auth_to_replay(context, krb5_tkt_authent *auth,krb5_donot_replay *rep)
+ given auth, take important information and make rep; return -1 if failed
+*/
+
+krb5_error_code
+krb5_auth_to_rep(krb5_context context, krb5_tkt_authent *auth, krb5_donot_replay *rep)
+{
+ krb5_error_code retval;
+ rep->cusec = auth->authenticator->cusec;
+ rep->ctime = auth->authenticator->ctime;
+ if ((retval = krb5_unparse_name(context, auth->ticket->server, &rep->server)))
+ return retval; /* shouldn't happen */
+ if ((retval = krb5_unparse_name(context, auth->authenticator->client,
+ &rep->client))) {
+ free(rep->server);
+ return retval; /* shouldn't happen. */
+ }
+ return 0;
+}
+
+/*
+ * Generate a printable hash value for a message for use in a replay
+ * record. It is not necessary for this hash function to be
+ * collision-proof (the only thing you can do with a second preimage
+ * is produce a false replay error) but for fine granularity replay detection
+ * it is necessary for the function to be consistent across implementations.
+ * When two implementations sharing a single replay cache don't agree on hash
+ * function, the code falls back to legacy replay detection based on
+ * (client, server, timestamp, usec) tuples. We do an unkeyed
+ * SHA256 hash of the message and convert it into uppercase hex
+ * representation.
+ */
+krb5_error_code
+krb5_rc_hash_message(krb5_context context, const krb5_data *message,
+ char **out)
+{
+ krb5_error_code retval;
+ uint8_t cksum[K5_SHA256_HASHLEN];
+ char *hash, *ptr;
+ unsigned int i;
+
+ *out = NULL;
+
+ /* Calculate the binary checksum. */
+ retval = k5_sha256(message, cksum);
+ if (retval)
+ return retval;
+
+ /* Convert the checksum into printable form. */
+ hash = malloc(K5_SHA256_HASHLEN * 2 + 1);
+ if (!hash) {
+ return KRB5_RC_MALLOC;
+ }
+
+ for (i = 0, ptr = hash; i < K5_SHA256_HASHLEN; i++, ptr += 2)
+ snprintf(ptr, 3, "%02X", cksum[i]);
+ *ptr = '\0';
+ *out = hash;
+ return 0;
+}
diff --git a/src/lib/krb5/rcache/rc_dfl.c b/src/lib/krb5/rcache/rc_dfl.c
new file mode 100644
index 000000000000..c4d2c744da40
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_dfl.c
@@ -0,0 +1,851 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_dfl.c */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+/*
+ * An implementation for the default replay cache type.
+ */
+#include "k5-int.h"
+#include "rc_base.h"
+#include "rc_dfl.h"
+#include "rc_io.h"
+#include "rc-int.h"
+
+/*
+ * If NOIOSTUFF is defined at compile time, dfl rcaches will be per-process.
+ */
+
+/*
+ Local stuff:
+
+ static int hash(krb5_donot_replay *rep, int hsize)
+ returns hash value of *rep, between 0 and hsize - 1
+ HASHSIZE
+ size of hash table (constant), can be preset
+ static int cmp(krb5_donot_replay *old, krb5_donot_replay *new, krb5_deltat t)
+ compare old and new; return CMP_REPLAY or CMP_HOHUM
+ static int alive(krb5_context, krb5_donot_replay *new, krb5_deltat t)
+ see if new is still alive; return CMP_EXPIRED or CMP_HOHUM
+ CMP_MALLOC, CMP_EXPIRED, CMP_REPLAY, CMP_HOHUM
+ return codes from cmp(), alive(), and store()
+ struct dfl_data
+ data stored in this cache type, namely "dfl"
+ struct authlist
+ multilinked list of reps
+ static int rc_store(context, krb5_rcache id, krb5_donot_replay *rep)
+ store rep in cache id; return CMP_REPLAY if replay, else CMP_MALLOC/CMP_HOHUM
+
+*/
+
+#ifndef HASHSIZE
+#define HASHSIZE 997 /* a convenient prime */
+#endif
+
+#ifndef EXCESSREPS
+#define EXCESSREPS 30
+#endif
+
+/*
+ * The rcache will be automatically expunged when the number of
+ * expired krb5_donot_replays encountered incidentally in searching
+ * exceeds the number of live krb5_donot_replays by EXCESSREPS. With
+ * the defaults here, a typical cache might build up some 10K of
+ * expired krb5_donot_replays before an automatic expunge, with the
+ * waste basically independent of the number of stores per minute.
+ *
+ * The rcache will also automatically be expunged when it encounters
+ * more than EXCESSREPS expired entries when recovering a cache in
+ * dfl_recover.
+ */
+
+static unsigned int
+hash(krb5_donot_replay *rep, unsigned int hsize)
+{
+ unsigned int h = rep->cusec + rep->ctime;
+ h += *rep->server;
+ h += *rep->client;
+ return h % hsize;
+}
+
+#define CMP_MALLOC -3
+#define CMP_EXPIRED -2
+#define CMP_REPLAY -1
+#define CMP_HOHUM 0
+
+/*ARGSUSED*/
+static int
+cmp(krb5_donot_replay *old, krb5_donot_replay *new1, krb5_deltat t)
+{
+ if ((old->cusec == new1->cusec) && /* most likely to distinguish */
+ (old->ctime == new1->ctime) &&
+ (strcmp(old->client, new1->client) == 0) &&
+ (strcmp(old->server, new1->server) == 0)) { /* always true */
+ /* If both records include message hashes, compare them as well. */
+ if (old->msghash == NULL || new1->msghash == NULL ||
+ strcmp(old->msghash, new1->msghash) == 0)
+ return CMP_REPLAY;
+ }
+ return CMP_HOHUM;
+}
+
+static int
+alive(krb5_int32 mytime, krb5_donot_replay *new1, krb5_deltat t)
+{
+ if (mytime == 0)
+ return CMP_HOHUM; /* who cares? */
+ /* I hope we don't have to worry about overflow */
+ if (new1->ctime + t < mytime)
+ return CMP_EXPIRED;
+ return CMP_HOHUM;
+}
+
+struct dfl_data
+{
+ char *name;
+ krb5_deltat lifespan;
+ unsigned int hsize;
+ int numhits;
+ int nummisses;
+ struct authlist **h;
+ struct authlist *a;
+#ifndef NOIOSTUFF
+ krb5_rc_iostuff d;
+#endif
+ char recovering;
+};
+
+struct authlist
+{
+ krb5_donot_replay rep;
+ struct authlist *na;
+ struct authlist *nh;
+};
+
+/* of course, list is backwards from file */
+/* hash could be forwards since we have to search on match, but naaaah */
+
+static int
+rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep,
+ krb5_int32 now, krb5_boolean fromfile)
+{
+ struct dfl_data *t = (struct dfl_data *)id->data;
+ unsigned int rephash;
+ struct authlist *ta;
+
+ rephash = hash(rep, t->hsize);
+
+ for (ta = t->h[rephash]; ta; ta = ta->nh) {
+ switch(cmp(&ta->rep, rep, t->lifespan))
+ {
+ case CMP_REPLAY:
+ if (fromfile) {
+ /*
+ * This is an expected collision between a hash
+ * extension record and a normal-format record. Make
+ * sure the message hash is included in the stored
+ * record and carry on.
+ */
+ if (!ta->rep.msghash && rep->msghash) {
+ if (!(ta->rep.msghash = strdup(rep->msghash)))
+ return CMP_MALLOC;
+ }
+ return CMP_HOHUM;
+ } else
+ return CMP_REPLAY;
+ case CMP_HOHUM:
+ if (alive(now, &ta->rep, t->lifespan) == CMP_EXPIRED)
+ t->nummisses++;
+ else
+ t->numhits++;
+ break;
+ default:
+ ; /* wtf? */
+ }
+ }
+
+ if (!(ta = (struct authlist *) malloc(sizeof(struct authlist))))
+ return CMP_MALLOC;
+ ta->rep = *rep;
+ ta->rep.client = ta->rep.server = ta->rep.msghash = NULL;
+ if (!(ta->rep.client = strdup(rep->client)))
+ goto error;
+ if (!(ta->rep.server = strdup(rep->server)))
+ goto error;
+ if (rep->msghash && !(ta->rep.msghash = strdup(rep->msghash)))
+ goto error;
+ ta->na = t->a; t->a = ta;
+ ta->nh = t->h[rephash]; t->h[rephash] = ta;
+ return CMP_HOHUM;
+error:
+ if (ta->rep.client)
+ free(ta->rep.client);
+ if (ta->rep.server)
+ free(ta->rep.server);
+ if (ta->rep.msghash)
+ free(ta->rep.msghash);
+ free(ta);
+ return CMP_MALLOC;
+}
+
+char * KRB5_CALLCONV
+krb5_rc_dfl_get_name(krb5_context context, krb5_rcache id)
+{
+ return ((struct dfl_data *) (id->data))->name;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_get_span(krb5_context context, krb5_rcache id,
+ krb5_deltat *lifespan)
+{
+ struct dfl_data *t;
+
+ k5_mutex_lock(&id->lock);
+ t = (struct dfl_data *) id->data;
+ *lifespan = t->lifespan;
+ k5_mutex_unlock(&id->lock);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
+{
+ struct dfl_data *t = (struct dfl_data *)id->data;
+ krb5_error_code retval;
+
+ t->lifespan = lifespan ? lifespan : context->clockskew;
+ /* default to clockskew from the context */
+#ifndef NOIOSTUFF
+ if ((retval = krb5_rc_io_creat(context, &t->d, &t->name))) {
+ return retval;
+ }
+ if ((krb5_rc_io_write(context, &t->d,
+ (krb5_pointer) &t->lifespan, sizeof(t->lifespan))
+ || krb5_rc_io_sync(context, &t->d))) {
+ return KRB5_RC_IO;
+ }
+#endif
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan)
+{
+ krb5_error_code retval;
+
+ k5_mutex_lock(&id->lock);
+ retval = krb5_rc_dfl_init_locked(context, id, lifespan);
+ k5_mutex_unlock(&id->lock);
+ return retval;
+}
+
+/* Called with the mutex already locked. */
+krb5_error_code
+krb5_rc_dfl_close_no_free(krb5_context context, krb5_rcache id)
+{
+ struct dfl_data *t = (struct dfl_data *)id->data;
+ struct authlist *q;
+
+ free(t->h);
+ if (t->name)
+ free(t->name);
+ while ((q = t->a))
+ {
+ t->a = q->na;
+ free(q->rep.client);
+ free(q->rep.server);
+ if (q->rep.msghash)
+ free(q->rep.msghash);
+ free(q);
+ }
+#ifndef NOIOSTUFF
+ (void) krb5_rc_io_close(context, &t->d);
+#endif
+ free(t);
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_close(krb5_context context, krb5_rcache id)
+{
+ k5_mutex_lock(&id->lock);
+ krb5_rc_dfl_close_no_free(context, id);
+ k5_mutex_unlock(&id->lock);
+ k5_mutex_destroy(&id->lock);
+ free(id);
+ return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_destroy(krb5_context context, krb5_rcache id)
+{
+#ifndef NOIOSTUFF
+ if (krb5_rc_io_destroy(context, &((struct dfl_data *) (id->data))->d))
+ return KRB5_RC_IO;
+#endif
+ return krb5_rc_dfl_close(context, id);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_resolve(krb5_context context, krb5_rcache id, char *name)
+{
+ struct dfl_data *t = 0;
+ krb5_error_code retval;
+
+ /* 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) {
+ t->name = strdup(name);
+ if (!t->name) {
+ retval = KRB5_RC_MALLOC;
+ goto cleanup;
+ }
+ } else
+ t->name = 0;
+ t->numhits = t->nummisses = 0;
+ t->hsize = HASHSIZE; /* no need to store---it's memory-only */
+ t->h = (struct authlist **) malloc(t->hsize*sizeof(struct authlist *));
+ if (!t->h) {
+ retval = KRB5_RC_MALLOC;
+ goto cleanup;
+ }
+ memset(t->h, 0, t->hsize*sizeof(struct authlist *));
+ t->a = (struct authlist *) 0;
+#ifndef NOIOSTUFF
+ t->d.fd = -1;
+#endif
+ t->recovering = 0;
+ return 0;
+
+cleanup:
+ if (t) {
+ if (t->name)
+ free(t->name);
+ if (t->h)
+ free(t->h);
+ free(t);
+ }
+ return retval;
+}
+
+void
+krb5_rc_free_entry(krb5_context context, krb5_donot_replay **rep)
+{
+ krb5_donot_replay *rp = *rep;
+
+ *rep = NULL;
+ if (rp)
+ {
+ if (rp->client)
+ free(rp->client);
+ if (rp->server)
+ free(rp->server);
+ if (rp->msghash)
+ free(rp->msghash);
+ rp->client = NULL;
+ rp->server = NULL;
+ rp->msghash = NULL;
+ free(rp);
+ }
+}
+
+/*
+ * Parse a string in the format <len>:<data>, with the length
+ * represented in ASCII decimal. On parse failure, return 0 but set
+ * *result to NULL.
+ */
+static krb5_error_code
+parse_counted_string(char **strptr, char **result)
+{
+ char *str = *strptr, *end;
+ unsigned long len;
+
+ *result = NULL;
+
+ /* Parse the length, expecting a ':' afterwards. */
+ errno = 0;
+ len = strtoul(str, &end, 10);
+ if (errno != 0 || *end != ':' || len > strlen(end + 1))
+ return 0;
+
+ /* Allocate space for *result and copy the data. */
+ *result = malloc(len + 1);
+ if (!*result)
+ return KRB5_RC_MALLOC;
+ memcpy(*result, end + 1, len);
+ (*result)[len] = '\0';
+ *strptr = end + 1 + len;
+ return 0;
+}
+
+/*
+ * Hash extension records have the format:
+ * client = <empty string>
+ * server = SHA256:<msghash> <clientlen>:<client> <serverlen>:<server>
+ * Spaces in the client and server string are represented with
+ * with backslashes. Client and server lengths are represented in
+ * ASCII decimal (which is different from the 32-bit binary we use
+ * elsewhere in the replay cache).
+ *
+ * On parse failure, we leave the record unmodified.
+ */
+static krb5_error_code
+check_hash_extension(krb5_donot_replay *rep)
+{
+ char *msghash = NULL, *client = NULL, *server = NULL, *str, *end;
+ krb5_error_code retval = 0;
+
+ /* Check if this appears to match the hash extension format. */
+ if (*rep->client)
+ return 0;
+ if (strncmp(rep->server, "SHA256:", 7) != 0)
+ return 0;
+
+ /* Parse out the message hash. */
+ str = rep->server + 7;
+ end = strchr(str, ' ');
+ if (!end)
+ return 0;
+ msghash = k5memdup0(str, end - str, &retval);
+ if (!msghash)
+ return KRB5_RC_MALLOC;
+ str = end + 1;
+
+ /* Parse out the client and server. */
+ retval = parse_counted_string(&str, &client);
+ if (retval != 0 || client == NULL)
+ goto error;
+ if (*str != ' ')
+ goto error;
+ str++;
+ retval = parse_counted_string(&str, &server);
+ if (retval != 0 || server == NULL)
+ goto error;
+ if (*str)
+ goto error;
+
+ free(rep->client);
+ free(rep->server);
+ rep->client = client;
+ rep->server = server;
+ rep->msghash = msghash;
+ return 0;
+
+error:
+ if (msghash)
+ free(msghash);
+ if (client)
+ free(client);
+ if (server)
+ free(server);
+ return retval;
+}
+
+static krb5_error_code
+krb5_rc_io_fetch(krb5_context context, struct dfl_data *t,
+ krb5_donot_replay *rep, int maxlen)
+{
+ int len2;
+ unsigned int len;
+ krb5_error_code retval;
+
+ rep->client = rep->server = rep->msghash = NULL;
+
+ retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2,
+ sizeof(len2));
+ if (retval)
+ return retval;
+
+ if ((len2 <= 0) || (len2 >= maxlen))
+ return KRB5_RC_IO_EOF;
+
+ len = len2;
+ rep->client = malloc (len);
+ if (!rep->client)
+ return KRB5_RC_MALLOC;
+
+ retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->client, len);
+ if (retval)
+ goto errout;
+
+ retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2,
+ sizeof(len2));
+ if (retval)
+ goto errout;
+
+ if ((len2 <= 0) || (len2 >= maxlen)) {
+ retval = KRB5_RC_IO_EOF;
+ goto errout;
+ }
+ len = len2;
+
+ rep->server = malloc (len);
+ if (!rep->server) {
+ retval = KRB5_RC_MALLOC;
+ goto errout;
+ }
+
+ retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->server, len);
+ if (retval)
+ goto errout;
+
+ retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->cusec,
+ sizeof(rep->cusec));
+ if (retval)
+ goto errout;
+
+ retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->ctime,
+ sizeof(rep->ctime));
+ if (retval)
+ goto errout;
+
+ retval = check_hash_extension(rep);
+ if (retval)
+ goto errout;
+
+ return 0;
+
+errout:
+ if (rep->client)
+ free(rep->client);
+ if (rep->server)
+ free(rep->server);
+ if (rep->msghash)
+ free(rep->msghash);
+ rep->client = rep->server = 0;
+ return retval;
+}
+
+
+static krb5_error_code
+krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id);
+
+static krb5_error_code
+krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id)
+{
+#ifdef NOIOSTUFF
+ return KRB5_RC_NOIO;
+#else
+
+ struct dfl_data *t = (struct dfl_data *)id->data;
+ krb5_donot_replay *rep = 0;
+ krb5_error_code retval;
+ long max_size;
+ int expired_entries = 0;
+ krb5_int32 now;
+
+ if ((retval = krb5_rc_io_open(context, &t->d, t->name))) {
+ return retval;
+ }
+
+ t->recovering = 1;
+
+ max_size = krb5_rc_io_size(context, &t->d);
+
+ rep = NULL;
+ if (krb5_rc_io_read(context, &t->d, (krb5_pointer) &t->lifespan,
+ sizeof(t->lifespan))) {
+ retval = KRB5_RC_IO;
+ goto io_fail;
+ }
+
+ if (!(rep = (krb5_donot_replay *) malloc(sizeof(krb5_donot_replay)))) {
+ retval = KRB5_RC_MALLOC;
+ goto io_fail;
+ }
+ rep->client = rep->server = rep->msghash = NULL;
+
+ if (krb5_timeofday(context, &now))
+ now = 0;
+
+ /* now read in each auth_replay and insert into table */
+ for (;;) {
+ if (krb5_rc_io_mark(context, &t->d)) {
+ retval = KRB5_RC_IO;
+ goto io_fail;
+ }
+
+ retval = krb5_rc_io_fetch(context, t, rep, (int) max_size);
+
+ if (retval == KRB5_RC_IO_EOF)
+ break;
+ else if (retval != 0)
+ goto io_fail;
+
+ if (alive(now, rep, t->lifespan) != CMP_EXPIRED) {
+ if (rc_store(context, id, rep, now, TRUE) == CMP_MALLOC) {
+ retval = KRB5_RC_MALLOC; goto io_fail;
+ }
+ } else {
+ expired_entries++;
+ }
+
+ /*
+ * free fields allocated by rc_io_fetch
+ */
+ free(rep->server);
+ free(rep->client);
+ if (rep->msghash)
+ free(rep->msghash);
+ rep->client = rep->server = rep->msghash = NULL;
+ }
+ retval = 0;
+ krb5_rc_io_unmark(context, &t->d);
+ /*
+ * An automatic expunge here could remove the need for
+ * mark/unmark but that would be inefficient.
+ */
+io_fail:
+ krb5_rc_free_entry(context, &rep);
+ if (retval)
+ krb5_rc_io_close(context, &t->d);
+ else if (expired_entries > EXCESSREPS)
+ retval = krb5_rc_dfl_expunge_locked(context, id);
+ t->recovering = 0;
+ return retval;
+
+#endif
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_recover(krb5_context context, krb5_rcache id)
+{
+ krb5_error_code ret;
+
+ k5_mutex_lock(&id->lock);
+ ret = krb5_rc_dfl_recover_locked(context, id);
+ k5_mutex_unlock(&id->lock);
+ return ret;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_recover_or_init(krb5_context context, krb5_rcache id,
+ krb5_deltat lifespan)
+{
+ krb5_error_code retval;
+
+ k5_mutex_lock(&id->lock);
+ retval = krb5_rc_dfl_recover_locked(context, id);
+ if (retval)
+ retval = krb5_rc_dfl_init_locked(context, id, lifespan);
+ k5_mutex_unlock(&id->lock);
+ return retval;
+}
+
+static krb5_error_code
+krb5_rc_io_store(krb5_context context, struct dfl_data *t,
+ krb5_donot_replay *rep)
+{
+ size_t clientlen, serverlen;
+ unsigned int len;
+ krb5_error_code ret;
+ struct k5buf buf, extbuf;
+ char *extstr;
+
+ clientlen = strlen(rep->client);
+ serverlen = strlen(rep->server);
+
+ if (rep->msghash) {
+ /*
+ * Write a hash extension record, to be followed by a record
+ * in regular format (without the message hash) for the
+ * benefit of old implementations.
+ */
+
+ /* Format the extension value so we know its length. */
+ k5_buf_init_dynamic(&extbuf);
+ k5_buf_add_fmt(&extbuf, "SHA256:%s %lu:%s %lu:%s", rep->msghash,
+ (unsigned long)clientlen, rep->client,
+ (unsigned long)serverlen, rep->server);
+ if (k5_buf_status(&extbuf) != 0)
+ return KRB5_RC_MALLOC;
+ extstr = extbuf.data;
+
+ /*
+ * Put the extension value into the server field of a
+ * regular-format record, with an empty client field.
+ */
+ k5_buf_init_dynamic(&buf);
+ len = 1;
+ k5_buf_add_len(&buf, (char *)&len, sizeof(len));
+ k5_buf_add_len(&buf, "", 1);
+ len = strlen(extstr) + 1;
+ k5_buf_add_len(&buf, (char *)&len, sizeof(len));
+ k5_buf_add_len(&buf, extstr, len);
+ k5_buf_add_len(&buf, (char *)&rep->cusec, sizeof(rep->cusec));
+ k5_buf_add_len(&buf, (char *)&rep->ctime, sizeof(rep->ctime));
+ free(extstr);
+ } else /* No extension record needed. */
+ k5_buf_init_dynamic(&buf);
+
+ len = clientlen + 1;
+ k5_buf_add_len(&buf, (char *)&len, sizeof(len));
+ k5_buf_add_len(&buf, rep->client, len);
+ len = serverlen + 1;
+ k5_buf_add_len(&buf, (char *)&len, sizeof(len));
+ k5_buf_add_len(&buf, rep->server, len);
+ k5_buf_add_len(&buf, (char *)&rep->cusec, sizeof(rep->cusec));
+ k5_buf_add_len(&buf, (char *)&rep->ctime, sizeof(rep->ctime));
+
+ if (k5_buf_status(&buf) != 0)
+ return KRB5_RC_MALLOC;
+
+ ret = krb5_rc_io_write(context, &t->d, buf.data, buf.len);
+ k5_buf_free(&buf);
+ return ret;
+}
+
+static krb5_error_code krb5_rc_dfl_expunge_locked(krb5_context, krb5_rcache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep)
+{
+ krb5_error_code ret;
+ struct dfl_data *t;
+ krb5_int32 now;
+
+ ret = krb5_timeofday(context, &now);
+ if (ret)
+ return ret;
+
+ k5_mutex_lock(&id->lock);
+
+ switch(rc_store(context, id, rep, now, FALSE)) {
+ case CMP_MALLOC:
+ k5_mutex_unlock(&id->lock);
+ return KRB5_RC_MALLOC;
+ case CMP_REPLAY:
+ k5_mutex_unlock(&id->lock);
+ return KRB5KRB_AP_ERR_REPEAT;
+ case 0: break;
+ default: /* wtf? */ ;
+ }
+ t = (struct dfl_data *)id->data;
+#ifndef NOIOSTUFF
+ ret = krb5_rc_io_store(context, t, rep);
+ if (ret) {
+ k5_mutex_unlock(&id->lock);
+ return ret;
+ }
+#endif
+ /* Shall we automatically expunge? */
+ if (t->nummisses > t->numhits + EXCESSREPS)
+ {
+ ret = krb5_rc_dfl_expunge_locked(context, id);
+ k5_mutex_unlock(&id->lock);
+ return ret;
+ }
+#ifndef NOIOSTUFF
+ else
+ {
+ if (krb5_rc_io_sync(context, &t->d)) {
+ k5_mutex_unlock(&id->lock);
+ return KRB5_RC_IO;
+ }
+ }
+#endif
+ k5_mutex_unlock(&id->lock);
+ return 0;
+}
+
+static krb5_error_code
+krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id)
+{
+ struct dfl_data *t = (struct dfl_data *)id->data;
+#ifdef NOIOSTUFF
+ unsigned int i;
+ struct authlist **q;
+ struct authlist **qt;
+ struct authlist *r;
+ struct authlist *rt;
+ krb5_int32 now;
+
+ if (krb5_timestamp(context, &now))
+ now = 0;
+
+ for (q = &t->a; *q; q = qt) {
+ qt = &(*q)->na;
+ if (alive(now, &(*q)->rep, t->lifespan) == CMP_EXPIRED) {
+ free((*q)->rep.client);
+ free((*q)->rep.server);
+ if ((*q)->rep.msghash)
+ free((*q)->rep.msghash);
+ free(*q);
+ *q = *qt; /* why doesn't this feel right? */
+ }
+ }
+ for (i = 0; i < t->hsize; i++)
+ t->h[i] = (struct authlist *) 0;
+ for (r = t->a; r; r = r->na) {
+ i = hash(&r->rep, t->hsize);
+ rt = t->h[i];
+ t->h[i] = r;
+ r->nh = rt;
+ }
+ return 0;
+#else
+ struct authlist *q;
+ char *name;
+ krb5_error_code retval = 0;
+ krb5_rcache tmp;
+ krb5_deltat lifespan = t->lifespan; /* save original lifespan */
+
+ if (! t->recovering) {
+ name = t->name;
+ t->name = 0; /* Clear name so it isn't freed */
+ (void) krb5_rc_dfl_close_no_free(context, id);
+ retval = krb5_rc_dfl_resolve(context, id, name);
+ free(name);
+ if (retval)
+ return retval;
+ retval = krb5_rc_dfl_recover_locked(context, id);
+ if (retval)
+ return retval;
+ t = (struct dfl_data *)id->data; /* point to recovered cache */
+ }
+
+ retval = krb5_rc_resolve_type(context, &tmp, "dfl");
+ if (retval)
+ return retval;
+ retval = krb5_rc_resolve(context, tmp, 0);
+ if (retval)
+ goto cleanup;
+ retval = krb5_rc_initialize(context, tmp, lifespan);
+ if (retval)
+ goto cleanup;
+ for (q = t->a; q; q = q->na) {
+ if (krb5_rc_io_store(context, (struct dfl_data *)tmp->data, &q->rep)) {
+ retval = KRB5_RC_IO;
+ goto cleanup;
+ }
+ }
+ /* NOTE: We set retval in case we have an error */
+ retval = KRB5_RC_IO;
+ if (krb5_rc_io_sync(context, &((struct dfl_data *)tmp->data)->d))
+ goto cleanup;
+ if (krb5_rc_io_sync(context, &t->d))
+ goto cleanup;
+ if (krb5_rc_io_move(context, &t->d, &((struct dfl_data *)tmp->data)->d))
+ goto cleanup;
+ retval = 0;
+cleanup:
+ (void) krb5_rc_dfl_close(context, tmp);
+ return retval;
+#endif
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_expunge(krb5_context context, krb5_rcache id)
+{
+ krb5_error_code ret;
+
+ k5_mutex_lock(&id->lock);
+ ret = krb5_rc_dfl_expunge_locked(context, id);
+ k5_mutex_unlock(&id->lock);
+ return ret;
+}
diff --git a/src/lib/krb5/rcache/rc_dfl.h b/src/lib/krb5/rcache/rc_dfl.h
new file mode 100644
index 000000000000..ad036a207e3d
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_dfl.h
@@ -0,0 +1,48 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_dfl.h */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+/*
+ * Declarations for the default replay cache implementation.
+ */
+
+#ifndef KRB5_RC_DFL_H
+#define KRB5_RC_DFL_H
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_init(krb5_context, krb5_rcache, krb5_deltat);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_recover(krb5_context, krb5_rcache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_recover_or_init(krb5_context, krb5_rcache, krb5_deltat);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_destroy(krb5_context, krb5_rcache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_close(krb5_context, krb5_rcache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_store(krb5_context, krb5_rcache, krb5_donot_replay *);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_expunge(krb5_context, krb5_rcache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_get_span(krb5_context, krb5_rcache, krb5_deltat *);
+
+char * KRB5_CALLCONV
+krb5_rc_dfl_get_name(krb5_context, krb5_rcache);
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_dfl_resolve(krb5_context, krb5_rcache, char *);
+
+krb5_error_code krb5_rc_dfl_close_no_free(krb5_context, krb5_rcache);
+void krb5_rc_free_entry(krb5_context, krb5_donot_replay **);
+#endif
diff --git a/src/lib/krb5/rcache/rc_io.c b/src/lib/krb5/rcache/rc_io.c
new file mode 100644
index 000000000000..b9859fe9f657
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_io.c
@@ -0,0 +1,522 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_io.c */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+/*
+ * I/O functions for the replay cache default implementation.
+ */
+
+#if defined(_WIN32)
+# define PATH_SEPARATOR "\\"
+#else
+# define PATH_SEPARATOR "/"
+#endif
+
+#define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include "k5-int.h"
+#include <stdio.h> /* for P_tmpdir */
+#include "rc_base.h"
+#include "rc_dfl.h"
+#include "rc_io.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#if !defined(_WINSOCKAPI_)
+#include <netinet/in.h>
+#endif
+#else
+#error find some way to use net-byte-order file version numbers.
+#endif
+
+#define UNIQUE getpid() /* hopefully unique number */
+
+#define GETDIR (dir = getdir(), dirlen = strlen(dir) + sizeof(PATH_SEPARATOR) - 1)
+
+static char *
+getdir(void)
+{
+ char *dir;
+
+ if (!(dir = getenv("KRB5RCACHEDIR"))) {
+#if defined(_WIN32)
+ if (!(dir = getenv("TEMP")))
+ if (!(dir = getenv("TMP")))
+ dir = "C:";
+#else
+ if (!(dir = getenv("TMPDIR"))) {
+#ifdef RCTMPDIR
+ dir = RCTMPDIR;
+#else
+ dir = "/tmp";
+#endif
+ }
+#endif
+ }
+ return dir;
+}
+
+/*
+ * Called from krb5_rc_io_creat(); calls mkstemp() and does some
+ * sanity checking on the file modes in case some broken mkstemp()
+ * implementation creates the file with overly permissive modes. To
+ * avoid race conditions, do not fchmod() a file for which mkstemp set
+ * incorrect modes.
+ */
+static krb5_error_code
+krb5_rc_io_mkstemp(krb5_context context, krb5_rc_iostuff *d, char *dir)
+{
+ krb5_error_code retval = 0;
+#if HAVE_SYS_STAT_H
+ struct stat stbuf;
+
+ memset(&stbuf, 0, sizeof(stbuf));
+#endif
+ if (asprintf(&d->fn, "%s%skrb5_RCXXXXXX",
+ dir, PATH_SEPARATOR) < 0) {
+ d->fn = NULL;
+ return KRB5_RC_IO_MALLOC;
+ }
+ d->fd = mkstemp(d->fn);
+ if (d->fd == -1) {
+ /*
+ * This return value is deliberate because d->fd == -1 causes
+ * caller to go into errno interpretation code.
+ */
+ return 0;
+ }
+#if HAVE_SYS_STAT_H
+ /*
+ * Be paranoid and check that mkstemp made the file accessible
+ * only to the user.
+ */
+ retval = fstat(d->fd, &stbuf);
+ if (retval) {
+ k5_setmsg(context, retval,
+ _("Cannot fstat replay cache file %s: %s"),
+ d->fn, strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ if (stbuf.st_mode & 077) {
+ k5_setmsg(context, retval,
+ _("Insecure mkstemp() file mode for replay cache file %s; "
+ "try running this program with umask 077"), d->fn);
+ return KRB5_RC_IO_UNKNOWN;
+ }
+#endif
+ return 0;
+}
+
+#if 0
+static krb5_error_code rc_map_errno (int) __attribute__((cold));
+#endif
+
+static krb5_error_code
+rc_map_errno (krb5_context context, int e, const char *fn,
+ const char *operation)
+{
+ switch (e) {
+ case EFBIG:
+#ifdef EDQUOT
+ case EDQUOT:
+#endif
+ case ENOSPC:
+ return KRB5_RC_IO_SPACE;
+
+ case EIO:
+ return KRB5_RC_IO_IO;
+
+ case EPERM:
+ case EACCES:
+ case EROFS:
+ case EEXIST:
+ k5_setmsg(context, KRB5_RC_IO_PERM,
+ _("Cannot %s replay cache file %s: %s"),
+ operation, fn, strerror(e));
+ return KRB5_RC_IO_PERM;
+
+ default:
+ k5_setmsg(context, KRB5_RC_IO_UNKNOWN, _("Cannot %s replay cache: %s"),
+ operation, strerror(e));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+}
+
+
+krb5_error_code
+krb5_rc_io_creat(krb5_context context, krb5_rc_iostuff *d, char **fn)
+{
+ krb5_int16 rc_vno = htons(KRB5_RC_VNO);
+ krb5_error_code retval = 0;
+ int flags, do_not_unlink = 0;
+ char *dir;
+ size_t dirlen;
+
+ GETDIR;
+ if (fn && *fn) {
+ if (asprintf(&d->fn, "%s%s%s", dir, PATH_SEPARATOR, *fn) < 0)
+ return KRB5_RC_IO_MALLOC;
+ d->fd = -1;
+ do {
+ if (unlink(d->fn) == -1 && errno != ENOENT)
+ break;
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY;
+ d->fd = THREEPARAMOPEN(d->fn, flags, 0600);
+ } while (d->fd == -1 && errno == EEXIST);
+ } else {
+ retval = krb5_rc_io_mkstemp(context, d, dir);
+ if (retval)
+ goto cleanup;
+ if (d->fd != -1 && fn) {
+ *fn = strdup(d->fn + dirlen);
+ if (*fn == NULL) {
+ free(d->fn);
+ return KRB5_RC_IO_MALLOC;
+ }
+ }
+ }
+ if (d->fd == -1) {
+ retval = rc_map_errno(context, errno, d->fn, "create");
+ if (retval == KRB5_RC_IO_PERM)
+ do_not_unlink = 1;
+ goto cleanup;
+ }
+ set_cloexec_fd(d->fd);
+ retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno,
+ sizeof(rc_vno));
+ if (retval)
+ goto cleanup;
+
+ retval = krb5_rc_io_sync(context, d);
+
+cleanup:
+ if (retval) {
+ if (d->fn) {
+ if (!do_not_unlink)
+ (void) unlink(d->fn);
+ free(d->fn);
+ d->fn = NULL;
+ }
+ if (d->fd != -1) {
+ (void) close(d->fd);
+ }
+ }
+ return retval;
+}
+
+static krb5_error_code
+krb5_rc_io_open_internal(krb5_context context, krb5_rc_iostuff *d, char *fn,
+ char* full_pathname)
+{
+ krb5_int16 rc_vno;
+ krb5_error_code retval = 0;
+ int do_not_unlink = 1;
+#ifndef NO_USERID
+ struct stat sb1, sb2;
+#endif
+ char *dir;
+
+ dir = getdir();
+ if (full_pathname) {
+ if (!(d->fn = strdup(full_pathname)))
+ return KRB5_RC_IO_MALLOC;
+ } else {
+ if (asprintf(&d->fn, "%s%s%s", dir, PATH_SEPARATOR, fn) < 0)
+ return KRB5_RC_IO_MALLOC;
+ }
+
+#ifdef NO_USERID
+ d->fd = THREEPARAMOPEN(d->fn, O_RDWR | O_BINARY, 0600);
+ if (d->fd == -1) {
+ retval = rc_map_errno(context, errno, d->fn, "open");
+ goto cleanup;
+ }
+#else
+ d->fd = -1;
+ retval = lstat(d->fn, &sb1);
+ if (retval != 0) {
+ retval = rc_map_errno(context, errno, d->fn, "lstat");
+ goto cleanup;
+ }
+ d->fd = THREEPARAMOPEN(d->fn, O_RDWR | O_BINARY, 0600);
+ if (d->fd < 0) {
+ retval = rc_map_errno(context, errno, d->fn, "open");
+ goto cleanup;
+ }
+ retval = fstat(d->fd, &sb2);
+ if (retval < 0) {
+ retval = rc_map_errno(context, errno, d->fn, "fstat");
+ goto cleanup;
+ }
+ /* check if someone was playing with symlinks */
+ if ((sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino)
+ || (sb1.st_mode & S_IFMT) != S_IFREG)
+ {
+ retval = KRB5_RC_IO_PERM;
+ k5_setmsg(context, retval, "rcache not a file %s", d->fn);
+ goto cleanup;
+ }
+ /* check that non other can read/write/execute the file */
+ if (sb1.st_mode & 077) {
+ k5_setmsg(context, retval,
+ _("Insecure file mode for replay cache file %s"), d->fn);
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ /* owned by me */
+ if (sb1.st_uid != geteuid()) {
+ retval = KRB5_RC_IO_PERM;
+ k5_setmsg(context, retval, _("rcache not owned by %d"),
+ (int)geteuid());
+ goto cleanup;
+ }
+#endif
+ set_cloexec_fd(d->fd);
+
+ do_not_unlink = 0;
+ retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno,
+ sizeof(rc_vno));
+ if (retval)
+ goto cleanup;
+
+ if (ntohs(rc_vno) != KRB5_RC_VNO)
+ retval = KRB5_RCACHE_BADVNO;
+
+cleanup:
+ if (retval) {
+ if (!do_not_unlink)
+ (void) unlink(d->fn);
+ free(d->fn);
+ d->fn = NULL;
+ if (d->fd >= 0)
+ (void) close(d->fd);
+ }
+ return retval;
+}
+
+krb5_error_code
+krb5_rc_io_open(krb5_context context, krb5_rc_iostuff *d, char *fn)
+{
+ return krb5_rc_io_open_internal(context, d, fn, NULL);
+}
+
+krb5_error_code
+krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1,
+ krb5_rc_iostuff *old)
+{
+#if defined(_WIN32) || defined(__CYGWIN__)
+ char *new_fn = NULL;
+ char *old_fn = NULL;
+ off_t offset = 0;
+ krb5_error_code retval = 0;
+ /*
+ * Initial work around provided by Tom Sanfilippo to work around
+ * poor Windows emulation of POSIX functions. Rename and dup has
+ * different semantics!
+ *
+ * Additional fixes and explanation provided by dalmeida@mit.edu:
+ *
+ * First, we save the offset of "old". Then, we close and remove
+ * the "new" file so we can do the rename. We also close "old" to
+ * make sure the rename succeeds (though that might not be
+ * necessary on some systems).
+ *
+ * Next, we do the rename. If all goes well, we seek the "new"
+ * file to the position "old" was at.
+ *
+ * --- WARNING!!! ---
+ *
+ * Since "old" is now gone, we mourn its disappearance, but we
+ * cannot emulate that Unix behavior... THIS BEHAVIOR IS
+ * DIFFERENT FROM UNIX. However, it is ok because this function
+ * gets called such that "old" gets closed right afterwards.
+ */
+ offset = lseek(old->fd, 0, SEEK_CUR);
+
+ new_fn = new1->fn;
+ new1->fn = NULL;
+ close(new1->fd);
+ new1->fd = -1;
+
+ unlink(new_fn);
+
+ old_fn = old->fn;
+ old->fn = NULL;
+ close(old->fd);
+ old->fd = -1;
+
+ if (rename(old_fn, new_fn) == -1) { /* MUST be atomic! */
+ retval = KRB5_RC_IO_UNKNOWN;
+ goto cleanup;
+ }
+
+ retval = krb5_rc_io_open_internal(context, new1, 0, new_fn);
+ if (retval)
+ goto cleanup;
+
+ if (lseek(new1->fd, offset, SEEK_SET) == -1) {
+ retval = KRB5_RC_IO_UNKNOWN;
+ goto cleanup;
+ }
+
+cleanup:
+ free(new_fn);
+ free(old_fn);
+ return retval;
+#else
+ char *fn = NULL;
+ if (rename(old->fn, new1->fn) == -1) /* MUST be atomic! */
+ return KRB5_RC_IO_UNKNOWN;
+ fn = new1->fn;
+ new1->fn = NULL; /* avoid clobbering */
+ (void) krb5_rc_io_close(context, new1);
+ new1->fn = fn;
+ new1->fd = dup(old->fd);
+ set_cloexec_fd(new1->fd);
+ return 0;
+#endif
+}
+
+krb5_error_code
+krb5_rc_io_write(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
+ unsigned int num)
+{
+ if (write(d->fd, (char *) buf, num) == -1)
+ switch(errno)
+ {
+#ifdef EDQUOT
+ case EDQUOT:
+#endif
+ case EFBIG:
+ case ENOSPC:
+ k5_setmsg(context, KRB5_RC_IO_SPACE,
+ _("Can't write to replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_SPACE;
+ case EIO:
+ k5_setmsg(context, KRB5_RC_IO_IO,
+ _("Can't write to replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_IO;
+ case EBADF:
+ default:
+ k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
+ _("Can't write to replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_io_sync(krb5_context context, krb5_rc_iostuff *d)
+{
+#if defined(_WIN32)
+#ifndef fsync
+#define fsync _commit
+#endif
+#endif
+ if (fsync(d->fd) == -1) {
+ switch(errno)
+ {
+ case EBADF: return KRB5_RC_IO_UNKNOWN;
+ case EIO: return KRB5_RC_IO_IO;
+ default:
+ k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
+ _("Cannot sync replay cache file: %s"), strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_io_read(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf,
+ unsigned int num)
+{
+ int count;
+ if ((count = read(d->fd, (char *) buf, num)) == -1)
+ switch(errno)
+ {
+ case EIO: return KRB5_RC_IO_IO;
+ case EBADF:
+ default:
+ k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
+ _("Can't read from replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ if (count < 0 || (unsigned int)count != num)
+ return KRB5_RC_IO_EOF;
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_io_close(krb5_context context, krb5_rc_iostuff *d)
+{
+ if (d->fn != NULL) {
+ free(d->fn);
+ d->fn = NULL;
+ }
+ if (d->fd != -1) {
+ if (close(d->fd) == -1) /* can't happen */
+ return KRB5_RC_IO_UNKNOWN;
+ d->fd = -1;
+ }
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_io_destroy(krb5_context context, krb5_rc_iostuff *d)
+{
+ if (unlink(d->fn) == -1)
+ switch(errno)
+ {
+ case EIO:
+ k5_setmsg(context, KRB5_RC_IO_IO,
+ _("Can't destroy replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_IO;
+ case EPERM:
+ case EBUSY:
+ case EROFS:
+ k5_setmsg(context, KRB5_RC_IO_PERM,
+ _("Can't destroy replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_PERM;
+ case EBADF:
+ default:
+ k5_setmsg(context, KRB5_RC_IO_UNKNOWN,
+ _("Can't destroy replay cache: %s"), strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_io_mark(krb5_context context, krb5_rc_iostuff *d)
+{
+ d->mark = lseek(d->fd, (off_t) 0, SEEK_CUR); /* can't fail */
+ return 0;
+}
+
+krb5_error_code
+krb5_rc_io_unmark(krb5_context context, krb5_rc_iostuff *d)
+{
+ (void) lseek(d->fd, d->mark, SEEK_SET); /* if it fails, tough luck */
+ return 0;
+}
+
+long
+krb5_rc_io_size(krb5_context context, krb5_rc_iostuff *d)
+{
+ struct stat statb;
+
+ if (fstat(d->fd, &statb) == 0)
+ return statb.st_size;
+ else
+ return 0;
+}
diff --git a/src/lib/krb5/rcache/rc_io.h b/src/lib/krb5/rcache/rc_io.h
new file mode 100644
index 000000000000..f5ab23903822
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_io.h
@@ -0,0 +1,60 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_io.h */
+/*
+ * This file of the Kerberos V5 software is derived from public-domain code
+ * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>.
+ *
+ */
+
+/*
+ * Declarations for the I/O sub-package of the replay cache
+ */
+
+#ifndef KRB5_RC_IO_H
+#define KRB5_RC_IO_H
+
+typedef struct krb5_rc_iostuff {
+ int fd;
+#ifdef MSDOS_FILESYSTEM
+ long mark;
+#else
+ off_t mark; /* on newer systems, should be pos_t */
+#endif
+ char *fn;
+} krb5_rc_iostuff;
+
+/* first argument is always iostuff for result file */
+
+krb5_error_code
+krb5_rc_io_creat(krb5_context, krb5_rc_iostuff *, char **);
+
+krb5_error_code
+krb5_rc_io_open(krb5_context, krb5_rc_iostuff *, char *);
+
+krb5_error_code
+krb5_rc_io_move(krb5_context, krb5_rc_iostuff *, krb5_rc_iostuff *);
+
+krb5_error_code
+krb5_rc_io_write(krb5_context, krb5_rc_iostuff *, krb5_pointer, unsigned int);
+
+krb5_error_code
+krb5_rc_io_read(krb5_context, krb5_rc_iostuff *, krb5_pointer, unsigned int);
+
+krb5_error_code
+krb5_rc_io_close(krb5_context, krb5_rc_iostuff *);
+
+krb5_error_code
+krb5_rc_io_destroy(krb5_context, krb5_rc_iostuff *);
+
+krb5_error_code
+krb5_rc_io_mark(krb5_context, krb5_rc_iostuff *);
+
+krb5_error_code
+krb5_rc_io_unmark(krb5_context, krb5_rc_iostuff *);
+
+krb5_error_code
+krb5_rc_io_sync(krb5_context, krb5_rc_iostuff *);
+
+long
+krb5_rc_io_size(krb5_context, krb5_rc_iostuff *);
+#endif
diff --git a/src/lib/krb5/rcache/rc_none.c b/src/lib/krb5/rcache/rc_none.c
new file mode 100644
index 000000000000..e30aed09f135
--- /dev/null
+++ b/src/lib/krb5/rcache/rc_none.c
@@ -0,0 +1,96 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rc_none.c */
+/*
+ * Copyright 2004 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ *
+ * replay cache no-op implementation
+ */
+
+#include "k5-int.h"
+#include "rc-int.h"
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_none_init(krb5_context ctx, krb5_rcache rc, krb5_deltat d)
+{
+ return 0;
+}
+#define krb5_rc_none_recover_or_init krb5_rc_none_init
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_none_noargs(krb5_context ctx, krb5_rcache rc)
+{
+ return 0;
+}
+#define krb5_rc_none_recover krb5_rc_none_noargs
+#define krb5_rc_none_expunge krb5_rc_none_noargs
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_none_close(krb5_context ctx, krb5_rcache rc)
+{
+ free (rc);
+ return 0;
+}
+#define krb5_rc_none_destroy krb5_rc_none_close
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_none_store(krb5_context ctx, krb5_rcache rc, krb5_donot_replay *r)
+{
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_none_get_span(krb5_context ctx, krb5_rcache rc, krb5_deltat *d)
+{
+ return 0;
+}
+
+static char * KRB5_CALLCONV
+krb5_rc_none_get_name(krb5_context ctx, krb5_rcache rc)
+{
+ return "";
+}
+
+static krb5_error_code KRB5_CALLCONV
+krb5_rc_none_resolve(krb5_context ctx, krb5_rcache rc, char *name)
+{
+ rc->data = "none";
+ return 0;
+}
+
+const krb5_rc_ops krb5_rc_none_ops = {
+ 0,
+ "none",
+ krb5_rc_none_init,
+ krb5_rc_none_recover,
+ krb5_rc_none_recover_or_init,
+ krb5_rc_none_destroy,
+ krb5_rc_none_close,
+ krb5_rc_none_store,
+ krb5_rc_none_expunge,
+ krb5_rc_none_get_span,
+ krb5_rc_none_get_name,
+ krb5_rc_none_resolve
+};
diff --git a/src/lib/krb5/rcache/rcdef.c b/src/lib/krb5/rcache/rcdef.c
new file mode 100644
index 000000000000..01d166bec280
--- /dev/null
+++ b/src/lib/krb5/rcache/rcdef.c
@@ -0,0 +1,45 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rcdef.c - Default replay cache operations vector */
+/*
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include "rc-int.h"
+#include "rc_dfl.h"
+
+const krb5_rc_ops krb5_rc_dfl_ops =
+{
+ 0,
+ "dfl",
+ krb5_rc_dfl_init,
+ krb5_rc_dfl_recover,
+ krb5_rc_dfl_recover_or_init,
+ krb5_rc_dfl_destroy,
+ krb5_rc_dfl_close,
+ krb5_rc_dfl_store,
+ krb5_rc_dfl_expunge,
+ krb5_rc_dfl_get_span,
+ krb5_rc_dfl_get_name,
+ krb5_rc_dfl_resolve
+};
diff --git a/src/lib/krb5/rcache/rcfns.c b/src/lib/krb5/rcache/rcfns.c
new file mode 100644
index 000000000000..b12864ab51d6
--- /dev/null
+++ b/src/lib/krb5/rcache/rcfns.c
@@ -0,0 +1,95 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/rcfns.c */
+/*
+ * Copyright 2001 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Dispatch methods for replay cache code.
+ */
+
+#include "k5-int.h"
+#include "rc-int.h"
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_initialize (krb5_context context, krb5_rcache id, krb5_deltat span)
+{
+ return krb5_x(id->ops->init,(context, id, span));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_recover_or_initialize (krb5_context context, krb5_rcache id,
+ krb5_deltat span)
+{
+ return krb5_x(id->ops->recover_or_init,(context, id, span));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_recover (krb5_context context, krb5_rcache id)
+{
+ return krb5_x((id)->ops->recover,(context, id));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_destroy (krb5_context context, krb5_rcache id)
+{
+ return krb5_x((id)->ops->destroy,(context, id));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_close (krb5_context context, krb5_rcache id)
+{
+ return krb5_x((id)->ops->close,(context, id));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_store (krb5_context context, krb5_rcache id,
+ krb5_donot_replay *dontreplay)
+{
+ return krb5_x((id)->ops->store,(context, id, dontreplay));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_expunge (krb5_context context, krb5_rcache id)
+{
+ return krb5_x((id)->ops->expunge,(context, id));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_get_lifespan (krb5_context context, krb5_rcache id,
+ krb5_deltat *spanp)
+{
+ return krb5_x((id)->ops->get_span,(context, id, spanp));
+}
+
+char *KRB5_CALLCONV
+krb5_rc_get_name (krb5_context context, krb5_rcache id)
+{
+ return krb5_xc((id)->ops->get_name,(context, id));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_rc_resolve (krb5_context context, krb5_rcache id, char *name)
+{
+ return krb5_x((id)->ops->resolve,(context, id, name));
+}
diff --git a/src/lib/krb5/rcache/ser_rc.c b/src/lib/krb5/rcache/ser_rc.c
new file mode 100644
index 000000000000..556af21e5e48
--- /dev/null
+++ b/src/lib/krb5/rcache/ser_rc.c
@@ -0,0 +1,212 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/ser_rc.c - Serialize replay cache context */
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include "rc-int.h"
+
+/*
+ * Routines to deal with externalizing krb5_rcache.
+ * krb5_rcache_size();
+ * krb5_rcache_externalize();
+ * krb5_rcache_internalize();
+ */
+static krb5_error_code
+krb5_rcache_size(krb5_context, krb5_pointer, size_t *);
+
+static krb5_error_code
+krb5_rcache_externalize(krb5_context, krb5_pointer, krb5_octet **, size_t *);
+
+static krb5_error_code
+krb5_rcache_internalize(krb5_context,krb5_pointer *, krb5_octet **, size_t *);
+
+/*
+ * Serialization entry for this type.
+ */
+static const krb5_ser_entry krb5_rcache_ser_entry = {
+ KV5M_RCACHE, /* Type */
+ krb5_rcache_size, /* Sizer routine */
+ krb5_rcache_externalize, /* Externalize routine */
+ krb5_rcache_internalize /* Internalize routine */
+};
+
+/*
+ * krb5_rcache_size() - Determine the size required to externalize
+ * this krb5_rcache variant.
+ */
+static krb5_error_code
+krb5_rcache_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep)
+{
+ krb5_error_code kret;
+ krb5_rcache rcache;
+ size_t required;
+
+ kret = EINVAL;
+ if ((rcache = (krb5_rcache) arg)) {
+ /*
+ * Saving FILE: variants of krb5_rcache requires at minimum:
+ * krb5_int32 for KV5M_RCACHE
+ * krb5_int32 for length of rcache name.
+ * krb5_int32 for KV5M_RCACHE
+ */
+ required = sizeof(krb5_int32) * 3;
+ if (rcache->ops && rcache->ops->type)
+ required += (strlen(rcache->ops->type)+1);
+
+ /*
+ * The rcache name is formed as follows:
+ * <type>:<name>
+ */
+ required += strlen(krb5_rc_get_name(kcontext, rcache));
+
+ kret = 0;
+ *sizep += required;
+ }
+ return(kret);
+}
+
+/*
+ * krb5_rcache_externalize() - Externalize the krb5_rcache.
+ */
+static krb5_error_code
+krb5_rcache_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain)
+{
+ krb5_error_code kret;
+ krb5_rcache rcache;
+ size_t required;
+ krb5_octet *bp;
+ size_t remain;
+ char *rcname;
+ char *fnamep;
+
+ required = 0;
+ bp = *buffer;
+ remain = *lenremain;
+ kret = EINVAL;
+ if ((rcache = (krb5_rcache) arg)) {
+ kret = ENOMEM;
+ if (!krb5_rcache_size(kcontext, arg, &required) &&
+ (required <= remain)) {
+ /* Our identifier */
+ (void) krb5_ser_pack_int32(KV5M_RCACHE, &bp, &remain);
+
+ fnamep = krb5_rc_get_name(kcontext, rcache);
+
+ if (rcache->ops->type) {
+ if (asprintf(&rcname, "%s:%s", rcache->ops->type, fnamep) < 0)
+ rcname = NULL;
+ } else
+ rcname = strdup(fnamep);
+
+ if (rcname) {
+ /* Put the length of the file name */
+ (void) krb5_ser_pack_int32((krb5_int32) strlen(rcname),
+ &bp, &remain);
+
+ /* Put the name */
+ (void) krb5_ser_pack_bytes((krb5_octet *) rcname,
+ strlen(rcname),
+ &bp, &remain);
+
+ /* Put the trailer */
+ (void) krb5_ser_pack_int32(KV5M_RCACHE, &bp, &remain);
+ kret = 0;
+ *buffer = bp;
+ *lenremain = remain;
+ free(rcname);
+ }
+ }
+ }
+ return(kret);
+}
+
+/*
+ * krb5_rcache_internalize() - Internalize the krb5_rcache.
+ */
+static krb5_error_code
+krb5_rcache_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain)
+{
+ krb5_error_code kret;
+ krb5_rcache rcache = NULL;
+ krb5_int32 ibuf;
+ krb5_octet *bp;
+ size_t remain;
+ char *rcname = NULL;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ /* Read our magic number */
+ if (krb5_ser_unpack_int32(&ibuf, &bp, &remain) || ibuf != KV5M_RCACHE)
+ return EINVAL;
+
+ /* Get the length of the rcache name */
+ kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (kret)
+ return kret;
+
+ /* Get the rcache name. */
+ rcname = malloc(ibuf + 1);
+ if (!rcname)
+ return ENOMEM;
+ kret = krb5_ser_unpack_bytes((krb5_octet*)rcname, (size_t) ibuf,
+ &bp, &remain);
+ if (kret)
+ goto cleanup;
+ rcname[ibuf] = '\0';
+
+ /* Resolve and recover the rcache. */
+ kret = krb5_rc_resolve_full(kcontext, &rcache, rcname);
+ if (kret)
+ goto cleanup;
+ krb5_rc_recover(kcontext, rcache);
+
+ /* Read our magic number again. */
+ kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (kret)
+ goto cleanup;
+ if (ibuf != KV5M_RCACHE) {
+ kret = EINVAL;
+ goto cleanup;
+ }
+
+ *buffer = bp;
+ *lenremain = remain;
+ *argp = (krb5_pointer) rcache;
+cleanup:
+ free(rcname);
+ if (kret != 0 && rcache)
+ krb5_rc_close(kcontext, rcache);
+ return kret;
+}
+
+/*
+ * Register the rcache serializer.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_ser_rcache_init(krb5_context kcontext)
+{
+ return(krb5_register_serializer(kcontext, &krb5_rcache_ser_entry));
+}
diff --git a/src/lib/krb5/rcache/t_replay.c b/src/lib/krb5/rcache/t_replay.c
new file mode 100644
index 000000000000..db273ec2f221
--- /dev/null
+++ b/src/lib/krb5/rcache/t_replay.c
@@ -0,0 +1,265 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/t_replay.c - Test harness for replay cache */
+/*
+ * Copyright (C) 2009 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "%s: Usage:\n", progname);
+ fprintf(stderr, " %s dump <filename>\n", progname);
+ fprintf(stderr, " %s store <rc> <cli> <srv> <msg> <tstamp> <usec>"
+ " <now> <now-usec>\n", progname);
+ fprintf(stderr, " %s expunge <rc> <now> <now-usec>\n", progname);
+ exit(1);
+}
+
+static char *
+read_counted_string(FILE *fp)
+{
+ unsigned int len;
+ char *str;
+
+ if (fread(&len, sizeof(len), 1, fp) != 1)
+ return NULL;
+ if (len == 0 || len > 10000)
+ return NULL;
+ if ((str = malloc(len)) == NULL)
+ return NULL;
+ if (fread(str, 1, len, fp) != len)
+ return NULL;
+ if (str[len - 1] != 0)
+ return NULL;
+ return str;
+}
+
+static void
+dump_rcache(const char *filename)
+{
+ FILE *fp;
+ krb5_deltat lifespan;
+ krb5_int16 vno;
+ char *str;
+ krb5_int32 usec;
+ krb5_timestamp timestamp;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Can't open filename: %s\n", strerror(errno));
+ return;
+ }
+ if (fread(&vno, sizeof(vno), 1, fp) != 1)
+ return;
+ if (fread(&lifespan, sizeof(lifespan), 1, fp) != 1)
+ return;
+ printf("Lifespan: %ld\n", (long) lifespan);
+ while (1) {
+ printf("---\n");
+
+ if (!(str = read_counted_string(fp)))
+ return;
+ printf("Client: %s\n", str);
+ free(str);
+
+ if (!(str = read_counted_string(fp)))
+ return;
+ printf("Server: %s\n", str);
+ free(str);
+
+ if (fread(&usec, sizeof(usec), 1, fp) != 1)
+ return;
+ printf("Microseconds: %ld\n", (long) usec);
+
+ if (fread(&timestamp, sizeof(timestamp), 1, fp) != 1)
+ return;
+ printf("Timestamp: %ld\n", (long) timestamp);
+ }
+}
+
+static void
+store(krb5_context ctx, char *rcspec, char *client, char *server, char *msg,
+ krb5_timestamp timestamp, krb5_int32 usec, krb5_timestamp now_timestamp,
+ krb5_int32 now_usec)
+{
+ krb5_rcache rc = NULL;
+ krb5_error_code retval = 0;
+ char *hash = NULL;
+ krb5_donot_replay rep;
+ krb5_data d;
+
+ if (now_timestamp > 0)
+ krb5_set_debugging_time(ctx, now_timestamp, now_usec);
+ if ((retval = krb5_rc_resolve_full(ctx, &rc, rcspec)))
+ goto cleanup;
+ if ((retval = krb5_rc_recover_or_initialize(ctx, rc, ctx->clockskew)))
+ goto cleanup;
+ if (msg) {
+ d.data = msg;
+ d.length = strlen(msg);
+ if ((retval = krb5_rc_hash_message(ctx, &d, &hash)))
+ goto cleanup;
+ }
+ rep.client = client;
+ rep.server = server;
+ rep.msghash = hash;
+ rep.cusec = usec;
+ rep.ctime = timestamp;
+ retval = krb5_rc_store(ctx, rc, &rep);
+cleanup:
+ if (retval == KRB5KRB_AP_ERR_REPEAT)
+ printf("Replay\n");
+ else if (!retval)
+ printf("Entry successfully stored\n");
+ else
+ fprintf(stderr, "Failure: %s\n", krb5_get_error_message(ctx, retval));
+ if (rc)
+ krb5_rc_close(ctx, rc);
+ if (hash)
+ free(hash);
+}
+
+static void
+expunge(krb5_context ctx, char *rcspec, krb5_timestamp now_timestamp,
+ krb5_int32 now_usec)
+{
+ krb5_rcache rc = NULL;
+ krb5_error_code retval = 0;
+
+ if (now_timestamp > 0)
+ krb5_set_debugging_time(ctx, now_timestamp, now_usec);
+ if ((retval = krb5_rc_resolve_full(ctx, &rc, rcspec)))
+ goto cleanup;
+ if ((retval = krb5_rc_recover_or_initialize(ctx, rc, ctx->clockskew)))
+ goto cleanup;
+ retval = krb5_rc_expunge(ctx, rc);
+cleanup:
+ if (!retval)
+ printf("Cache successfully expunged\n");
+ else
+ fprintf(stderr, "Failure: %s\n", krb5_get_error_message(ctx, retval));
+ if (rc)
+ krb5_rc_close(ctx, rc);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context ctx;
+ krb5_error_code retval;
+ const char *progname;
+
+ retval = krb5_init_context(&ctx);
+ if (retval) {
+ fprintf(stderr, "krb5_init_context returned error %ld\n",
+ (long) retval);
+ exit(1);
+ }
+ progname = argv[0];
+
+ /* Parse arguments. */
+ argc--; argv++;
+ while (argc) {
+ if (strcmp(*argv, "dump") == 0) {
+ /*
+ * Without going through the rcache interface, dump a
+ * named dfl-format rcache file to stdout. Takes a full
+ * pathname argument.
+ */
+ const char *filename;
+
+ argc--; argv++;
+ if (!argc) usage(progname);
+ filename = *argv;
+ dump_rcache(filename);
+ } else if (strcmp(*argv, "store") == 0) {
+ /*
+ * Using the rcache interface, store a replay record.
+ * Takes an rcache spec like dfl:host as the first
+ * argument. If non-empty, the "msg" argument will be
+ * hashed and provided in the replay record. The
+ * now-timestamp argument can be 0 to use the current
+ * time.
+ */
+ char *rcspec, *client, *server, *msg;
+ krb5_timestamp timestamp, now_timestamp;
+ krb5_int32 usec, now_usec;
+
+ argc--; argv++;
+ if (!argc) usage(progname);
+ rcspec = *argv;
+ argc--; argv++;
+ if (!argc) usage(progname);
+ client = *argv;
+ argc--; argv++;
+ if (!argc) usage(progname);
+ server = *argv;
+ argc--; argv++;
+ if (!argc) usage(progname);
+ msg = (**argv) ? *argv : NULL;
+ argc--; argv++;
+ if (!argc) usage(progname);
+ timestamp = (krb5_timestamp) atol(*argv);
+ argc--; argv++;
+ if (!argc) usage(progname);
+ usec = (krb5_int32) atol(*argv);
+ argc--; argv++;
+ if (!argc) usage(progname);
+ now_timestamp = (krb5_timestamp) atol(*argv);
+ argc--; argv++;
+ if (!argc) usage(progname);
+ now_usec = (krb5_int32) atol(*argv);
+
+ store(ctx, rcspec, client, server, msg, timestamp, usec,
+ now_timestamp, now_usec);
+ } else if (strcmp(*argv, "expunge") == 0) {
+ /*
+ * Using the rcache interface, expunge a replay cache.
+ * The now-timestamp argument can be 0 to use the current
+ * time.
+ */
+ char *rcspec;
+ krb5_timestamp now_timestamp;
+ krb5_int32 now_usec;
+
+ argc--; argv++;
+ if (!argc) usage(progname);
+ rcspec = *argv;
+ argc--; argv++;
+ if (!argc) usage(progname);
+ now_timestamp = (krb5_timestamp) atol(*argv);
+ argc--; argv++;
+ if (!argc) usage(progname);
+ now_usec = (krb5_int32) atol(*argv);
+ expunge(ctx, rcspec, now_timestamp, now_usec);
+ } else
+ usage(progname);
+ argc--; argv++;
+ }
+
+ krb5_free_context(ctx);
+
+ return 0;
+}