Merge branch 'unknown-transform-types'

This changes how unknown transform types are handled in proposals.  In
particular we make sure not to accept a proposal if it contains unknown
transform types (they were just ignored previously, which could have
resulted in an invalid selected proposal).

Fixes #2557.
This commit is contained in:
Tobias Brunner 2018-03-07 14:25:48 +01:00
commit b53eb857bf
4 changed files with 297 additions and 54 deletions

View File

@ -57,6 +57,11 @@ struct private_proposal_t {
*/
array_t *transforms;
/**
* Types of transforms contained, as transform_type_t
*/
array_t *types;
/**
* senders SPI
*/
@ -68,6 +73,101 @@ struct private_proposal_t {
u_int number;
};
/**
* This is a hack to not change the previous order when printing proposals
*/
static transform_type_t type_for_sort(const void *type)
{
const transform_type_t *t = type;
switch (*t)
{
case PSEUDO_RANDOM_FUNCTION:
return INTEGRITY_ALGORITHM;
case INTEGRITY_ALGORITHM:
return PSEUDO_RANDOM_FUNCTION;
default:
return *t;
}
}
/**
* Sort transform types
*/
static int type_sort(const void *a, const void *b, void *user)
{
transform_type_t ta = type_for_sort(a), tb = type_for_sort(b);
return ta - tb;
}
/**
* Find a transform type
*/
static int type_find(const void *a, const void *b)
{
return type_sort(a, b, NULL);
}
/**
* Check if the given transform type is already in the set
*/
static bool contains_type(array_t *types, transform_type_t type)
{
return array_bsearch(types, &type, type_find, NULL) != -1;
}
/**
* Add the given transform type to the set
*/
static void add_type(array_t *types, transform_type_t type)
{
if (!contains_type(types, type))
{
array_insert(types, ARRAY_TAIL, &type);
array_sort(types, type_sort, NULL);
}
}
/**
* Merge two sets of transform types into a new array
*/
static array_t *merge_types(private_proposal_t *this, private_proposal_t *other)
{
array_t *types;
transform_type_t type;
int i, count;
count = max(array_count(this->types), array_count(other->types));
types = array_create(sizeof(transform_type_t), count);
for (i = 0; i < count; i++)
{
if (array_get(this->types, i, &type))
{
add_type(types, type);
}
if (array_get(other->types, i, &type))
{
add_type(types, type);
}
}
return types;
}
/**
* Remove the given transform type from the set
*/
static void remove_type(private_proposal_t *this, transform_type_t type)
{
int i;
i = array_bsearch(this->types, &type, type_find, NULL);
if (i >= 0)
{
array_remove(this->types, i, NULL);
}
}
/**
* Struct used to store different kinds of algorithms.
*/
@ -91,6 +191,7 @@ METHOD(proposal_t, add_algorithm, void,
};
array_insert(this->transforms, ARRAY_TAIL, &entry);
add_type(this->types, type);
}
CALLBACK(alg_filter, bool,
@ -206,17 +307,29 @@ METHOD(proposal_t, strip_dh, void,
{
enumerator_t *enumerator;
entry_t *entry;
bool found = FALSE;
enumerator = array_create_enumerator(this->transforms);
while (enumerator->enumerate(enumerator, &entry))
{
if (entry->type == DIFFIE_HELLMAN_GROUP &&
entry->alg != keep)
if (entry->type == DIFFIE_HELLMAN_GROUP)
{
array_remove_at(this->transforms, enumerator);
if (entry->alg != keep)
{
array_remove_at(this->transforms, enumerator);
}
else
{
found = TRUE;
}
}
}
enumerator->destroy(enumerator);
if (keep == MODP_NONE || !found)
{
remove_type(this, DIFFIE_HELLMAN_GROUP);
}
}
/**
@ -310,6 +423,9 @@ METHOD(proposal_t, select_proposal, proposal_t*,
bool private)
{
proposal_t *selected;
transform_type_t type;
array_t *types;
int i;
DBG2(DBG_CFG, "selecting proposal:");
@ -328,18 +444,20 @@ METHOD(proposal_t, select_proposal, proposal_t*,
{
selected = proposal_create(this->protocol, this->number);
selected->set_spi(selected, this->spi);
}
if (!select_algo(this, other, selected, ENCRYPTION_ALGORITHM, private) ||
!select_algo(this, other, selected, PSEUDO_RANDOM_FUNCTION, private) ||
!select_algo(this, other, selected, INTEGRITY_ALGORITHM, private) ||
!select_algo(this, other, selected, DIFFIE_HELLMAN_GROUP, private) ||
!select_algo(this, other, selected, EXTENDED_SEQUENCE_NUMBERS, private))
types = merge_types(this, (private_proposal_t*)other);
for (i = 0; i < array_count(types); i++)
{
selected->destroy(selected);
return NULL;
array_get(types, i, &type);
if (!select_algo(this, other, selected, type, private))
{
selected->destroy(selected);
array_destroy(types);
return NULL;
}
}
array_destroy(types);
DBG2(DBG_CFG, " proposal matches");
return selected;
@ -409,16 +527,27 @@ METHOD(proposal_t, get_number, u_int,
METHOD(proposal_t, equals, bool,
private_proposal_t *this, proposal_t *other)
{
transform_type_t type;
array_t *types;
int i;
if (&this->public == other)
{
return TRUE;
}
return (
algo_list_equals(this, other, ENCRYPTION_ALGORITHM) &&
algo_list_equals(this, other, INTEGRITY_ALGORITHM) &&
algo_list_equals(this, other, PSEUDO_RANDOM_FUNCTION) &&
algo_list_equals(this, other, DIFFIE_HELLMAN_GROUP) &&
algo_list_equals(this, other, EXTENDED_SEQUENCE_NUMBERS));
types = merge_types(this, (private_proposal_t*)other);
for (i = 0; i < array_count(types); i++)
{
array_get(types, i, &type);
if (!algo_list_equals(this, other, type))
{
array_destroy(types);
return FALSE;
}
}
array_destroy(types);
return TRUE;
}
METHOD(proposal_t, clone_, proposal_t*,
@ -427,6 +556,7 @@ METHOD(proposal_t, clone_, proposal_t*,
private_proposal_t *clone;
enumerator_t *enumerator;
entry_t *entry;
transform_type_t *type;
clone = (private_proposal_t*)proposal_create(this->protocol, 0);
@ -436,6 +566,12 @@ METHOD(proposal_t, clone_, proposal_t*,
array_insert(clone->transforms, ARRAY_TAIL, entry);
}
enumerator->destroy(enumerator);
enumerator = array_create_enumerator(this->types);
while (enumerator->enumerate(enumerator, &type))
{
array_insert(clone->types, ARRAY_TAIL, type);
}
enumerator->destroy(enumerator);
clone->spi = this->spi;
clone->number = this->number;
@ -479,6 +615,7 @@ static void remove_transform(private_proposal_t *this, transform_type_t type)
}
}
e->destroy(e);
remove_type(this, type);
}
/**
@ -605,6 +742,7 @@ static bool check_proposal(private_proposal_t *this)
}
}
e->destroy(e);
remove_type(this, ENCRYPTION_ALGORITHM);
if (!get_algorithm(this, INTEGRITY_ALGORITHM, NULL, NULL))
{
@ -646,30 +784,44 @@ static bool add_string_algo(private_proposal_t *this, const char *alg)
}
/**
* print all algorithms of a kind to buffer
* Print all algorithms of the given type
*/
static int print_alg(private_proposal_t *this, printf_hook_data_t *data,
u_int kind, void *names, bool *first)
transform_type_t type, bool *first)
{
enumerator_t *enumerator;
size_t written = 0;
uint16_t alg, size;
entry_t *entry;
enum_name_t *names;
enumerator = create_enumerator(this, kind);
while (enumerator->enumerate(enumerator, &alg, &size))
names = transform_get_enum_names(type);
enumerator = array_create_enumerator(this->transforms);
while (enumerator->enumerate(enumerator, &entry))
{
char *prefix = "/";
if (type != entry->type)
{
continue;
}
if (*first)
{
written += print_in_hook(data, "%N", names, alg);
prefix = "";
*first = FALSE;
}
if (names)
{
written += print_in_hook(data, "%s%N", prefix, names, entry->alg);
}
else
{
written += print_in_hook(data, "/%N", names, alg);
written += print_in_hook(data, "%sUNKNOWN_%u_%u", prefix,
entry->type, entry->alg);
}
if (size)
if (entry->key_size)
{
written += print_in_hook(data, "_%u", size);
written += print_in_hook(data, "_%u", entry->key_size);
}
}
enumerator->destroy(enumerator);
@ -685,6 +837,7 @@ int proposal_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
private_proposal_t *this = *((private_proposal_t**)(args[0]));
linked_list_t *list = *((linked_list_t**)(args[0]));
enumerator_t *enumerator;
transform_type_t *type;
size_t written = 0;
bool first = TRUE;
@ -713,16 +866,12 @@ int proposal_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
}
written = print_in_hook(data, "%N:", protocol_id_names, this->protocol);
written += print_alg(this, data, ENCRYPTION_ALGORITHM,
encryption_algorithm_names, &first);
written += print_alg(this, data, INTEGRITY_ALGORITHM,
integrity_algorithm_names, &first);
written += print_alg(this, data, PSEUDO_RANDOM_FUNCTION,
pseudo_random_function_names, &first);
written += print_alg(this, data, DIFFIE_HELLMAN_GROUP,
diffie_hellman_group_names, &first);
written += print_alg(this, data, EXTENDED_SEQUENCE_NUMBERS,
extended_sequence_numbers_names, &first);
enumerator = array_create_enumerator(this->types);
while (enumerator->enumerate(enumerator, &type))
{
written += print_alg(this, data, *type, &first);
}
enumerator->destroy(enumerator);
return written;
}
@ -730,6 +879,7 @@ METHOD(proposal_t, destroy, void,
private_proposal_t *this)
{
array_destroy(this->transforms);
array_destroy(this->types);
free(this);
}
@ -760,6 +910,7 @@ proposal_t *proposal_create(protocol_id_t protocol, u_int number)
.protocol = protocol,
.number = number,
.transforms = array_create(sizeof(entry_t), 0),
.types = array_create(sizeof(transform_type_t), 0),
);
return &this->public;

View File

@ -17,21 +17,20 @@
#include <crypto/hashers/hasher.h>
#include <crypto/rngs/rng.h>
ENUM_BEGIN(transform_type_names, UNDEFINED_TRANSFORM_TYPE, EXTENDED_OUTPUT_FUNCTION,
"UNDEFINED_TRANSFORM_TYPE",
"HASH_ALGORITHM",
"RANDOM_NUMBER_GENERATOR",
"AEAD_ALGORITHM",
"COMPRESSION_ALGORITHM",
"EXTENDED OUTPUT FUNCTION");
ENUM_NEXT(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS,
EXTENDED_OUTPUT_FUNCTION,
ENUM_BEGIN(transform_type_names, ENCRYPTION_ALGORITHM, EXTENDED_SEQUENCE_NUMBERS,
"ENCRYPTION_ALGORITHM",
"PSEUDO_RANDOM_FUNCTION",
"INTEGRITY_ALGORITHM",
"DIFFIE_HELLMAN_GROUP",
"EXTENDED_SEQUENCE_NUMBERS");
ENUM_END(transform_type_names, EXTENDED_SEQUENCE_NUMBERS);
ENUM_NEXT(transform_type_names, HASH_ALGORITHM, EXTENDED_OUTPUT_FUNCTION,
EXTENDED_SEQUENCE_NUMBERS,
"HASH_ALGORITHM",
"RANDOM_NUMBER_GENERATOR",
"AEAD_ALGORITHM",
"COMPRESSION_ALGORITHM",
"EXTENDED OUTPUT FUNCTION");
ENUM_END(transform_type_names, EXTENDED_OUTPUT_FUNCTION);
ENUM(extended_sequence_numbers_names, NO_EXT_SEQ_NUMBERS, EXT_SEQ_NUMBERS,
@ -64,7 +63,6 @@ enum_name_t* transform_get_enum_names(transform_type_t type)
return extended_sequence_numbers_names;
case EXTENDED_OUTPUT_FUNCTION:
return ext_out_function_names;
case UNDEFINED_TRANSFORM_TYPE:
case COMPRESSION_ALGORITHM:
break;
}

View File

@ -29,17 +29,16 @@ typedef enum transform_type_t transform_type_t;
* Type of a transform, as in IKEv2 RFC 3.3.2.
*/
enum transform_type_t {
UNDEFINED_TRANSFORM_TYPE = 241,
HASH_ALGORITHM = 242,
RANDOM_NUMBER_GENERATOR = 243,
AEAD_ALGORITHM = 244,
COMPRESSION_ALGORITHM = 245,
EXTENDED_OUTPUT_FUNCTION = 246,
ENCRYPTION_ALGORITHM = 1,
PSEUDO_RANDOM_FUNCTION = 2,
INTEGRITY_ALGORITHM = 3,
DIFFIE_HELLMAN_GROUP = 4,
EXTENDED_SEQUENCE_NUMBERS = 5
EXTENDED_SEQUENCE_NUMBERS = 5,
HASH_ALGORITHM = 256,
RANDOM_NUMBER_GENERATOR = 257,
AEAD_ALGORITHM = 258,
COMPRESSION_ALGORITHM = 259,
EXTENDED_OUTPUT_FUNCTION = 260,
};
/**

View File

@ -194,6 +194,93 @@ START_TEST(test_promote_dh_group_not_contained)
}
END_TEST
START_TEST(test_unknown_transform_types_print)
{
proposal_t *proposal;
proposal = proposal_create(PROTO_IKE, 0);
proposal->add_algorithm(proposal, 242, 42, 128);
assert_proposal_eq(proposal, "IKE:UNKNOWN_242_42_128");
proposal->destroy(proposal);
proposal = proposal_create_from_string(PROTO_IKE,
"aes128-sha256-ecp256");
proposal->add_algorithm(proposal, 242, 42, 128);
proposal->add_algorithm(proposal, 243, 1, 0);
assert_proposal_eq(proposal, "IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256/UNKNOWN_242_42_128/UNKNOWN_243_1");
proposal->destroy(proposal);
}
END_TEST
START_TEST(test_unknown_transform_types_equals)
{
proposal_t *self, *other;
self = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
other = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
other->add_algorithm(other, 242, 42, 0);
ck_assert(!self->equals(self, other));
ck_assert(!other->equals(other, self));
self->add_algorithm(self, 242, 42, 0);
ck_assert(self->equals(self, other));
ck_assert(other->equals(other, self));
other->destroy(other);
self->destroy(self);
}
END_TEST
START_TEST(test_unknown_transform_types_select_fail)
{
proposal_t *self, *other, *selected;
self = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
other = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
other->add_algorithm(other, 242, 42, 0);
selected = self->select(self, other, TRUE, FALSE);
ck_assert(!selected);
other->destroy(other);
self->destroy(self);
}
END_TEST
START_TEST(test_unknown_transform_types_select_fail_subtype)
{
proposal_t *self, *other, *selected;
self = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
self->add_algorithm(self, 242, 8, 0);
other = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
other->add_algorithm(other, 242, 42, 0);
selected = self->select(self, other, TRUE, FALSE);
ck_assert(!selected);
other->destroy(other);
self->destroy(self);
}
END_TEST
START_TEST(test_unknown_transform_types_select_success)
{
proposal_t *self, *other, *selected;
self = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
self->add_algorithm(self, 242, 42, 128);
other = proposal_create_from_string(PROTO_IKE, "aes128-sha256-ecp256");
other->add_algorithm(other, 242, 42, 128);
other->add_algorithm(other, 242, 1, 0);
selected = self->select(self, other, TRUE, FALSE);
ck_assert(selected);
assert_proposal_eq(selected, "IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256/UNKNOWN_242_42_128");
selected->destroy(selected);
other->destroy(other);
self->destroy(self);
}
END_TEST
Suite *proposal_suite_create()
{
Suite *s;
@ -216,5 +303,13 @@ Suite *proposal_suite_create()
tcase_add_test(tc, test_promote_dh_group_not_contained);
suite_add_tcase(s, tc);
tc = tcase_create("unknown transform types");
tcase_add_test(tc, test_unknown_transform_types_print);
tcase_add_test(tc, test_unknown_transform_types_equals);
tcase_add_test(tc, test_unknown_transform_types_select_fail);
tcase_add_test(tc, test_unknown_transform_types_select_fail_subtype);
tcase_add_test(tc, test_unknown_transform_types_select_success);
suite_add_tcase(s, tc);
return s;
}