]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add memory replay cache facility
authorGreg Hudson <ghudson@mit.edu>
Thu, 28 Feb 2019 16:47:26 +0000 (11:47 -0500)
committerGreg Hudson <ghudson@mit.edu>
Tue, 12 Mar 2019 15:29:31 +0000 (11:29 -0400)
Add a k5_memrcache type which can efficiently detect replayed tags
without any persistence or inteprocess sharing.  Also add unit tests.

.gitignore
src/lib/krb5/rcache/Makefile.in
src/lib/krb5/rcache/deps
src/lib/krb5/rcache/memrcache.c [new file with mode: 0644]
src/lib/krb5/rcache/memrcache.h [new file with mode: 0644]
src/lib/krb5/rcache/t_memrcache.c [new file with mode: 0644]

index 9e0546d34ff4d119b9ab39d32a19c7d5cf762f1d..8117674854bd7cae3e3f8f2c139c985a2c8c8355 100644 (file)
@@ -389,6 +389,8 @@ local.properties
 /src/lib/krb5/os/t_std_conf
 /src/lib/krb5/os/t_trace
 
+/src/lib/krb5/rcache/t_memrcache
+
 /src/lib/krb5/unicode/.links
 /src/lib/krb5/unicode/ucdata.[ch]
 /src/lib/krb5/unicode/ucgendat.c
index 5b800c572fc327e985eb4c10789203dc22763c76..e61b65728093db681ef42263ecd5474e06778fd0 100644 (file)
@@ -6,6 +6,7 @@ BUILDTOP=$(REL)..$(S)..$(S)..
 ##DOS##OBJFILE=..\$(OUTPRE)$(PREFIXDIR).lst
 
 STLIBOBJS = \
+       memrcache.o     \
        rc_base.o       \
        rc_dfl.o        \
        rc_io.o         \
@@ -16,6 +17,7 @@ STLIBOBJS = \
        rcfns.o
 
 OBJS=  \
+       $(OUTPRE)memrcache.$(OBJEXT)    \
        $(OUTPRE)rc_base.$(OBJEXT)      \
        $(OUTPRE)rc_dfl.$(OBJEXT)       \
        $(OUTPRE)rc_io.$(OBJEXT)        \
@@ -26,6 +28,7 @@ OBJS= \
        $(OUTPRE)rcfns.$(OBJEXT)
 
 SRCS=  \
+       $(srcdir)/memrcache.c   \
        $(srcdir)/rc_base.c     \
        $(srcdir)/rc_dfl.c      \
        $(srcdir)/rc_io.c       \
@@ -34,6 +37,7 @@ SRCS= \
        $(srcdir)/rc_conv.c     \
        $(srcdir)/ser_rc.c      \
        $(srcdir)/rcfns.c       \
+       $(srcdir)/t_memrcache.c \
        $(srcdir)/t_replay.c
 
 ##DOS##LIBOBJS = $(OBJS)
@@ -41,10 +45,19 @@ SRCS=       \
 all-unix: all-libobjs
 clean-unix:: clean-libobjs
 
+t_memrcache: t_memrcache.o $(KRB5_BASE_DEPLIBS)
+       $(CC_LINK) -o $@ t_memrcache.o $(KRB5_BASE_LIBS)
+
 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)
 
+check-unix: t_memrcache
+       $(RUN_TEST) ./t_memrcache
+
+clean-unix::
+       $(RM) t_memrcache.o t_memrcache
+
 @libobj_frag@
 
index 0fe8808722c06bd549c32428f2bd7cb03e09fdf3..445439a797eadcf46019cc60df45f782932996de 100644 (file)
@@ -1,6 +1,18 @@
 #
 # Generated makefile dependencies follow.
 #
+memrcache.so memrcache.po $(OUTPRE)memrcache.$(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-hashtab.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-queue.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 \
+  memrcache.c memrcache.h
 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 \
@@ -85,6 +97,18 @@ rcfns.so rcfns.po $(OUTPRE)rcfns.$(OBJEXT): $(BUILDTOP)/include/autoconf.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_memrcache.so t_memrcache.po $(OUTPRE)t_memrcache.$(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-hashtab.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-queue.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 \
+  memrcache.c memrcache.h t_memrcache.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 \
diff --git a/src/lib/krb5/rcache/memrcache.c b/src/lib/krb5/rcache/memrcache.c
new file mode 100644 (file)
index 0000000..90f855a
--- /dev/null
@@ -0,0 +1,165 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/memrcache.c - in-memory replay cache implementation */
+/*
+ * Copyright (C) 2019 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "k5-queue.h"
+#include "k5-hashtab.h"
+#include "memrcache.h"
+
+struct entry {
+    K5_TAILQ_ENTRY(entry) links;
+    krb5_timestamp timestamp;
+    krb5_data tag;
+};
+
+K5_LIST_HEAD(entry_list, entry);
+K5_TAILQ_HEAD(entry_queue, entry);
+
+struct k5_memrcache_st {
+    struct k5_hashtab *hash_table;
+    struct entry_queue expiration_queue;
+};
+
+static krb5_error_code
+insert_entry(krb5_context context, k5_memrcache mrc, const krb5_data *tag,
+             krb5_timestamp now)
+{
+    krb5_error_code ret;
+    struct entry *entry = NULL;
+
+    entry = calloc(1, sizeof(*entry));
+    if (entry == NULL)
+        return ENOMEM;
+    entry->timestamp = now;
+
+    ret = krb5int_copy_data_contents(context, tag, &entry->tag);
+    if (ret)
+        goto error;
+
+    ret = k5_hashtab_add(mrc->hash_table, entry->tag.data, entry->tag.length,
+                         entry);
+    if (ret)
+        goto error;
+    K5_TAILQ_INSERT_TAIL(&mrc->expiration_queue, entry, links);
+
+    return 0;
+
+error:
+    if (entry != NULL) {
+        krb5_free_data_contents(context, &entry->tag);
+        free(entry);
+    }
+    return ret;
+}
+
+
+/* Remove entry from its hash bucket and the expiration queue, and free it. */
+static void
+discard_entry(krb5_context context, k5_memrcache mrc, struct entry *entry)
+{
+    k5_hashtab_remove(mrc->hash_table, entry->tag.data, entry->tag.length);
+    K5_TAILQ_REMOVE(&mrc->expiration_queue, entry, links);
+    krb5_free_data_contents(context, &entry->tag);
+    free(entry);
+}
+
+/* Initialize the lookaside cache structures and randomize the hash seed. */
+krb5_error_code
+k5_memrcache_create(krb5_context context, k5_memrcache *mrc_out)
+{
+    krb5_error_code ret;
+    k5_memrcache mrc;
+    uint8_t seed[K5_HASH_SEED_LEN];
+    krb5_data seed_data = make_data(seed, sizeof(seed));
+
+    *mrc_out = NULL;
+
+    ret = krb5_c_random_make_octets(context, &seed_data);
+    if (ret)
+        return ret;
+
+    mrc = calloc(1, sizeof(*mrc));
+    if (mrc == NULL)
+        return ENOMEM;
+    ret = k5_hashtab_create(seed, 64, &mrc->hash_table);
+    if (ret) {
+        free(mrc);
+        return ret;
+    }
+    K5_TAILQ_INIT(&mrc->expiration_queue);
+
+    *mrc_out = mrc;
+    return 0;
+}
+
+krb5_error_code
+k5_memrcache_store(krb5_context context, k5_memrcache mrc,
+                   const krb5_data *tag)
+{
+    krb5_error_code ret;
+    krb5_timestamp now;
+    struct entry *e, *next;
+
+    ret = krb5_timeofday(context, &now);
+    if (ret)
+        return ret;
+
+    /* Check if we already have a matching entry. */
+    e = k5_hashtab_get(mrc->hash_table, tag->data, tag->length);
+    if (e != NULL)
+        return KRB5KRB_AP_ERR_REPEAT;
+
+    /* Discard stale entries. */
+    K5_TAILQ_FOREACH_SAFE(e, &mrc->expiration_queue, links, next) {
+        if (!ts_after(now, ts_incr(e->timestamp, context->clockskew)))
+            break;
+        discard_entry(context, mrc, e);
+    }
+
+    /* Add the new entry. */
+    return insert_entry(context, mrc, tag, now);
+}
+
+/* Free all entries in the lookaside cache. */
+void
+k5_memrcache_free(krb5_context context, k5_memrcache mrc)
+{
+    struct entry *e, *next;
+
+    if (mrc == NULL)
+        return;
+    K5_TAILQ_FOREACH_SAFE(e, &mrc->expiration_queue, links, next) {
+        discard_entry(context, mrc, e);
+    }
+    k5_hashtab_free(mrc->hash_table);
+    free(mrc);
+}
diff --git a/src/lib/krb5/rcache/memrcache.h b/src/lib/krb5/rcache/memrcache.h
new file mode 100644 (file)
index 0000000..02ef730
--- /dev/null
@@ -0,0 +1,46 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/memrcache.h - declarations for in-memory replay cache */
+/*
+ * Copyright (C) 2019 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MEMRCACHE_H
+#define MEMRCACHE_H
+
+typedef struct k5_memrcache_st *k5_memrcache;
+
+krb5_error_code k5_memrcache_create(krb5_context context,
+                                    k5_memrcache *mrc_out);
+
+krb5_error_code k5_memrcache_store(krb5_context context, k5_memrcache mrc,
+                                   const krb5_data *tag);
+
+void k5_memrcache_free(krb5_context context, k5_memrcache mrc);
+
+#endif /* MEMRCACHE_H */
diff --git a/src/lib/krb5/rcache/t_memrcache.c b/src/lib/krb5/rcache/t_memrcache.c
new file mode 100644 (file)
index 0000000..a086042
--- /dev/null
@@ -0,0 +1,81 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/rcache/t_memrcache.c - memory replay cache tests */
+/*
+ * Copyright (C) 2019 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "memrcache.c"
+
+int
+main()
+{
+    krb5_error_code ret;
+    krb5_context context;
+    k5_memrcache mrc;
+    int i;
+    uint8_t tag[4];
+    krb5_data tag_data = make_data(tag, 4);
+    struct entry *e;
+
+    ret = krb5_init_context(&context);
+    assert(ret == 0);
+
+    /* Store a thousand unique tags, then verify that they all appear as
+     * replays. */
+    ret = k5_memrcache_create(context, &mrc);
+    assert(ret == 0);
+    for (i = 0; i < 1000; i++) {
+        store_32_be(i, tag);
+        ret = k5_memrcache_store(context, mrc, &tag_data);
+        assert(ret == 0);
+    }
+    for (i = 0; i < 1000; i++) {
+        store_32_be(i, tag);
+        ret = k5_memrcache_store(context, mrc, &tag_data);
+        assert(ret == KRB5KRB_AP_ERR_REPEAT);
+    }
+    k5_memrcache_free(context, mrc);
+
+    /* Store a thousand unique tags, each spaced out so that previous entries
+     * appear as expired.  Verify that the expiration queue has one entry. */
+    ret = k5_memrcache_create(context, &mrc);
+    assert(ret == 0);
+    context->clockskew = 100;
+    for (i = 1; i < 1000; i++) {
+        krb5_set_debugging_time(context, i * 200, 0);
+        store_32_be(i, tag);
+        ret = k5_memrcache_store(context, mrc, &tag_data);
+        assert(ret == 0);
+    }
+    e = K5_TAILQ_FIRST(&mrc->expiration_queue);
+    assert(e != NULL && K5_TAILQ_NEXT(e, links) == NULL);
+    k5_memrcache_free(context, mrc);
+
+    return 0;
+}