#include "k5-int.h"
#include "int-proto.h"
#include "auth_con.h"
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_checksum **cksum_out)
{
krb5_error_code ret;
krb5_safe *krbsafe;
krb5_data *safe_body = NULL, *der_zerosafe = NULL;
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;
ret = decode_krb5_safe_with_body(der_krbsafe, &krbsafe, &safe_body);
if (ret)
return ret;
if (!krb5_c_valid_cksumtype(krbsafe->checksum->checksum_type)) {
ret = KRB5_PROG_SUMTYPE_NOSUPP;
goto cleanup;
}
if (!krb5_c_is_coll_proof_cksum(krbsafe->checksum->checksum_type) ||
!krb5_c_is_keyed_cksum(krbsafe->checksum->checksum_type)) {
ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
goto cleanup;
}
ret = k5_privsafe_check_addrs(context, ac, krbsafe->s_address,
krbsafe->r_address);
if (ret)
goto cleanup;
safe_cksum = krbsafe->checksum;
zero_cksum.length = 0;
zero_cksum.checksum_type = 0;
zero_cksum.contents = &zero_octet;
krbsafe->checksum = &zero_cksum;
swb.body = safe_body;
swb.safe = krbsafe;
ret = encode_krb5_safe_with_body(&swb, &der_zerosafe);
krbsafe->checksum = NULL;
if (ret)
goto cleanup;
ret = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
der_zerosafe, safe_cksum, &valid);
if (!valid) {
ret = krb5_k_verify_checksum(context, key,
KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
safe_body, safe_cksum, &valid);
if (!valid) {
ret = KRB5KRB_AP_ERR_MODIFIED;
goto cleanup;
}
}
rdata_out->timestamp = krbsafe->timestamp;
rdata_out->usec = krbsafe->usec;
rdata_out->seq = krbsafe->seq_number;
*userdata_out = krbsafe->user_data;
krbsafe->user_data.data = NULL;
*cksum_out = safe_cksum;
safe_cksum = NULL;
cleanup:
zapfreedata(der_zerosafe);
krb5_free_data(context, safe_body);
krb5_free_safe(context, krbsafe);
krb5_free_checksum(context, safe_cksum);
return ret;
}
krb5_error_code KRB5_CALLCONV
krb5_rd_safe(krb5_context context, krb5_auth_context authcon,
const krb5_data *inbuf, krb5_data *userdata_out,
krb5_replay_data *rdata_out)
{
krb5_error_code 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();
if (((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
(flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && rdata_out == NULL)
return KRB5_RC_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,
&cksum);
if (ret)
goto cleanup;
ret = k5_privsafe_check_replay(context, authcon, &rdata, NULL, cksum);
if (ret)
goto cleanup;
if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
if (!k5_privsafe_check_seqnum(context, authcon, rdata.seq)) {
ret = KRB5KRB_AP_ERR_BADORDER;
goto cleanup;
}
authcon->remote_seq_number++;
}
if ((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
(flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
rdata_out->timestamp = rdata.timestamp;
rdata_out->usec = rdata.usec;
rdata_out->seq = rdata.seq;
}
*userdata_out = userdata;
userdata = empty_data();
cleanup:
krb5_free_data_contents(context, &userdata);
krb5_free_checksum(context, cksum);
return ret;
}