#include "gsskrb5_locl.h"
krb5_error_code
_gsskrb5_decode_be_om_uint32(const void *ptr, OM_uint32 *n)
{
const u_char *p = ptr;
*n = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
return 0;
}
static OM_uint32
store_ext(krb5_storage *sp, uint16_t type, krb5_data *data)
{
krb5_error_code ret;
ret = krb5_store_uint16(sp, type);
if (ret) return ret;
ret = krb5_store_uint16(sp, data->length);
if (ret) return ret;
ret = krb5_storage_write(sp, data->data, data->length);
if (ret != data->length)
return ENOMEM;
return 0;
}
OM_uint32
_gsskrb5_create_8003_checksum(OM_uint32 *minor_status,
krb5_context context,
krb5_crypto crypto,
const gss_channel_bindings_t input_chan_bindings,
OM_uint32 flags,
krb5_data *fwd_data,
krb5_data *pkt_cksum,
Checksum *result)
{
uint8_t channelbindings[16];
gss_buffer_desc cksum;
krb5_error_code ret;
krb5_storage *sp;
OM_uint32 maj_stat, junk;
cksum.value = NULL;
cksum.length = 0;
sp = krb5_storage_emem();
if (sp == NULL) {
ret = ENOMEM;
goto out;
}
krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
ret = krb5_store_uint32(sp, 16);
if (ret) goto out;
if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS) {
memset (channelbindings, 0, 16);
} else {
maj_stat = gss_mg_gen_cb(minor_status, input_chan_bindings, channelbindings, &cksum);
if (maj_stat) {
ret = *minor_status;
goto out;
}
}
ret = krb5_storage_write(sp, channelbindings, sizeof(channelbindings));
if (ret != sizeof(channelbindings)) {
ret = ENOMEM;
goto out;
}
ret = krb5_store_uint32(sp, flags);
if (ret) goto out;
if (flags & GSS_C_DELEG_FLAG) {
ret = store_ext(sp, 1, fwd_data);
if (ret) goto out;
}
if (pkt_cksum->length > 0) {
ret = store_ext(sp, 2, pkt_cksum);
if (ret) goto out;
}
if (crypto && input_chan_bindings && cksum.length) {
Checksum checksum;
krb5_data data;
size_t size;
memset(&checksum, 0, sizeof(checksum));
ret = krb5_create_checksum(context, crypto, KRB5_KU_GSSAPI_EXTS, 0,
cksum.value, cksum.length, &checksum);
if (ret) goto out;
ASN1_MALLOC_ENCODE(Checksum, data.data, data.length,
&checksum, &size, ret);
if (ret)
goto out;
if (data.length != size)
krb5_abortx(context, "internal error in ASN.1 encoder");
ret = store_ext(sp, 0, &data);
krb5_data_free(&data);
if (ret) goto out;
}
result->cksumtype = CKSUMTYPE_GSSAPI;
ret = krb5_storage_to_data(sp, &result->checksum);
out:
gss_release_buffer(&junk, &cksum);
if (sp)
krb5_storage_free(sp);
if (ret) {
*minor_status = ret;
return GSS_S_FAILURE;
}
return GSS_S_COMPLETE;
}
static krb5_error_code
read_ext(krb5_storage *sp, uint16_t *type, krb5_data *data)
{
krb5_error_code ret;
uint16_t len;
ret = krb5_ret_uint16(sp, type);
if (ret)
return ret;
ret = krb5_ret_uint16(sp, &len);
if (ret)
return ret;
ret = krb5_data_alloc(data, len);
if (ret)
return ret;
ret = krb5_storage_read(sp, data->data, data->length);
if (ret != data->length) {
krb5_data_free(data);
return HEIM_ERR_EOF;
}
return 0;
}
OM_uint32
_gsskrb5_verify_8003_checksum(OM_uint32 *minor_status,
krb5_context context,
krb5_crypto crypto,
const gss_channel_bindings_t input_chan_bindings,
const Checksum *cksum,
OM_uint32 *flags,
krb5_data *fwd_data,
krb5_data *finished)
{
unsigned count = 0, verified_checksum = 0;
krb5_error_code ret;
krb5_storage *sp;
unsigned char hash[16], pkthash[16];
uint32_t length;
krb5_data data;
uint16_t type;
OM_uint32 maj_stat, junk;
gss_buffer_desc cbdata;
cbdata.length = 0;
krb5_data_zero(&data);
if(cksum->cksumtype != CKSUMTYPE_GSSAPI || cksum->checksum.length < 24) {
*minor_status = 0;
return GSS_S_BAD_BINDINGS;
}
sp = krb5_storage_from_readonly_mem(cksum->checksum.data,
cksum->checksum.length);
if (sp == NULL) {
*minor_status = ENOMEM;
return GSS_S_FAILURE;
}
krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
ret = krb5_ret_uint32(sp, &length);
if (ret) goto out;
if(length != sizeof(hash)) {
ret = ENOMEM;
goto out;
}
ret = krb5_storage_read(sp, pkthash, sizeof(pkthash));
if (ret != sizeof(pkthash)) {
ret = ENOMEM;
goto out;
}
maj_stat = gss_mg_validate_cb(minor_status, input_chan_bindings,
pkthash, &cbdata);
if (maj_stat) {
ret = *minor_status;
goto out;
}
ret = krb5_ret_uint32(sp, flags);
if (ret) goto out;
if ((*flags) & GSS_C_DELEG_FLAG) {
ret = read_ext(sp, &type, fwd_data);
if (ret) goto out;
if (type != 1) {
ret = EINVAL;
goto out;
}
}
while ((ret = read_ext(sp, &type, &data)) == 0) {
count++;
if (type == 0 && input_chan_bindings) {
Checksum checksum;
if (crypto == NULL) {
ret = ENOMEM;
goto out;
}
ret = decode_Checksum(data.data, data.length, &checksum, NULL);
if (ret) goto out;
ret = krb5_verify_checksum(context, crypto, KRB5_KU_GSSAPI_EXTS,
cbdata.value, cbdata.length,
&checksum);
free_Checksum(&checksum);
krb5_data_free(&data);
if (ret) goto out;
verified_checksum = 1;
} else if (type == 2) {
*finished = data;
krb5_data_zero(&data);
} else
krb5_data_free(&data);
}
if (ret != HEIM_ERR_EOF && ret != 0)
goto out;
if (input_chan_bindings && count && !verified_checksum) {
ret = EINVAL;
goto out;
}
ret = 0;
out:
gss_release_buffer(&junk, &cbdata);
krb5_data_free(&data);
krb5_storage_free(sp);
if (ret) {
*minor_status = ret;
return GSS_S_BAD_BINDINGS;
}
return GSS_S_COMPLETE;
}