/src/tests/localauth
/src/tests/plugorder
/src/tests/rdreq
+/src/tests/replay
/src/tests/responder
/src/tests/s2p
/src/tests/s4u2proxy
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);
* 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.
* 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.
* 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.
* @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
*/
* 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
* @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
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;
}
#ifndef KRB5_AUTH_CONTEXT
#define KRB5_AUTH_CONTEXT
+#include "../rcache/memrcache.h"
+
struct _krb5_auth_context {
krb5_magic magic;
krb5_address * remote_addr;
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;
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,
/*
* 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;
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++);
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);
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));
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;
authcon->local_seq_number++;
cleanup:
+ krb5_free_data_contents(context, &enc.ciphertext);
free(lstorage.contents);
free(rstorage.contents);
if (der_krbcred != NULL) {
#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;
*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);
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)
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;
cleanup:
krb5_free_data_contents(context, &der_krbpriv);
+ zapfree(enc.ciphertext.data, enc.ciphertext.length);
free(lstorage.contents);
free(rstorage.contents);
return ret;
/*
* 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;
/* 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;
}
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)
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;
cleanup:
krb5_free_data_contents(context, &der_krbsafe);
+ krb5_free_checksum_contents(context, &cksum);
free(lstorage.contents);
free(rstorage.contents);
return ret;
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);
}
/*
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;
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) {
/*
* 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;
*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);
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;
userdata = empty_data();
cleanup:
+ krb5_free_data_contents(context, &enc.ciphertext);
krb5_free_data_contents(context, &userdata);
return ret;
}
/*
* 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;
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;
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;
*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);
}
krb5_free_data(context, safe_body);
krb5_free_safe(context, krbsafe);
+ krb5_free_checksum(context, safe_cksum);
return ret;
}
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();
(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;
cleanup:
krb5_free_data_contents(context, &userdata);
+ krb5_free_checksum(context, cksum);
return ret;
}
*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;
+}
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
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)
$(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)
$(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
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+from k5test import *
+
+realm = K5Realm()
+realm.run(['./replay', realm.host_princ])
+
+success('Replay tests')