From 529e72785f09c36a9aa34fd7f3fc30fb41a1c92e Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Mon, 12 Jan 2009 18:29:42 +0000 Subject: [PATCH] Add message hash support to the replay interface, using extension 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 --- src/include/k5-int.h | 4 + src/kdc/kdc_preauth.c | 1 + src/lib/krb5/krb/mk_cred.c | 1 + src/lib/krb5/krb/mk_priv.c | 1 + src/lib/krb5/krb/mk_safe.c | 1 + src/lib/krb5/krb/rd_cred.c | 1 + src/lib/krb5/krb/rd_priv.c | 1 + src/lib/krb5/krb/rd_req_dec.c | 8 +- src/lib/krb5/krb/rd_safe.c | 1 + src/lib/krb5/libkrb5.exports | 2 + src/lib/krb5/rcache/Makefile.in | 10 +- src/lib/krb5/rcache/rc_conv.c | 41 ++++++ src/lib/krb5/rcache/rc_dfl.c | 127 ++++++++++++------ src/lib/krb5/rcache/t_replay.c | 227 ++++++++++++++++++++++++++++++++ src/tests/threads/t_rcache.c | 1 + 15 files changed, 384 insertions(+), 43 deletions(-) create mode 100644 src/lib/krb5/rcache/t_replay.c diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 43f130710..9ad55694e 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -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 diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index c059216a1..8ea296940 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -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); diff --git a/src/lib/krb5/krb/mk_cred.c b/src/lib/krb5/krb/mk_cred.c index 8611e1409..a63b07ac6 100644 --- a/src/lib/krb5/krb/mk_cred.c +++ b/src/lib/krb5/krb/mk_cred.c @@ -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))) { diff --git a/src/lib/krb5/krb/mk_priv.c b/src/lib/krb5/krb/mk_priv.c index 08cbee969..2a56bd097 100644 --- a/src/lib/krb5/krb/mk_priv.c +++ b/src/lib/krb5/krb/mk_priv.c @@ -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))) { diff --git a/src/lib/krb5/krb/mk_safe.c b/src/lib/krb5/krb/mk_safe.c index 0517f4275..01abfab96 100644 --- a/src/lib/krb5/krb/mk_safe.c +++ b/src/lib/krb5/krb/mk_safe.c @@ -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))) { diff --git a/src/lib/krb5/krb/rd_cred.c b/src/lib/krb5/krb/rd_cred.c index 3c76506b2..5e159ab80 100644 --- a/src/lib/krb5/krb/rd_cred.c +++ b/src/lib/krb5/krb/rd_cred.c @@ -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))) { diff --git a/src/lib/krb5/krb/rd_priv.c b/src/lib/krb5/krb/rd_priv.c index 618726efe..66b29b0fa 100644 --- a/src/lib/krb5/krb/rd_priv.c +++ b/src/lib/krb5/krb/rd_priv.c @@ -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))) { diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c index bbf7ed6a7..2e64233fd 100644 --- a/src/lib/krb5/krb/rd_req_dec.c +++ b/src/lib/krb5/krb/rd_req_dec.c @@ -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); } diff --git a/src/lib/krb5/krb/rd_safe.c b/src/lib/krb5/krb/rd_safe.c index 98d73733c..d14d9d428 100644 --- a/src/lib/krb5/krb/rd_safe.c +++ b/src/lib/krb5/krb/rd_safe.c @@ -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))) { diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index ad560c071..4a6581fe1 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -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 diff --git a/src/lib/krb5/rcache/Makefile.in b/src/lib/krb5/rcache/Makefile.in index d4c133623..abbcc32de 100644 --- a/src/lib/krb5/rcache/Makefile.in +++ b/src/lib/krb5/rcache/Makefile.in @@ -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@ diff --git a/src/lib/krb5/rcache/rc_conv.c b/src/lib/krb5/rcache/rc_conv.c index 16ed9e7eb..3370f45df 100644 --- a/src/lib/krb5/rcache/rc_conv.c +++ b/src/lib/krb5/rcache/rc_conv.c @@ -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; +} diff --git a/src/lib/krb5/rcache/rc_dfl.c b/src/lib/krb5/rcache/rc_dfl.c index aa0b3a5f9..04a641dc2 100644 --- a/src/lib/krb5/rcache/rc_dfl.c +++ b/src/lib/krb5/rcache/rc_dfl.c @@ -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 index 000000000..4147896ce --- /dev/null +++ b/src/lib/krb5/rcache/t_replay.c @@ -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 +#include +#include +#include + +#include "k5-int.h" + +static void usage(const char *progname) +{ + fprintf(stderr, "%s: Usage:\n", progname); + fprintf(stderr, " %s dump \n", progname); + fprintf(stderr, " %s store " + " \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(×tamp, 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; +} diff --git a/src/tests/threads/t_rcache.c b/src/tests/threads/t_rcache.c index 06f4d3b19..3a654a4b3 100644 --- a/src/tests/threads/t_rcache.c +++ b/src/tests/threads/t_rcache.c @@ -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, -- 2.26.2