From: Greg Hudson Date: Fri, 1 Mar 2019 02:28:43 +0000 (-0500) Subject: Use memory replay cache for DO_TIME auth contexts X-Git-Tag: krb5-1.18-beta1~178 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F901%2Fhead;p=thirdparty%2Fkrb5.git 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) --- diff --git a/.gitignore b/.gitignore index 8117674854..f39dd6a516 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 7d3c1295ea..71dce7315f 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 a67eec1cfb..eb31607706 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 c86a4af634..a8a97ebe40 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 821b41e4a0..a010ae40ab 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 3e80241b3c..d45adab57b 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 003a0fdbaa..62224f1cf0 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 d66ab8ad1f..d0231697a2 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 02ee7256a3..8a106160e4 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 a1e5230c0c..461e11fabd 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 7fdd10354a..720147c3bc 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 34eb656e32..29a97465f8 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 eab7f6d746..17f2b278f6 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 f2fe528ac3..ff33a97ec5 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 0eec2b69f4..5afbe2121b 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 0000000000..1703123d29 --- /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 0000000000..6ad58fe132 --- /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')