krb5 commit: Use memory replay cache for DO_TIME auth contexts
Greg Hudson
ghudson at mit.edu
Tue Mar 12 14:13:32 EDT 2019
https://github.com/krb5/krb5/commit/0b1c78fcbdef574fb6835f0cd0a89388f4098cb5
commit 0b1c78fcbdef574fb6835f0cd0a89388f4098cb5
Author: Greg Hudson <ghudson at mit.edu>
Date: Thu Feb 28 21:28:43 2019 -0500
Use memory replay cache for DO_TIME auth contexts
Instead of requiring the caller to set up a persistent replay cache
for KRB-PRIV/KRB-SAFE/KRB-CRED messages produced in DO_TIME auth
contexts, use an in-memory replay cache.
Update the API documentation for the affected functions and correct
some inaccuracies.
ticket: 8785 (new)
.gitignore | 1 +
src/include/k5-int.h | 6 ++
src/include/krb5/krb5.hin | 138 ++++++++++++++++++++-------------
src/lib/krb5/krb/auth_con.c | 1 +
src/lib/krb5/krb/auth_con.h | 3 +
src/lib/krb5/krb/int-proto.h | 11 ++-
src/lib/krb5/krb/mk_cred.c | 28 +++++--
src/lib/krb5/krb/mk_priv.c | 21 +++--
src/lib/krb5/krb/mk_safe.c | 23 ++++--
src/lib/krb5/krb/privsafe.c | 40 +++++-----
src/lib/krb5/krb/rd_cred.c | 17 ++---
src/lib/krb5/krb/rd_priv.c | 22 +++--
src/lib/krb5/krb/rd_safe.c | 31 +++++---
src/lib/krb5/rcache/rc_conv.c | 20 +++++
src/tests/Makefile.in | 14 ++-
src/tests/replay.c | 172 +++++++++++++++++++++++++++++++++++++++++
src/tests/t_replay.py | 6 ++
17 files changed, 414 insertions(+), 140 deletions(-)
diff --git a/.gitignore b/.gitignore
index 8117674..f39dd6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -438,6 +438,7 @@ local.properties
/src/tests/localauth
/src/tests/plugorder
/src/tests/rdreq
+/src/tests/replay
/src/tests/responder
/src/tests/s2p
/src/tests/s4u2proxy
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 7d3c129..71dce73 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -1989,6 +1989,12 @@ krb5_error_code krb5_auth_to_rep(krb5_context, krb5_tkt_authent *,
krb5_error_code krb5_rc_hash_message(krb5_context context,
const krb5_data *message, char **out);
+/* Set *tag_out to the integrity tag of *enc. (Does not allocate memory;
+ * returned buffer is a subrange of *ctext.) */
+krb5_error_code
+k5_rc_tag_from_ciphertext(krb5_context context, const krb5_enc_data *enc,
+ krb5_data *tag_out);
+
krb5_error_code KRB5_CALLCONV
krb5_rc_initialize(krb5_context, krb5_rcache, krb5_deltat);
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index a67eec1..eb31607 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -3316,25 +3316,24 @@ krb5_rd_error(krb5_context context, const krb5_data *enc_errbuf,
* This function parses a @c KRB-SAFE message, verifies its integrity, and
* stores its data into @a userdata_out.
*
- * @note The @a rdata_out argument is required if #KRB5_AUTH_CONTEXT_RET_TIME
- * or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.
+ * @note The @a rdata_out argument is required if the
+ * #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set
+ * in @a auth_context.
*
- * @note @a auth_context must have a remote address set. This address will be
- * used to verify the sender address in the KRB-SAFE message. If @a
- * auth_context has a local address set, it will be used to verify the receiver
- * address in the KRB-SAFE message if the message contains one. Both addresses
- * must use type @c ADDRTYPE_ADDRPORT.
+ * If @a auth_context has a remote address set, the address will be used to
+ * verify the sender address in the KRB-SAFE message. If @a auth_context has a
+ * local address set, it will be used to verify the receiver address in the
+ * KRB-SAFE message if the message contains one.
*
* If the #KRB5_AUTH_CONTEXT_DO_SEQUENCE flag is set in @a auth_context, the
* sequence number of the KRB-SAFE message is checked against the remote
* sequence number field of @a auth_context. Otherwise, the sequence number is
* not used.
*
- * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context,
- * then two additional checks are performed:
- * @li The timestamp in the message must be within the permitted clock skew
- * (which is usually five minutes).
- * @li The message must not be a replayed message field in @a auth_context.
+ * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context, then the
+ * timestamp in the message is verified to be within the permitted clock skew
+ * of the current time, and the message is checked against an in-memory replay
+ * cache to detect reflections or replays.
*
* Use krb5_free_data_contents() to free @a userdata_out when it is no longer
* needed.
@@ -3358,25 +3357,24 @@ krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
* This function parses a @c KRB-PRIV message, verifies its integrity, and
* stores its unencrypted data into @a userdata_out.
*
- * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
- * flag is set in @a auth_context, @a rdata_out is required.
+ * @note The @a rdata_out argument is required if the
+ * #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set
+ * in @a auth_context.
*
- * @note @a auth_context must have a remote address set. This address will be
- * used to verify the sender address in the KRB-PRIV message. If @a
- * auth_context has a local address set, it will be used to verify the receiver
- * address in the KRB-PRIV message if the message contains one. Both addresses
- * must use type @c ADDRTYPE_ADDRPORT.
+ * If @a auth_context has a remote address set, the address will be used to
+ * verify the sender address in the KRB-PRIV message. If @a auth_context has a
+ * local address set, it will be used to verify the receiver address in the
+ * KRB-PRIV message if the message contains one.
*
* If the #KRB5_AUTH_CONTEXT_DO_SEQUENCE flag is set in @a auth_context, the
- * sequence number of the KRB-SAFE message is checked against the remote
+ * sequence number of the KRB-PRIV message is checked against the remote
* sequence number field of @a auth_context. Otherwise, the sequence number is
* not used.
*
- * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context,
- * then two additional checks are performed:
- * @li The timestamp in the message must be within the permitted clock skew
- * (which is usually five minutes).
- * @li The message must not be a replayed message field in @a auth_context.
+ * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context, then the
+ * timestamp in the message is verified to be within the permitted clock skew
+ * of the current time, and the message is checked against an in-memory replay
+ * cache to detect reflections or replays.
*
* Use krb5_free_data_contents() to free @a userdata_out when it is no longer
* needed.
@@ -5263,18 +5261,22 @@ krb5_kt_read_service_key(krb5_context context, krb5_pointer keyprocarg,
* optional; if specified, it will be used to form the receiver address used in
* the message.
*
- * If #KRB5_AUTH_CONTEXT_DO_TIME flag is set in the @a auth_context, an entry
- * describing the message is entered in the replay cache @a
- * auth_context->rcache which enables the caller to detect if this message is
- * reflected by an attacker. If #KRB5_AUTH_CONTEXT_DO_TIME is not set, the
- * replay cache is not used.
+ * @note The @a rdata_out argument is required if the
+ * #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set
+ * in @a auth_context.
*
- * If either #KRB5_AUTH_CONTEXT_DO_SEQUENCE or
- * #KRB5_AUTH_CONTEXT_RET_SEQUENCE is set, the @a auth_context local sequence
- * number will be placed in @a rdata_out as its sequence number.
+ * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context, a
+ * timestamp is included in the KRB-SAFE message, and an entry for the message
+ * is entered in an in-memory replay cache to detect if the message is
+ * reflected by an attacker. If #KRB5_AUTH_CONTEXT_DO_TIME is not set, no
+ * replay cache is used. If #KRB5_AUTH_CONTEXT_RET_TIME is set in @a
+ * auth_context, a timestamp is included in the KRB-SAFE message and is stored
+ * in @a rdata_out.
*
- * @note The @a rdata_out argument is required if #KRB5_AUTH_CONTEXT_RET_TIME
- * or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.
+ * If either #KRB5_AUTH_CONTEXT_DO_SEQUENCE or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * is set, the @a auth_context local sequence number is included in the
+ * KRB-SAFE message and then incremented. If #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * is set, the sequence number used is stored in @a rdata_out.
*
* Use krb5_free_data_contents() to free @a der_out when it is no longer
* needed.
@@ -5293,30 +5295,35 @@ krb5_mk_safe(krb5_context context, krb5_auth_context auth_context,
* @param [in] auth_context Authentication context
* @param [in] userdata User data for @c KRB-PRIV message
* @param [out] der_out Formatted @c KRB-PRIV message
- * @param [out] rdata_out Replay cache handle (NULL if not needed)
+ * @param [out] rdata_out Replay data (NULL if not needed)
*
* This function is similar to krb5_mk_safe(), but the message is encrypted and
* integrity-protected, not just integrity-protected.
*
* The local address in @a auth_context must be set, and is used to form the
- * sender address used in the KRB-SAFE message. The remote address is
+ * sender address used in the KRB-PRIV message. The remote address is
* optional; if specified, it will be used to form the receiver address used in
* the message.
*
- * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or
- * #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in @a auth_context, the @a
- * rdata_out is required.
+ * @note The @a rdata_out argument is required if the
+ * #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set
+ * in @a auth_context.
*
- * @note The flags from @a auth_context specify whether sequence numbers or
- * timestamps will be used to identify the message. Valid values are:
+ * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context, a
+ * timestamp is included in the KRB-PRIV message, and an entry for the message
+ * is entered in an in-memory replay cache to detect if the message is
+ * reflected by an attacker. If #KRB5_AUTH_CONTEXT_DO_TIME is not set, no
+ * replay cache is used. If #KRB5_AUTH_CONTEXT_RET_TIME is set in @a
+ * auth_context, a timestamp is included in the KRB-PRIV message and is stored
+ * in @a rdata_out.
*
- * @li #KRB5_AUTH_CONTEXT_DO_TIME - Use timestamps in @a outdata
- * @li #KRB5_AUTH_CONTEXT_RET_TIME - Copy timestamp to @a outdata.
- * @li #KRB5_AUTH_CONTEXT_DO_SEQUENCE - Use local sequence numbers from
- * @a auth_context in replay cache.
- * @li #KRB5_AUTH_CONTEXT_RET_SEQUENCE - Use local sequence numbers from
- * @a auth_context as a sequence number
- * in the encrypted message @a der_out.
+ * If either #KRB5_AUTH_CONTEXT_DO_SEQUENCE or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * is set, the @a auth_context local sequence number is included in the
+ * KRB-PRIV message and then incremented. If #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * is set, the sequence number used is stored in @a rdata_out.
+ *
+ * Use krb5_free_data_contents() to free @a der_out when it is no longer
+ * needed.
*
* @retval 0 Success; otherwise - Kerberos error codes
*/
@@ -5448,11 +5455,33 @@ krb5_recvauth_version(krb5_context context,
* This function takes an array of credentials @a creds and formats
* a @c KRB-CRED message @a der_out to pass to krb5_rd_cred().
*
- * @note If the #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
- * flag is set in @a auth_context, @a rdata_out is required.
+ * The local and remote addresses in @a auth_context are optional; if either is
+ * specified, they are used to form the sender and receiver addresses in the
+ * KRB-CRED message.
+ *
+ * @note The @a rdata_out argument is required if the
+ * #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set
+ * in @a auth_context.
+ *
+ * If the #KRB5_AUTH_CONTEXT_DO_TIME flag is set in @a auth_context, an entry
+ * for the message is entered in an in-memory replay cache to detect if the
+ * message is reflected by an attacker. If #KRB5_AUTH_CONTEXT_DO_TIME is not
+ * set, no replay cache is used. If #KRB5_AUTH_CONTEXT_RET_TIME is set in @a
+ * auth_context, the timestamp used for the KRB-CRED message is stored in @a
+ * rdata_out.
+ *
+ * If either #KRB5_AUTH_CONTEXT_DO_SEQUENCE or #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * is set, the @a auth_context local sequence number is included in the
+ * KRB-CRED message and then incremented. If #KRB5_AUTH_CONTEXT_RET_SEQUENCE
+ * is set, the sequence number used is stored in @a rdata_out.
+ *
+ * Use krb5_free_data_contents() to free @a der_out when it is no longer
+ * needed.
*
* The message will be encrypted using the send subkey of @a auth_context if it
- * is present, or the session key otherwise.
+ * is present, or the session key otherwise. If neither key is present, the
+ * credentials will not be encrypted, and the message should only be sent over
+ * a secure channel. No replay cache entry is used in this case.
*
* @retval
* 0 Success
@@ -5503,8 +5532,9 @@ krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
* @param [out] creds_out Null-terminated array of forwarded credentials
* @param [out] rdata_out Replay data (NULL if not needed)
*
- * @note The @a rdata_out argument is required if #KRB5_AUTH_CONTEXT_RET_TIME
- * or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set in the @a auth_context.`
+ * @note The @a rdata_out argument is required if the
+ * #KRB5_AUTH_CONTEXT_RET_TIME or #KRB5_AUTH_CONTEXT_RET_SEQUENCE flag is set
+ * in @a auth_context.`
*
* @a creddata will be decrypted using the receiving subkey if it is present in
* @a auth_context, or the session key if the receiving subkey is not present
diff --git a/src/lib/krb5/krb/auth_con.c b/src/lib/krb5/krb/auth_con.c
index c86a4af..a8a97eb 100644
--- a/src/lib/krb5/krb/auth_con.c
+++ b/src/lib/krb5/krb/auth_con.c
@@ -78,6 +78,7 @@ krb5_auth_con_free(krb5_context context, krb5_auth_context auth_context)
if (auth_context->ad_context)
krb5_authdata_context_free(context, auth_context->ad_context);
free(auth_context);
+ k5_memrcache_free(context, auth_context->memrcache);
return 0;
}
diff --git a/src/lib/krb5/krb/auth_con.h b/src/lib/krb5/krb/auth_con.h
index 821b41e..a010ae4 100644
--- a/src/lib/krb5/krb/auth_con.h
+++ b/src/lib/krb5/krb/auth_con.h
@@ -3,6 +3,8 @@
#ifndef KRB5_AUTH_CONTEXT
#define KRB5_AUTH_CONTEXT
+#include "../rcache/memrcache.h"
+
struct _krb5_auth_context {
krb5_magic magic;
krb5_address * remote_addr;
@@ -21,6 +23,7 @@ struct _krb5_auth_context {
krb5_cksumtype safe_cksumtype; /* mk_safe, ... */
krb5_data cstate; /* mk_priv, rd_priv only */
krb5_rcache rcache;
+ k5_memrcache memrcache;
krb5_enctype * permitted_etypes; /* rd_req */
krb5_mk_req_checksum_func checksum_func;
void *checksum_func_data;
diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
index 3e80241..d45adab 100644
--- a/src/lib/krb5/krb/int-proto.h
+++ b/src/lib/krb5/krb/int-proto.h
@@ -159,13 +159,16 @@ k5_privsafe_gen_addrs(krb5_context context, krb5_auth_context authcon,
krb5_address *lstorage, krb5_address *rstorage,
krb5_address **local_out, krb5_address **remote_out);
-/* If the DO_TIME flag is set in authcon, store a replay record. If check_time
- * is true, also check that the timestamp is within clock skew. */
+/*
+ * If the DO_TIME flag is set in authcon, store a replay record in a memory
+ * replay cache (initializing one if necessary). Either enc or cksum must be
+ * non-null. If rdata is not null, also check that its timestamp is within
+ * clock skew.
+ */
krb5_error_code
k5_privsafe_check_replay(krb5_context context, krb5_auth_context authcon,
- krb5_address *addr, const char *uniq,
const krb5_replay_data *rdata,
- krb5_boolean check_time);
+ const krb5_enc_data *enc, const krb5_checksum *cksum);
krb5_boolean
k5_privsafe_check_seqnum(krb5_context ctx, krb5_auth_context ac,
diff --git a/src/lib/krb5/krb/mk_cred.c b/src/lib/krb5/krb/mk_cred.c
index 003a0fd..62224f1 100644
--- a/src/lib/krb5/krb/mk_cred.c
+++ b/src/lib/krb5/krb/mk_cred.c
@@ -66,14 +66,16 @@ encrypt_credencpart(krb5_context context, krb5_cred_enc_part *encpart,
/*
* Marshal a KRB-CRED message into der_out, encrypted with key (or unencrypted
- * if key is NULL). Use the timestamp and sequence number from rdata and the
- * addresses from local_addr and remote_addr (either of which may be NULL).
- * der_out should be freed by the caller when finished.
+ * if key is NULL). Store the ciphertext in enc_out. Use the timestamp and
+ * sequence number from rdata and the addresses from local_addr and remote_addr
+ * (either of which may be NULL). der_out and enc_out should be freed by the
+ * caller when finished.
*/
static krb5_error_code
create_krbcred(krb5_context context, krb5_creds **creds, krb5_key key,
const krb5_replay_data *rdata, krb5_address *local_addr,
- krb5_address *remote_addr, krb5_data **der_out)
+ krb5_address *remote_addr, krb5_data **der_out,
+ krb5_enc_data *enc_out)
{
krb5_error_code ret;
krb5_cred_enc_part credenc;
@@ -84,6 +86,7 @@ create_krbcred(krb5_context context, krb5_creds **creds, krb5_key key,
size_t i, ncreds;
*der_out = NULL;
+ memset(enc_out, 0, sizeof(*enc_out));
memset(&enc, 0, sizeof(enc));
for (ncreds = 0; creds[ncreds] != NULL; ncreds++);
@@ -137,6 +140,9 @@ create_krbcred(krb5_context context, krb5_creds **creds, krb5_key key,
if (ret)
goto cleanup;
+ *enc_out = enc;
+ memset(&enc, 0, sizeof(enc));
+
cleanup:
krb5_free_tickets(context, tickets);
krb5_free_data_contents(context, &enc.ciphertext);
@@ -154,9 +160,11 @@ krb5_mk_ncred(krb5_context context, krb5_auth_context authcon,
krb5_key key;
krb5_replay_data rdata;
krb5_data *der_krbcred = NULL;
+ krb5_enc_data enc;
krb5_address *local_addr, *remote_addr, lstorage, rstorage;
*der_out = NULL;
+ memset(&enc, 0, sizeof(enc));
memset(&lstorage, 0, sizeof(lstorage));
memset(&rstorage, 0, sizeof(rstorage));
@@ -180,14 +188,15 @@ krb5_mk_ncred(krb5_context context, krb5_auth_context authcon,
key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
ret = create_krbcred(context, creds, key, &rdata, local_addr, remote_addr,
- &der_krbcred);
+ &der_krbcred, &enc);
if (ret)
goto cleanup;
- ret = k5_privsafe_check_replay(context, authcon, authcon->local_addr,
- "_forw", &rdata, FALSE);
- if (ret)
- goto cleanup;
+ if (key != NULL) {
+ ret = k5_privsafe_check_replay(context, authcon, NULL, &enc, NULL);
+ if (ret)
+ goto cleanup;
+ }
*der_out = der_krbcred;
der_krbcred = NULL;
@@ -196,6 +205,7 @@ krb5_mk_ncred(krb5_context context, krb5_auth_context authcon,
authcon->local_seq_number++;
cleanup:
+ krb5_free_data_contents(context, &enc.ciphertext);
free(lstorage.contents);
free(rstorage.contents);
if (der_krbcred != NULL) {
diff --git a/src/lib/krb5/krb/mk_priv.c b/src/lib/krb5/krb/mk_priv.c
index d66ab8a..d023169 100644
--- a/src/lib/krb5/krb/mk_priv.c
+++ b/src/lib/krb5/krb/mk_priv.c
@@ -35,16 +35,16 @@
#include "auth_con.h"
/*
- * Marshal a KRB-PRIV message into der_out, encrypted with key. Use the
- * timestamp and sequence number from rdata and the addresses from local_addr
- * and remote_addr (the second of which may be NULL). der_out should be freed
- * by the caller when finished.
+ * Marshal a KRB-PRIV message into der_out, encrypted with key. Store the
+ * ciphertext in enc_out. Use the timestamp and sequence number from rdata and
+ * the addresses from local_addr and remote_addr (the second of which may be
+ * NULL). der_out and enc_out should be freed by the caller when finished.
*/
static krb5_error_code
create_krbpriv(krb5_context context, const krb5_data *userdata,
krb5_key key, const krb5_replay_data *rdata,
krb5_address *local_addr, krb5_address *remote_addr,
- krb5_data *cstate, krb5_data *der_out)
+ krb5_data *cstate, krb5_data *der_out, krb5_enc_data *enc_out)
{
krb5_enctype enctype = krb5_k_key_enctype(context, key);
krb5_error_code ret;
@@ -91,6 +91,9 @@ create_krbpriv(krb5_context context, const krb5_data *userdata,
*der_out = *der_krbpriv;
free(der_krbpriv);
+ *enc_out = privmsg.enc_part;
+ memset(&privmsg.enc_part, 0, sizeof(privmsg.enc_part));
+
cleanup:
zapfree(privmsg.enc_part.ciphertext.data,
privmsg.enc_part.ciphertext.length);
@@ -111,9 +114,11 @@ krb5_mk_priv(krb5_context context, krb5_auth_context authcon,
krb5_key key;
krb5_replay_data rdata;
krb5_data der_krbpriv = empty_data();
+ krb5_enc_data enc;
krb5_address *local_addr, *remote_addr, lstorage, rstorage;
*der_out = empty_data();
+ memset(&enc, 0, sizeof(enc));
memset(&lstorage, 0, sizeof(lstorage));
memset(&rstorage, 0, sizeof(rstorage));
if (!authcon->local_addr)
@@ -130,12 +135,11 @@ krb5_mk_priv(krb5_context context, krb5_auth_context authcon,
key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
ret = create_krbpriv(context, userdata, key, &rdata, local_addr,
- remote_addr, &authcon->cstate, &der_krbpriv);
+ remote_addr, &authcon->cstate, &der_krbpriv, &enc);
if (ret)
goto cleanup;
- ret = k5_privsafe_check_replay(context, authcon, authcon->local_addr,
- "_priv", &rdata, FALSE);
+ ret = k5_privsafe_check_replay(context, authcon, NULL, &enc, NULL);
if (ret)
goto cleanup;
@@ -147,6 +151,7 @@ krb5_mk_priv(krb5_context context, krb5_auth_context authcon,
cleanup:
krb5_free_data_contents(context, &der_krbpriv);
+ zapfree(enc.ciphertext.data, enc.ciphertext.length);
free(lstorage.contents);
free(rstorage.contents);
return ret;
diff --git a/src/lib/krb5/krb/mk_safe.c b/src/lib/krb5/krb/mk_safe.c
index 02ee725..8a10616 100644
--- a/src/lib/krb5/krb/mk_safe.c
+++ b/src/lib/krb5/krb/mk_safe.c
@@ -36,15 +36,16 @@
/*
* Marshal a KRB-SAFE message into der_out, with a keyed checksum of type
- * sumtype. Use the timestamp and sequence number from rdata and the addresses
- * from local_addr and remote_addr (the second of which may be NULL). der_out
- * should be freed by the caller when finished.
+ * sumtype. Store the checksum in cksum_out. Use the timestamp and sequence
+ * number from rdata and the addresses from local_addr and remote_addr (the
+ * second of which may be NULL). der_out and cksum_out should be freed by the
+ * caller when finished.
*/
static krb5_error_code
create_krbsafe(krb5_context context, const krb5_data *userdata, krb5_key key,
const krb5_replay_data *rdata, krb5_address *local_addr,
krb5_address *remote_addr, krb5_cksumtype sumtype,
- krb5_data *der_out)
+ krb5_data *der_out, krb5_checksum *cksum_out)
{
krb5_error_code ret;
krb5_safe safemsg;
@@ -85,12 +86,14 @@ create_krbsafe(krb5_context context, const krb5_data *userdata, krb5_key key,
/* Encode the message again with the real checksum. */
safemsg.checksum = &safe_checksum;
ret = encode_krb5_safe(&safemsg, &der_krbsafe);
- krb5_free_checksum_contents(context, &safe_checksum);
- if (ret)
+ if (ret) {
+ krb5_free_checksum_contents(context, &safe_checksum);
return ret;
+ }
*der_out = *der_krbsafe;
free(der_krbsafe);
+ *cksum_out = safe_checksum;
return 0;
}
@@ -126,10 +129,12 @@ krb5_mk_safe(krb5_context context, krb5_auth_context authcon,
krb5_key key;
krb5_replay_data rdata;
krb5_data der_krbsafe = empty_data();
+ krb5_checksum cksum;
krb5_address *local_addr, *remote_addr, lstorage, rstorage;
krb5_cksumtype sumtype;
*der_out = empty_data();
+ memset(&cksum, 0, sizeof(cksum));
memset(&lstorage, 0, sizeof(lstorage));
memset(&rstorage, 0, sizeof(rstorage));
if (authcon->local_addr == NULL)
@@ -147,12 +152,11 @@ krb5_mk_safe(krb5_context context, krb5_auth_context authcon,
key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
sumtype = safe_cksumtype(context, authcon, key->keyblock.enctype);
ret = create_krbsafe(context, userdata, key, &rdata, local_addr,
- remote_addr, sumtype, &der_krbsafe);
+ remote_addr, sumtype, &der_krbsafe, &cksum);
if (ret)
goto cleanup;
- ret = k5_privsafe_check_replay(context, authcon, authcon->local_addr,
- "_safe", &rdata, FALSE);
+ ret = k5_privsafe_check_replay(context, authcon, NULL, NULL, &cksum);
if (ret)
goto cleanup;
@@ -164,6 +168,7 @@ krb5_mk_safe(krb5_context context, krb5_auth_context authcon,
cleanup:
krb5_free_data_contents(context, &der_krbsafe);
+ krb5_free_checksum_contents(context, &cksum);
free(lstorage.contents);
free(rstorage.contents);
return ret;
diff --git a/src/lib/krb5/krb/privsafe.c b/src/lib/krb5/krb/privsafe.c
index a1e5230..461e11f 100644
--- a/src/lib/krb5/krb/privsafe.c
+++ b/src/lib/krb5/krb/privsafe.c
@@ -106,38 +106,38 @@ k5_privsafe_gen_addrs(krb5_context context, krb5_auth_context authcon,
krb5_error_code
k5_privsafe_check_replay(krb5_context context, krb5_auth_context authcon,
- krb5_address *addr, const char *uniq,
const krb5_replay_data *rdata,
- krb5_boolean check_time)
+ const krb5_enc_data *enc, const krb5_checksum *cksum)
{
krb5_error_code ret;
- krb5_donot_replay replay;
- char *client = NULL;
+ krb5_data tag;
+
+ assert(enc != NULL || cksum != NULL);
if (!(authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME))
return 0;
- if (authcon->rcache == NULL)
- return KRB5_RC_REQUIRED;
-
- if (check_time) {
+ if (rdata != NULL) {
ret = krb5_check_clockskew(context, rdata->timestamp);
if (ret)
return ret;
}
- ret = krb5_gen_replay_name(context, addr, uniq, &client);
- if (ret)
- return ret;
-
- replay.client = client;
- replay.server = "";
- replay.msghash = NULL;
- replay.cusec = rdata->usec;
- replay.ctime = rdata->timestamp;
- ret = krb5_rc_store(context, authcon->rcache, &replay);
- free(replay.client);
- return ret;
+ if (enc != NULL) {
+ ret = k5_rc_tag_from_ciphertext(context, enc, &tag);
+ if (ret)
+ return ret;
+ } else {
+ tag = make_data(cksum->contents, cksum->length);
+ }
+
+ if (authcon->memrcache == NULL) {
+ ret = k5_memrcache_create(context, &authcon->memrcache);
+ if (ret)
+ return ret;
+ }
+
+ return k5_memrcache_store(context, authcon->memrcache, &tag);
}
/*
diff --git a/src/lib/krb5/krb/rd_cred.c b/src/lib/krb5/krb/rd_cred.c
index 7fdd103..720147c 100644
--- a/src/lib/krb5/krb/rd_cred.c
+++ b/src/lib/krb5/krb/rd_cred.c
@@ -158,9 +158,6 @@ krb5_rd_cred(krb5_context context, krb5_auth_context authcon,
replaydata_out == NULL)
return KRB5_RC_REQUIRED;
- if ((flags & KRB5_AUTH_CONTEXT_DO_TIME) && authcon->rcache == NULL)
- return KRB5_RC_REQUIRED;
-
ret = decode_krb5_cred(creddata, &krbcred);
if (ret)
goto cleanup;
@@ -173,13 +170,13 @@ krb5_rd_cred(krb5_context context, krb5_auth_context authcon,
if (ret)
goto cleanup;
- rdata.timestamp = encpart->timestamp;
- rdata.usec = encpart->usec;
- rdata.seq = encpart->nonce;
- ret = k5_privsafe_check_replay(context, authcon, authcon->remote_addr,
- "_forw", &rdata, TRUE);
- if (ret)
- goto cleanup;
+ if (authcon->recv_subkey != NULL || authcon->key != NULL) {
+ rdata.timestamp = encpart->timestamp;
+ ret = k5_privsafe_check_replay(context, authcon, &rdata,
+ &krbcred->enc_part, NULL);
+ if (ret)
+ goto cleanup;
+ }
if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
if (authcon->remote_seq_number != (uint32_t)encpart->nonce) {
diff --git a/src/lib/krb5/krb/rd_priv.c b/src/lib/krb5/krb/rd_priv.c
index 34eb656..29a9746 100644
--- a/src/lib/krb5/krb/rd_priv.c
+++ b/src/lib/krb5/krb/rd_priv.c
@@ -36,13 +36,15 @@
/*
* Unmarshal a KRB-PRIV message from der_krbpriv, placing the confidential user
- * data in *userdata_out and replay data in *rdata_out. The caller should free
- * *userdata_out when finished.
+ * data in *userdata_out, ciphertext in *enc_out, and replay data in
+ * *rdata_out. The caller should free *userdata_out and *enc_out when
+ * finished.
*/
static krb5_error_code
read_krbpriv(krb5_context context, krb5_auth_context authcon,
const krb5_data *der_krbpriv, const krb5_key key,
- krb5_replay_data *rdata_out, krb5_data *userdata_out)
+ krb5_replay_data *rdata_out, krb5_data *userdata_out,
+ krb5_enc_data *enc_out)
{
krb5_error_code ret;
krb5_priv *privmsg = NULL;
@@ -84,6 +86,9 @@ read_krbpriv(krb5_context context, krb5_auth_context authcon,
*userdata_out = encpart->user_data;
encpart->user_data.data = NULL;
+ *enc_out = privmsg->enc_part;
+ memset(&privmsg->enc_part, 0, sizeof(privmsg->enc_part));
+
cleanup:
krb5_free_priv_enc_part(context, encpart);
krb5_free_priv(context, privmsg);
@@ -99,26 +104,24 @@ krb5_rd_priv(krb5_context context, krb5_auth_context authcon,
krb5_error_code ret;
krb5_key key;
krb5_replay_data rdata;
+ krb5_enc_data enc;
krb5_data userdata = empty_data();
const krb5_int32 flags = authcon->auth_context_flags;
*userdata_out = empty_data();
+ memset(&enc, 0, sizeof(enc));
if (((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
(flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && rdata_out == NULL)
return KRB5_RC_REQUIRED;
- if ((flags & KRB5_AUTH_CONTEXT_DO_TIME) && authcon->remote_addr == NULL)
- return KRB5_REMOTE_ADDR_REQUIRED;
-
key = (authcon->recv_subkey != NULL) ? authcon->recv_subkey : authcon->key;
memset(&rdata, 0, sizeof(rdata));
- ret = read_krbpriv(context, authcon, inbuf, key, &rdata, &userdata);
+ ret = read_krbpriv(context, authcon, inbuf, key, &rdata, &userdata, &enc);
if (ret)
goto cleanup;
- ret = k5_privsafe_check_replay(context, authcon, authcon->remote_addr,
- "_priv", &rdata, TRUE);
+ ret = k5_privsafe_check_replay(context, authcon, &rdata, &enc, NULL);
if (ret)
goto cleanup;
@@ -141,6 +144,7 @@ krb5_rd_priv(krb5_context context, krb5_auth_context authcon,
userdata = empty_data();
cleanup:
+ krb5_free_data_contents(context, &enc.ciphertext);
krb5_free_data_contents(context, &userdata);
return ret;
}
diff --git a/src/lib/krb5/krb/rd_safe.c b/src/lib/krb5/krb/rd_safe.c
index eab7f6d..17f2b27 100644
--- a/src/lib/krb5/krb/rd_safe.c
+++ b/src/lib/krb5/krb/rd_safe.c
@@ -36,23 +36,26 @@
/*
* Unmarshal a KRB-SAFE message from der_krbsafe, placing the
- * integrity-protected user data in *userdata_out and replay data in
- * *rdata_out. The caller should free *userdata_out when finished.
+ * integrity-protected user data in *userdata_out, replay data in *rdata_out,
+ * and checksum in *cksum_out. The caller should free *userdata_out and
+ * *cksum_out when finished.
*/
static krb5_error_code
read_krbsafe(krb5_context context, krb5_auth_context ac,
const krb5_data *der_krbsafe, krb5_key key,
- krb5_replay_data *rdata_out, krb5_data *userdata_out)
+ krb5_replay_data *rdata_out, krb5_data *userdata_out,
+ krb5_checksum **cksum_out)
{
krb5_error_code ret;
krb5_safe *krbsafe;
krb5_data *safe_body = NULL, *der_zerosafe = NULL;
- krb5_checksum zero_cksum, *safe_cksum;
+ krb5_checksum zero_cksum, *safe_cksum = NULL;
krb5_octet zero_octet = 0;
krb5_boolean valid;
struct krb5_safe_with_body swb;
*userdata_out = empty_data();
+ *cksum_out = NULL;
if (!krb5_is_krb_safe(der_krbsafe))
return KRB5KRB_AP_ERR_MSG_TYPE;
@@ -75,7 +78,8 @@ read_krbsafe(krb5_context context, krb5_auth_context ac,
if (ret)
goto cleanup;
- /* Regenerate the KRB-SAFE message without the checksum. */
+ /* Regenerate the KRB-SAFE message without the checksum. Save the message
+ * checksum to verify. */
safe_cksum = krbsafe->checksum;
zero_cksum.length = 0;
zero_cksum.checksum_type = 0;
@@ -84,7 +88,7 @@ read_krbsafe(krb5_context context, krb5_auth_context ac,
swb.body = safe_body;
swb.safe = krbsafe;
ret = encode_krb5_safe_with_body(&swb, &der_zerosafe);
- krbsafe->checksum = safe_cksum;
+ krbsafe->checksum = NULL;
if (ret)
goto cleanup;
@@ -109,6 +113,9 @@ read_krbsafe(krb5_context context, krb5_auth_context ac,
*userdata_out = krbsafe->user_data;
krbsafe->user_data.data = NULL;
+ *cksum_out = safe_cksum;
+ safe_cksum = NULL;
+
cleanup:
if (der_zerosafe != NULL) {
zap(der_zerosafe->data, der_zerosafe->length);
@@ -116,6 +123,7 @@ cleanup:
}
krb5_free_data(context, safe_body);
krb5_free_safe(context, krbsafe);
+ krb5_free_checksum(context, safe_cksum);
return ret;
}
@@ -128,6 +136,7 @@ krb5_rd_safe(krb5_context context, krb5_auth_context authcon,
krb5_key key;
krb5_replay_data rdata;
krb5_data userdata = empty_data();
+ krb5_checksum *cksum;
const krb5_int32 flags = authcon->auth_context_flags;
*userdata_out = empty_data();
@@ -136,17 +145,14 @@ krb5_rd_safe(krb5_context context, krb5_auth_context authcon,
(flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && rdata_out == NULL)
return KRB5_RC_REQUIRED;
- if ((flags & KRB5_AUTH_CONTEXT_DO_TIME) && authcon->remote_addr == NULL)
- return KRB5_REMOTE_ADDR_REQUIRED;
-
key = (authcon->recv_subkey != NULL) ? authcon->recv_subkey : authcon->key;
memset(&rdata, 0, sizeof(rdata));
- ret = read_krbsafe(context, authcon, inbuf, key, &rdata, &userdata);
+ ret = read_krbsafe(context, authcon, inbuf, key, &rdata, &userdata,
+ &cksum);
if (ret)
goto cleanup;
- ret = k5_privsafe_check_replay(context, authcon, authcon->remote_addr,
- "_safe", &rdata, TRUE);
+ ret = k5_privsafe_check_replay(context, authcon, &rdata, NULL, cksum);
if (ret)
goto cleanup;
@@ -170,5 +176,6 @@ krb5_rd_safe(krb5_context context, krb5_auth_context authcon,
cleanup:
krb5_free_data_contents(context, &userdata);
+ krb5_free_checksum(context, cksum);
return ret;
}
diff --git a/src/lib/krb5/rcache/rc_conv.c b/src/lib/krb5/rcache/rc_conv.c
index f2fe528..ff33a97 100644
--- a/src/lib/krb5/rcache/rc_conv.c
+++ b/src/lib/krb5/rcache/rc_conv.c
@@ -74,3 +74,23 @@ krb5_rc_hash_message(krb5_context context, const krb5_data *message,
*out = hash;
return 0;
}
+
+krb5_error_code
+k5_rc_tag_from_ciphertext(krb5_context context, const krb5_enc_data *enc,
+ krb5_data *tag_out)
+{
+ krb5_error_code ret;
+ const krb5_data *cdata = &enc->ciphertext;
+ unsigned int len;
+
+ *tag_out = empty_data();
+
+ ret = krb5_c_crypto_length(context, enc->enctype,
+ KRB5_CRYPTO_TYPE_CHECKSUM, &len);
+ if (ret)
+ return ret;
+ if (cdata->length < len)
+ return EINVAL;
+ *tag_out = make_data(cdata->data + cdata->length - len, len);
+ return 0;
+}
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 0eec2b6..5afbe21 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -8,10 +8,10 @@ RUN_DB_TEST = $(RUN_SETUP) KRB5_KDC_PROFILE=kdc.conf KRB5_CONFIG=krb5.conf \
OBJS= adata.o etinfo.o forward.o gcred.o hist.o hooks.o hrealm.o \
icinterleave.o icred.o kdbtest.o localauth.o plugorder.o rdreq.o \
- responder.o s2p.o s4u2proxy.o unlockiter.o
+ replay.o responder.o s2p.o s4u2proxy.o unlockiter.o
EXTRADEPSRCS= adata.c etinfo.c forward.c gcred.c hist.c hooks.c hrealm.c \
- icinterleave.c icred.c kdbtest.c localauth.c plugorder.c rdreq.o \
- responder.c s2p.c s4u2proxy.c unlockiter.c
+ icinterleave.c icred.c kdbtest.c localauth.c plugorder.c rdreq.c \
+ replay.c responder.c s2p.c s4u2proxy.c unlockiter.c
TEST_DB = ./testdb
TEST_REALM = FOO.TEST.REALM
@@ -63,6 +63,9 @@ plugorder: plugorder.o $(KRB5_BASE_DEPLIBS)
rdreq: rdreq.o $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o $@ rdreq.o $(KRB5_BASE_LIBS)
+replay: replay.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ replay.o $(KRB5_BASE_LIBS)
+
responder: responder.o $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o $@ responder.o $(KRB5_BASE_LIBS)
@@ -119,7 +122,7 @@ kdb_check: kdc.conf krb5.conf
$(RM) $(TEST_DB)* stash_file
check-pytests: adata etinfo forward gcred hist hooks hrealm icinterleave icred
-check-pytests: kdbtest localauth plugorder rdreq responder s2p s4u2proxy
+check-pytests: kdbtest localauth plugorder rdreq replay responder s2p s4u2proxy
check-pytests: unlockiter
$(RUNPYTEST) $(srcdir)/t_general.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_hooks.py $(PYTESTFLAGS)
@@ -178,10 +181,11 @@ check-pytests: unlockiter
$(RUNPYTEST) $(srcdir)/t_kdcpolicy.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_u2u.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_kdcoptions.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_replay.py $(PYTESTFLAGS)
clean:
$(RM) adata etinfo forward gcred hist hooks hrealm icinterleave icred
- $(RM) kdbtest localauth plugorder rdreq responder s2p s4u2proxy
+ $(RM) kdbtest localauth plugorder rdreq replay responder s2p s4u2proxy
$(RM) unlockiter
$(RM) krb5.conf kdc.conf
$(RM) -rf kdc_realm/sandbox ldap
diff --git a/src/tests/replay.c b/src/tests/replay.c
new file mode 100644
index 0000000..1703123
--- /dev/null
+++ b/src/tests/replay.c
@@ -0,0 +1,172 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/replay.c - test replay cache using libkrb5 functions */
+/*
+ * 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"
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context ctx;
+ krb5_auth_context c_authcon, s_authcon, s_authcon2;
+ krb5_rcache rc;
+ krb5_ccache cc;
+ krb5_principal client, server;
+ krb5_creds mcred, *cred, **tmpcreds;
+ krb5_data der_apreq, der_krbsafe, der_krbpriv, *der_krbcred, tmpdata;
+ krb5_address addr;
+ struct in_addr inaddr;
+ const char *server_name;
+
+ assert(argc == 2);
+ server_name = argv[1];
+
+ /* Create client and server auth contexts. (They will use a replay cache
+ * by default.) */
+ ret = krb5_init_context(&ctx);
+ assert(ret == 0);
+ ret = krb5_auth_con_init(ctx, &c_authcon);
+ assert(ret == 0);
+ ret = krb5_auth_con_init(ctx, &s_authcon);
+ assert(ret == 0);
+
+ /* Set dummy addresses for the auth contexts. */
+ memset(&inaddr, 0, sizeof(inaddr));
+ addr.addrtype = ADDRTYPE_INET;
+ addr.length = sizeof(inaddr);
+ addr.contents = (uint8_t *)&inaddr;
+ ret = krb5_auth_con_setaddrs(ctx, c_authcon, &addr, &addr);
+ assert(ret == 0);
+ ret = krb5_auth_con_setaddrs(ctx, s_authcon, &addr, &addr);
+ assert(ret == 0);
+
+ /* Set up replay caches for the auth contexts. */
+ tmpdata = string2data("testclient");
+ ret = krb5_get_server_rcache(ctx, &tmpdata, &rc);
+ assert(ret == 0);
+ ret = krb5_auth_con_setrcache(ctx, c_authcon, rc);
+ assert(ret == 0);
+ tmpdata = string2data("testserver");
+ ret = krb5_get_server_rcache(ctx, &tmpdata, &rc);
+ assert(ret == 0);
+ ret = krb5_auth_con_setrcache(ctx, s_authcon, rc);
+ assert(ret == 0);
+
+ /* Construct the client and server principal names. */
+ ret = krb5_cc_default(ctx, &cc);
+ assert(ret == 0);
+ ret = krb5_cc_get_principal(ctx, cc, &client);
+ assert(ret == 0);
+ ret = krb5_parse_name(ctx, server_name, &server);
+ assert(ret == 0);
+
+ /* Get credentials for the client. */
+ memset(&mcred, 0, sizeof(mcred));
+ mcred.client = client;
+ mcred.server = server;
+ ret = krb5_get_credentials(ctx, 0, cc, &mcred, &cred);
+ assert(ret == 0);
+
+ /* Send an AP-REP to establish the sessions. */
+ ret = krb5_mk_req_extended(ctx, &c_authcon, 0, NULL, cred, &der_apreq);
+ assert(ret == 0);
+ ret = krb5_rd_req(ctx, &s_authcon, &der_apreq, NULL, NULL, NULL, NULL);
+ assert(ret == 0);
+
+ /* Set up another server auth context with the same rcache name and replay
+ * the AP-REQ. */
+ ret = krb5_auth_con_init(ctx, &s_authcon2);
+ assert(ret == 0);
+ tmpdata = string2data("testserver");
+ ret = krb5_get_server_rcache(ctx, &tmpdata, &rc);
+ assert(ret == 0);
+ ret = krb5_auth_con_setrcache(ctx, s_authcon2, rc);
+ assert(ret == 0);
+ ret = krb5_rd_req(ctx, &s_authcon2, &der_apreq, NULL, NULL, NULL, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+ krb5_auth_con_free(ctx, s_authcon2);
+
+ /* Make a KRB-SAFE message with the client auth context. */
+ tmpdata = string2data("safemsg");
+ ret = krb5_mk_safe(ctx, c_authcon, &tmpdata, &der_krbsafe, NULL);
+ assert(ret == 0);
+ /* Play it back to the client to detect a reflection. */
+ ret = krb5_rd_safe(ctx, c_authcon, &der_krbsafe, &tmpdata, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+ /* Send it to the server auth context twice, to detect a replay. */
+ ret = krb5_rd_safe(ctx, s_authcon, &der_krbsafe, &tmpdata, NULL);
+ assert(ret == 0);
+ krb5_free_data_contents(ctx, &tmpdata);
+ ret = krb5_rd_safe(ctx, s_authcon, &der_krbsafe, &tmpdata, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+
+ /* Make a KRB-PRIV message with the client auth context. */
+ tmpdata = string2data("safemsg");
+ ret = krb5_mk_priv(ctx, c_authcon, &tmpdata, &der_krbpriv, NULL);
+ assert(ret == 0);
+ /* Play it back to the client to detect a reflection. */
+ ret = krb5_rd_priv(ctx, c_authcon, &der_krbpriv, &tmpdata, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+ /* Send it to the server auth context twice, to detect a replay. */
+ ret = krb5_rd_priv(ctx, s_authcon, &der_krbpriv, &tmpdata, NULL);
+ assert(ret == 0);
+ krb5_free_data_contents(ctx, &tmpdata);
+ ret = krb5_rd_priv(ctx, s_authcon, &der_krbpriv, &tmpdata, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+
+ /* Make a KRB-CRED message with the client auth context. */
+ tmpdata = string2data("safemsg");
+ ret = krb5_mk_1cred(ctx, c_authcon, cred, &der_krbcred, NULL);
+ assert(ret == 0);
+ /* Play it back to the client to detect a reflection. */
+ ret = krb5_rd_cred(ctx, c_authcon, der_krbcred, &tmpcreds, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+ /* Send it to the server auth context twice, to detect a replay. */
+ ret = krb5_rd_cred(ctx, s_authcon, der_krbcred, &tmpcreds, NULL);
+ assert(ret == 0);
+ krb5_free_tgt_creds(ctx, tmpcreds);
+ ret = krb5_rd_cred(ctx, s_authcon, der_krbcred, &tmpcreds, NULL);
+ assert(ret == KRB5KRB_AP_ERR_REPEAT);
+
+ krb5_free_data_contents(ctx, &der_apreq);
+ krb5_free_data_contents(ctx, &der_krbsafe);
+ krb5_free_data_contents(ctx, &der_krbpriv);
+ krb5_free_data(ctx, der_krbcred);
+ krb5_free_creds(ctx, cred);
+ krb5_cc_close(ctx, cc);
+ krb5_free_principal(ctx, client);
+ krb5_free_principal(ctx, server);
+ krb5_auth_con_free(ctx, c_authcon);
+ krb5_auth_con_free(ctx, s_authcon);
+ krb5_free_context(ctx);
+ return 0;
+}
diff --git a/src/tests/t_replay.py b/src/tests/t_replay.py
new file mode 100644
index 0000000..6ad58fe
--- /dev/null
+++ b/src/tests/t_replay.py
@@ -0,0 +1,6 @@
+from k5test import *
+
+realm = K5Realm()
+realm.run(['./replay', realm.host_princ])
+
+success('Replay tests')
More information about the cvs-krb5
mailing list