Add message hash support to the replay interface, using extension
authorGreg Hudson <ghudson@mit.edu>
Mon, 12 Jan 2009 18:29:42 +0000 (18:29 +0000)
committerGreg Hudson <ghudson@mit.edu>
Mon, 12 Jan 2009 18:29:42 +0000 (18:29 +0000)
records (with an empty client string) to retain compatibility with old
code.  For rd_req, the ciphertext of the authenticator (with no ASN.1
wrapping) is hashed; for other uses of the replay cache, no message
hash is used at this time.

This commit adds a command-line tool for testing the replay cache but
does not add any automated tests.

ticket: 1201

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21723 dc483132-0cff-0310-8789-dd5450dbe970

15 files changed:
src/include/k5-int.h
src/kdc/kdc_preauth.c
src/lib/krb5/krb/mk_cred.c
src/lib/krb5/krb/mk_priv.c
src/lib/krb5/krb/mk_safe.c
src/lib/krb5/krb/rd_cred.c
src/lib/krb5/krb/rd_priv.c
src/lib/krb5/krb/rd_req_dec.c
src/lib/krb5/krb/rd_safe.c
src/lib/krb5/libkrb5.exports
src/lib/krb5/rcache/Makefile.in
src/lib/krb5/rcache/rc_conv.c
src/lib/krb5/rcache/rc_dfl.c
src/lib/krb5/rcache/t_replay.c [new file with mode: 0644]
src/tests/threads/t_rcache.c

index 43f1307109ac4bebc9499b6a03ec53840128537c..9ad55694e3ea089188322093a3e5efad9d0504f6 100644 (file)
@@ -2177,6 +2177,7 @@ typedef struct _krb5_donot_replay {
     krb5_ui_4 hash;
     char *server;                      /* null-terminated */
     char *client;                      /* null-terminated */
+    char *msghash;                     /* null-terminated */
     krb5_int32 cusec;
     krb5_timestamp ctime;
 } krb5_donot_replay;
@@ -2201,6 +2202,9 @@ krb5_error_code krb5_auth_to_rep
        (krb5_context,
                krb5_tkt_authent *,
                krb5_donot_replay *);
+krb5_error_code krb5_rc_hash_message
+       (krb5_context context,
+               const krb5_data *message, char **out);
 
 
 krb5_error_code KRB5_CALLCONV krb5_rc_initialize
index c059216a1199c0508169e6e41fa736025d7fa0bb..8ea296940e84579dcad99d1700c08ed32dc779e0 100644 (file)
@@ -2396,6 +2396,7 @@ verify_sam_response(krb5_context context, krb5_db_entry *client,
        /* Now check the replay cache. */
        rep.client = princ_psr;
        rep.server = "SAM/rc";  /* Should not match any principal name. */
+       rep.msghash = NULL;
        rep.ctime = psr->stime;
        rep.cusec = psr->susec;
        retval = krb5_rc_store(kdc_context, kdc_rcache, &rep);
index 8611e1409701dc3379c66ffa57d970dc001f7880..a63b07ac69420952c4b6b2961885405682ecf350 100644 (file)
@@ -258,6 +258,7 @@ krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
             goto error;
 
         replay.server = "";             /* XXX */
+        replay.msghash = NULL;
         replay.cusec = replaydata.usec;
         replay.ctime = replaydata.timestamp;
         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
index 08cbee969a05f5d73f030c3bc15bba09dda749c6..2a56bd0971b455fc3f75675131be1e005e4557fc 100644 (file)
@@ -214,6 +214,7 @@ krb5_mk_priv(krb5_context context, krb5_auth_context auth_context,
        }
 
        replay.server = "";             /* XXX */
+       replay.msghash = NULL;
        replay.cusec = replaydata.usec;
        replay.ctime = replaydata.timestamp;
        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
index 0517f42750baa43d72462a7afd7c117303e3d112..01abfab96187d89ae45cc27cba4f1ac5d5df4560 100644 (file)
@@ -239,6 +239,7 @@ krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
        }
 
        replay.server = "";             /* XXX */
+       replay.msghash = NULL;
        replay.cusec = replaydata.usec;
        replay.ctime = replaydata.timestamp;
        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
index 3c76506b2f099dc1d2724dd7663080e271d4da8d..5e159ab8025409eda7056e9957dc604f59fbc301 100644 (file)
@@ -210,6 +210,7 @@ krb5_rd_cred(krb5_context context, krb5_auth_context auth_context,
             goto error;
 
         replay.server = "";             /* XXX */
+        replay.msghash = NULL;
         replay.cusec = replaydata.usec;
         replay.ctime = replaydata.timestamp;
         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
index 618726efe281700e91f30d2a361f819179417bbd..66b29b0fa3b908457037616d87878b7ea6ffbfae 100644 (file)
@@ -235,6 +235,7 @@ krb5_rd_priv(krb5_context context, krb5_auth_context auth_context,
            goto error;
 
        replay.server = "";             /* XXX */
+       replay.msghash = NULL;
        replay.cusec = replaydata.usec;
        replay.ctime = replaydata.timestamp;
        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
index bbf7ed6a724ba28e5f9cfd57028a649fc6a07a63..2e64233fd7902917cdb66ff52b2fc0ffd27ddcbe 100644 (file)
@@ -349,7 +349,13 @@ krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
        tktauthent.ticket = req->ticket;        
        tktauthent.authenticator = (*auth_context)->authentp;
        if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) {
-           retval = krb5_rc_store(context, (*auth_context)->rcache, &rep);
+           retval = krb5_rc_hash_message(context,
+                                         &req->authenticator.ciphertext,
+                                         &rep.msghash);
+           if (!retval) {
+               retval = krb5_rc_store(context, (*auth_context)->rcache, &rep);
+               krb5_xfree(rep.msghash);
+           }
            krb5_xfree(rep.server);
            krb5_xfree(rep.client);
        }
index 98d73733c3794afa71fced1bf2f6d567b356bfee..d14d9d4287abb363a28534d2e1d4da742aba1d78 100644 (file)
@@ -241,6 +241,7 @@ krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
            goto error;
 
        replay.server = "";             /* XXX */
+       replay.msghash = NULL;
        replay.cusec = replaydata.usec;
        replay.ctime = replaydata.timestamp;
        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
index ad560c071bf883dcb543e29aa9d56ee4738618a3..4a6581fe12d6ba471eebfc3c2e43e5bb072a171a 100644 (file)
@@ -400,6 +400,7 @@ krb5_rc_free_entry
 krb5_rc_get_lifespan
 krb5_rc_get_name
 krb5_rc_get_type
+krb5_rc_hash_message
 krb5_rc_initialize
 krb5_rc_io_close
 krb5_rc_io_creat
@@ -413,6 +414,7 @@ krb5_rc_io_sync
 krb5_rc_io_unmark
 krb5_rc_io_write
 krb5_rc_recover
+krb5_rc_recover_or_initialize
 krb5_rc_register_type
 krb5_rc_resolve
 krb5_rc_resolve_full
index d4c13362346182152d605b187b32834200213bb3..abbcc32de67c7e09d916f7d95834ef9cf89790d0 100644 (file)
@@ -2,6 +2,8 @@ thisconfigdir=../../..
 myfulldir=lib/krb5/rcache
 mydir=lib/krb5/rcache
 BUILDTOP=$(REL)..$(S)..$(S)..
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
 DEFS=
 
 ##DOS##BUILDTOP = ..\..\..
@@ -36,12 +38,18 @@ SRCS=       \
        $(srcdir)/rc_none.c     \
        $(srcdir)/rc_conv.c     \
        $(srcdir)/ser_rc.c      \
-       $(srcdir)/rcfns.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@
 
index 16ed9e7eb8bf99ad386126b2b9782d7e97d053da..3370f45df0d24827b7bf10d2fa33dba5f3c65945 100644 (file)
@@ -36,3 +36,44 @@ krb5_auth_to_rep(krb5_context context, krb5_tkt_authent *auth, krb5_donot_replay
     }
     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 it is necessary for the
+ * function to be consistent across implementations.  We do an unkeyed
+ * MD5 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;
+    krb5_checksum cksum;
+    char *hash, *ptr;
+    unsigned int i;
+
+    *out = NULL;
+
+    /* Calculate the binary checksum. */
+    retval = krb5_c_make_checksum(context, CKSUMTYPE_RSA_MD5, 0, 0,
+                                  message, &cksum);
+    if (retval)
+        return retval;
+
+    /* Convert the checksum into printable form. */
+    hash = malloc(cksum.length * 2 + 1);
+    if (!hash) {
+        krb5_free_checksum_contents(context, &cksum);
+        return KRB5_RC_MALLOC;
+    }
+    ptr = hash;
+    for (i = 0, ptr = hash; i < cksum.length; i++, ptr += 2)
+        snprintf(ptr, 3, "%02X", cksum.contents[i]);
+    *ptr = '\0';
+    *out = hash;
+    krb5_free_checksum_contents(context, &cksum);
+    return 0;
+}
index aa0b3a5f98402f9d98e8822d9835d45c5110082e..04a641dc294bdddcb109d2a309b6280c0100d425 100644 (file)
@@ -85,8 +85,12 @@ 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 */
-        return CMP_REPLAY;
+        (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;
 }
 
@@ -157,17 +161,22 @@ rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep,
     ta->na = t->a; t->a = ta;
     ta->nh = t->h[rephash]; t->h[rephash] = ta;
     ta->rep = *rep;
-    if (!(ta->rep.client = strdup(rep->client))) {
-        FREE(ta);
-        return CMP_MALLOC;
-    }
-    if (!(ta->rep.server = strdup(rep->server))) {
-        FREE(ta->rep.client);
-        FREE(ta);
-        return CMP_MALLOC;
-    }
-
+    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;
     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);
+    return CMP_MALLOC;
 }
 
 char * KRB5_CALLCONV
@@ -241,6 +250,8 @@ krb5_rc_dfl_close_no_free(krb5_context context, krb5_rcache id)
         t->a = q->na;
         FREE(q->rep.client);
         FREE(q->rep.server);
+        if (q->rep.msghash)
+            FREE(q->rep.msghash);
         FREE(q);
     }
 #ifndef NOIOSTUFF
@@ -328,11 +339,13 @@ krb5_rc_free_entry(krb5_context context, krb5_donot_replay **rep)
     {
         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);
     }
 }
@@ -345,7 +358,7 @@ krb5_rc_io_fetch(krb5_context context, struct dfl_data *t,
     unsigned int len;
     krb5_error_code retval;
 
-    rep->client = rep->server = 0;
+    rep->client = rep->server = rep->msghash = NULL;
 
     retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2,
                              sizeof(len2));
@@ -423,6 +436,7 @@ krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id)
     long max_size;
     int expired_entries = 0;
     krb5_int32 now;
+    char *msghash = NULL;
 
     if ((retval = krb5_rc_io_open(context, &t->d, t->name))) {
         return retval;
@@ -443,8 +457,7 @@ krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id)
         retval = KRB5_RC_MALLOC;
         goto io_fail;
     }
-    rep->client = NULL;
-    rep->server = NULL;
+    rep->client = rep->server = rep->msghash = NULL;
 
     if (krb5_timeofday(context, &now))
         now = 0;
@@ -463,21 +476,38 @@ krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id)
         else if (retval != 0)
             goto io_fail;
 
-
-        if (alive(now, rep, t->lifespan) != CMP_EXPIRED) {
-            if (rc_store(context, id, rep, now) == CMP_MALLOC) {
-                retval = KRB5_RC_MALLOC; goto io_fail;
+        if (!*rep->client) {
+            /* An empty client field indicates an extension record. */
+            if (strncmp(rep->server, "HASH:", 5) == 0) {
+                msghash = strdup(rep->server + 5);
+                if (msghash == NULL) {
+                    retval = KRB5_RC_MALLOC;
+                    goto io_fail;
+                }
             }
         } else {
-            expired_entries++;
+            /* This is a normal record. */
+            if (msghash) {
+                /* Use the hash from the prior extension record. */
+                rep->msghash = msghash;
+                msghash = NULL;
+            }
+            if (alive(now, rep, t->lifespan) != CMP_EXPIRED) {
+                if (rc_store(context, id, rep, now) == CMP_MALLOC) {
+                    retval = KRB5_RC_MALLOC; goto io_fail;
+                }
+            } else {
+                expired_entries++;
+            }
         }
         /*
-         *  free fields allocated by rc_io_fetch
+         *  free fields allocated by rc_io_fetch (or by us)
          */
         FREE(rep->server);
         FREE(rep->client);
-        rep->server = 0;
-        rep->client = 0;
+        if (rep->msghash)
+            FREE(rep->msghash);
+        rep->client = rep->server = rep->msghash = NULL;
     }
     retval = 0;
     krb5_rc_io_unmark(context, &t->d);
@@ -487,6 +517,8 @@ krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id)
      */
 io_fail:
     krb5_rc_free_entry(context, &rep);
+    if (msghash)
+        FREE(msghash);
     if (retval)
         krb5_rc_io_close(context, &t->d);
     else if (expired_entries > EXCESSREPS)
@@ -529,27 +561,38 @@ static krb5_error_code
 krb5_rc_io_store(krb5_context context, struct dfl_data *t,
                  krb5_donot_replay *rep)
 {
-    unsigned int clientlen, serverlen, len;
-    char *buf, *ptr;
+    unsigned int clientlen, serverlen;
     krb5_error_code ret;
-
+    struct k5buf buf;
+    char *ptr;
+
+    krb5int_buf_init_dynamic(&buf);
+    if (rep->msghash) {
+        clientlen = 1;
+        serverlen = strlen(rep->msghash) + 6;
+        krb5int_buf_add_len(&buf, (char *) &clientlen, sizeof(clientlen));
+        krb5int_buf_add_len(&buf, "", 1);
+        krb5int_buf_add_len(&buf, (char *) &serverlen, sizeof(serverlen));
+        krb5int_buf_add_fmt(&buf, "HASH:%s", rep->msghash);
+        krb5int_buf_add_len(&buf, "", 1);
+        krb5int_buf_add_len(&buf, (char *) &rep->cusec, sizeof(rep->cusec));
+        krb5int_buf_add_len(&buf, (char *) &rep->ctime, sizeof(rep->ctime));
+    }
     clientlen = strlen(rep->client) + 1;
     serverlen = strlen(rep->server) + 1;
-    len = sizeof(clientlen) + clientlen + sizeof(serverlen) + serverlen +
-        sizeof(rep->cusec) + sizeof(rep->ctime);
-    buf = malloc(len);
-    if (buf == 0)
+    krb5int_buf_add_len(&buf, (char *) &clientlen, sizeof(clientlen));
+    krb5int_buf_add_len(&buf, rep->client, clientlen);
+    krb5int_buf_add_len(&buf, (char *) &serverlen, sizeof(serverlen));
+    krb5int_buf_add_len(&buf, rep->server, serverlen);
+    krb5int_buf_add_len(&buf, (char *) &rep->cusec, sizeof(rep->cusec));
+    krb5int_buf_add_len(&buf, (char *) &rep->ctime, sizeof(rep->ctime));
+
+    ptr = krb5int_buf_data(&buf);
+    if (ptr == NULL)
         return KRB5_RC_MALLOC;
-    ptr = buf;
-    memcpy(ptr, &clientlen, sizeof(clientlen)); ptr += sizeof(clientlen);
-    memcpy(ptr, rep->client, clientlen); ptr += clientlen;
-    memcpy(ptr, &serverlen, sizeof(serverlen)); ptr += sizeof(serverlen);
-    memcpy(ptr, rep->server, serverlen); ptr += serverlen;
-    memcpy(ptr, &rep->cusec, sizeof(rep->cusec)); ptr += sizeof(rep->cusec);
-    memcpy(ptr, &rep->ctime, sizeof(rep->ctime)); ptr += sizeof(rep->ctime);
-
-    ret = krb5_rc_io_write(context, &t->d, buf, len);
-    free(buf);
+
+    ret = krb5_rc_io_write(context, &t->d, ptr, krb5int_buf_len(&buf));
+    krb5int_free_buf(&buf);
     return ret;
 }
 
@@ -628,6 +671,8 @@ krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id)
         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? */
         }
diff --git a/src/lib/krb5/rcache/t_replay.c b/src/lib/krb5/rcache/t_replay.c
new file mode 100644 (file)
index 0000000..4147896
--- /dev/null
@@ -0,0 +1,227 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * test/threads/t_replay.c
+ *
+ * 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.
+ * 
+ * t_replay.c: Command-line interfaces to aid testing of replay cache
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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);
+    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;
+
+    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) {
+        char *str;
+        krb5_int32 usec;
+        krb5_timestamp timestamp;
+
+        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;
+
+    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) {
+        krb5_data d;
+
+        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);
+}
+
+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
+            usage(progname);
+        argc--; argv++;
+    }
+
+    krb5_free_context(ctx);
+
+    return 0;
+}
index 06f4d3b194a89f16039b1cfe7a88c14768c8e096..3a654a4b3251fdfff884e288edf4f66765b65913 100644 (file)
@@ -62,6 +62,7 @@ static void try_one (struct tinfo *t)
             buf);
     r.server = buf;
     r.client = (t->my_cusec & 7) + "abcdefgh@ATHENA.MIT.EDU";
+    r.msghash = NULL;
     if (t->now != t->my_ctime) {
        if (t->my_ctime != 0) {
            snprintf(buf2, sizeof(buf2), "%3d: %ld %5d\n", t->idx,