#include "k5-int.h"
#include "k5-input.h"
#include "k5-buf.h"
#include "k5-utf8.h"
#include "kdc_util.h"
struct encoded_wchars {
uint16_t bytes_len;
uint16_t num_wchars;
uint8_t *encoded;
};
static krb5_error_code
dec_wchar_pointer(struct k5input *in, char **out)
{
const uint8_t *bytes;
uint32_t actual_count;
(void)k5_input_get_uint32_le(in);
(void)k5_input_get_uint32_le(in);
actual_count = k5_input_get_uint32_le(in);
if (actual_count > UINT32_MAX / 2)
return ERANGE;
bytes = k5_input_get_bytes(in, actual_count * 2);
if (bytes == NULL || k5_utf16le_to_utf8(bytes, actual_count * 2, out) != 0)
return EINVAL;
if (actual_count % 2 == 1)
(void)k5_input_get_uint16_le(in);
return 0;
}
static krb5_error_code
enc_wchar_pointer(const char *utf8, struct encoded_wchars *encoded_out)
{
krb5_error_code ret;
struct k5buf b;
size_t utf16len, num_wchars;
uint8_t *utf16;
ret = k5_utf8_to_utf16le(utf8, &utf16, &utf16len);
if (ret)
return ret;
num_wchars = utf16len / 2;
k5_buf_init_dynamic(&b);
k5_buf_add_uint32_le(&b, num_wchars + 1);
k5_buf_add_uint32_le(&b, 0);
k5_buf_add_uint32_le(&b, num_wchars);
k5_buf_add_len(&b, utf16, utf16len);
free(utf16);
if (num_wchars % 2 == 1)
k5_buf_add_uint16_le(&b, 0);
ret = k5_buf_status(&b);
if (ret)
return ret;
encoded_out->bytes_len = b.len;
encoded_out->num_wchars = num_wchars;
encoded_out->encoded = b.data;
return 0;
}
krb5_error_code
ndr_dec_delegation_info(krb5_data *data, struct pac_s4u_delegation_info **out)
{
krb5_error_code ret;
struct pac_s4u_delegation_info *di = NULL;
struct k5input in;
uint32_t i, object_buffer_length, nservices;
uint8_t version, endianness, common_header_length;
*out = NULL;
di = k5alloc(sizeof(*di), &ret);
if (di == NULL)
return ret;
k5_input_init(&in, data->data, data->length);
version = k5_input_get_byte(&in);
endianness = k5_input_get_byte(&in);
common_header_length = k5_input_get_uint16_le(&in);
(void)k5_input_get_uint32_le(&in);
if (version != 1 || endianness != 0x10 || common_header_length != 8) {
ret = EINVAL;
goto error;
}
object_buffer_length = k5_input_get_uint32_le(&in);
if (data->length < 16 || object_buffer_length != data->length - 16) {
ret = EINVAL;
goto error;
}
(void)k5_input_get_uint32_le(&in);
(void)k5_input_get_uint32_le(&in);
(void)k5_input_get_uint16_le(&in);
(void)k5_input_get_uint16_le(&in);
(void)k5_input_get_uint32_le(&in);
(void)k5_input_get_uint32_le(&in);
(void)k5_input_get_uint32_le(&in);
ret = dec_wchar_pointer(&in, &di->proxy_target);
if (ret)
goto error;
nservices = k5_input_get_uint32_le(&in);
if (nservices > data->length / 8) {
ret = ERANGE;
goto error;
}
(void)k5_input_get_bytes(&in, 8 * nservices);
di->transited_services = k5calloc(nservices + 1, sizeof(char *), &ret);
if (di->transited_services == NULL)
goto error;
for (i = 0; i < nservices; i++) {
ret = dec_wchar_pointer(&in, &di->transited_services[i]);
if (ret)
goto error;
di->transited_services_length++;
}
ret = in.status;
if (ret)
goto error;
*out = di;
return 0;
error:
ndr_free_delegation_info(di);
return ret;
}
static inline void
write_ptr(struct k5buf *buf, uint32_t *pointer)
{
if (*pointer == 0)
*pointer = 0x00020000;
k5_buf_add_uint32_le(buf, *pointer);
*pointer += 4;
}
krb5_error_code
ndr_enc_delegation_info(struct pac_s4u_delegation_info *in, krb5_data *out)
{
krb5_error_code ret;
size_t i;
struct k5buf b = EMPTY_K5BUF;
struct encoded_wchars pt_encoded = { 0 }, *tss_encoded = NULL;
uint32_t pointer = 0;
ret = enc_wchar_pointer(in->proxy_target, &pt_encoded);
if (ret)
goto cleanup;
tss_encoded = k5calloc(in->transited_services_length, sizeof(*tss_encoded),
&ret);
if (tss_encoded == NULL)
goto cleanup;
k5_buf_init_dynamic(&b);
k5_buf_add_len(&b, "\x01\x10\x08\x00", 4);
k5_buf_add_uint32_le(&b, 0xcccccccc);
k5_buf_add_uint32_le(&b, 0);
k5_buf_add_uint32_le(&b, 0);
write_ptr(&b, &pointer);
k5_buf_add_uint16_le(&b, 2 * pt_encoded.num_wchars);
k5_buf_add_uint16_le(&b, 2 * (pt_encoded.num_wchars + 1));
write_ptr(&b, &pointer);
k5_buf_add_uint32_le(&b, in->transited_services_length);
write_ptr(&b, &pointer);
k5_buf_add_len(&b, pt_encoded.encoded, pt_encoded.bytes_len);
k5_buf_add_uint32_le(&b, in->transited_services_length);
for (i = 0; i < in->transited_services_length; i++) {
ret = enc_wchar_pointer(in->transited_services[i], &tss_encoded[i]);
if (ret)
goto cleanup;
k5_buf_add_uint16_le(&b, 2 * tss_encoded[i].num_wchars);
k5_buf_add_uint16_le(&b, 2 * (tss_encoded[i].num_wchars + 1));
write_ptr(&b, &pointer);
}
for (i = 0; i < in->transited_services_length; i++)
k5_buf_add_len(&b, tss_encoded[i].encoded, tss_encoded[i].bytes_len);
if (b.len % 8 != 0)
k5_buf_add_uint32_le(&b, 0);
if (b.data != NULL)
store_32_le(b.len - 0x10, ((uint8_t *)b.data) + 8);
ret = k5_buf_status(&b);
if (ret)
goto cleanup;
*out = make_data(b.data, b.len);
b.data = NULL;
cleanup:
free(b.data);
free(pt_encoded.encoded);
for (i = 0; tss_encoded != NULL && i < in->transited_services_length; i++)
free(tss_encoded[i].encoded);
free(tss_encoded);
return ret;
}
void
ndr_free_delegation_info(struct pac_s4u_delegation_info *di)
{
uint32_t i;
if (di == NULL)
return;
free(di->proxy_target);
for (i = 0; i < di->transited_services_length; i++)
free(di->transited_services[i]);
free(di->transited_services);
free(di);
}