diff --git a/src/libstrongswan/tests/suites/test_identification.c b/src/libstrongswan/tests/suites/test_identification.c index b5afa00fe..c0a21fe34 100644 --- a/src/libstrongswan/tests/suites/test_identification.c +++ b/src/libstrongswan/tests/suites/test_identification.c @@ -1,7 +1,8 @@ /* * Copyright (C) 2013-2015 Tobias Brunner + * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2009 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -122,67 +123,122 @@ static struct { } data; } result; } string_data[] = { - {NULL, ID_ANY, { .type = ENC_CHUNK }}, - {"", ID_ANY, { .type = ENC_CHUNK }}, - {"%any", ID_ANY, { .type = ENC_CHUNK }}, - {"%any6", ID_ANY, { .type = ENC_CHUNK }}, - {"0.0.0.0", ID_ANY, { .type = ENC_CHUNK }}, - {"0::0", ID_ANY, { .type = ENC_CHUNK }}, - {"::", ID_ANY, { .type = ENC_CHUNK }}, - {"*", ID_ANY, { .type = ENC_CHUNK }}, - {"any", ID_FQDN, { .type = ENC_SIMPLE }}, - {"any6", ID_FQDN, { .type = ENC_SIMPLE }}, - {"0", ID_FQDN, { .type = ENC_SIMPLE }}, - {"**", ID_FQDN, { .type = ENC_SIMPLE }}, - {"192.168.1.1", ID_IPV4_ADDR, { .type = ENC_CHUNK, + {NULL, ID_ANY, { .type = ENC_CHUNK }}, + {"", ID_ANY, { .type = ENC_CHUNK }}, + {"%any", ID_ANY, { .type = ENC_CHUNK }}, + {"%any6", ID_ANY, { .type = ENC_CHUNK }}, + {"0.0.0.0", ID_ANY, { .type = ENC_CHUNK }}, + {"0::0", ID_ANY, { .type = ENC_CHUNK }}, + {"::", ID_ANY, { .type = ENC_CHUNK }}, + {"*", ID_ANY, { .type = ENC_CHUNK }}, + {"any", ID_FQDN, { .type = ENC_SIMPLE }}, + {"any6", ID_FQDN, { .type = ENC_SIMPLE }}, + {"0", ID_FQDN, { .type = ENC_SIMPLE }}, + {"**", ID_FQDN, { .type = ENC_SIMPLE }}, + {"192.168.1.1", ID_IPV4_ADDR, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01) }}, - {"192.168.", ID_FQDN, { .type = ENC_SIMPLE }}, - {".", ID_FQDN, { .type = ENC_SIMPLE }}, - {"fec0::1", ID_IPV6_ADDR, { .type = ENC_CHUNK, + {"192.168.", ID_FQDN, { .type = ENC_SIMPLE }}, + {".", ID_FQDN, { .type = ENC_SIMPLE }}, + {"192.168.1.1/33", ID_FQDN, { .type = ENC_SIMPLE }}, + {"192.168.1.1/32", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01,0xff,0xff,0xff,0xff) }}, + {"192.168.1.1/31", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x00,0xff,0xff,0xff,0xfe) }}, + {"192.168.1.8/30", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x08,0xff,0xff,0xff,0xfc) }}, + {"192.168.1.128/25", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x80,0xff,0xff,0xff,0x80) }}, + {"192.168.1.0/24", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x00,0xff,0xff,0xff,0x00) }}, + {"192.168.1.0/23", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x00,0x00,0xff,0xff,0xfe,0x00) }}, + {"192.168.4.0/22", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x04,0x00,0xff,0xff,0xfc,0x00) }}, + {"0.0.0.0/0", ID_IPV4_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) }}, + {"192.168.1.0-192.168.1.40",ID_IPV4_ADDR_RANGE, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x00,0xc0,0xa8,0x01,0x28) }}, + {"0.0.0.0-255.255.255.255", ID_IPV4_ADDR_RANGE, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff) }}, + {"192.168.1.40-192.168.1.0",ID_FQDN, { .type = ENC_SIMPLE }}, + {"fec0::1", ID_IPV6_ADDR, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01) }}, - {"fec0::", ID_IPV6_ADDR, { .type = ENC_CHUNK, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01) }}, + {"fec0::", ID_IPV6_ADDR, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) }}, - {"fec0:", ID_KEY_ID, { .type = ENC_SIMPLE }}, - {":", ID_KEY_ID, { .type = ENC_SIMPLE }}, - {"alice@strongswan.org", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, - {"alice@strongswan", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, - {"alice@", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, - {"alice", ID_FQDN, { .type = ENC_SIMPLE }}, - {"@", ID_FQDN, { .type = ENC_CHUNK }}, - {" @", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, - {"@strongswan.org", ID_FQDN, { .type = ENC_STRING, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00) }}, + {"fec0:", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {":", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"fec0::1/129", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"fec0::1/128", ID_IPV6_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff ) }}, + {"fec0::1/127", ID_IPV6_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe ) }}, + {"fec0::4/126", ID_IPV6_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc ) }}, + {"fec0::100/120", ID_IPV6_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 ) }}, + {"::/0", ID_IPV6_ADDR_SUBNET, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ) }}, + {"fec0::1-fec0::4fff", ID_IPV6_ADDR_RANGE, { .type = ENC_CHUNK, + .data.c = chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0xff ) }}, + {"fec0::4fff-fec0::1", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"fec0::1-", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"alice@strongswan.org", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, + {"alice@strongswan", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, + {"alice@", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, + {"alice", ID_FQDN, { .type = ENC_SIMPLE }}, + {"@", ID_FQDN, { .type = ENC_CHUNK }}, + {" @", ID_RFC822_ADDR, { .type = ENC_SIMPLE }}, + {"@strongswan.org", ID_FQDN, { .type = ENC_STRING, .data.s = "strongswan.org" }}, - {"@#deadbeef", ID_KEY_ID, { .type = ENC_CHUNK, + {"@#deadbeef", ID_KEY_ID, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0xde,0xad,0xbe,0xef) }}, - {"@#deadbee", ID_KEY_ID, { .type = ENC_CHUNK, + {"@#deadbee", ID_KEY_ID, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0x0d,0xea,0xdb,0xee) }}, - {"foo=bar", ID_KEY_ID, { .type = ENC_SIMPLE }}, - {"foo=", ID_KEY_ID, { .type = ENC_SIMPLE }}, - {"=bar", ID_KEY_ID, { .type = ENC_SIMPLE }}, - {"C=", ID_DER_ASN1_DN, { .type = ENC_CHUNK, + {"foo=bar", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"foo=", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"=bar", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"C=", ID_DER_ASN1_DN, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0x30,0x0b,0x31,0x09,0x30,0x07,0x06, 0x03,0x55,0x04,0x06,0x13,0x00) }}, - {"C=CH", ID_DER_ASN1_DN, { .type = ENC_CHUNK, + {"C=CH", ID_DER_ASN1_DN, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0x30,0x0d,0x31,0x0b,0x30,0x09,0x06, 0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48) }}, - {"C=CH,", ID_DER_ASN1_DN, { .type = ENC_CHUNK, + {"C=CH,", ID_DER_ASN1_DN, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0x30,0x0d,0x31,0x0b,0x30,0x09,0x06, 0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48) }}, - {"C=CH, ", ID_DER_ASN1_DN, { .type = ENC_CHUNK, + {"C=CH, ", ID_DER_ASN1_DN, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0x30,0x0d,0x31,0x0b,0x30,0x09,0x06, 0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48) }}, - {"C=CH, O", ID_KEY_ID, { .type = ENC_SIMPLE }}, - {"IPv4:#c0a80101", ID_IPV4_ADDR, { .type = ENC_CHUNK, + {"C=CH, O", ID_KEY_ID, { .type = ENC_SIMPLE }}, + {"IPv4:#c0a80101", ID_IPV4_ADDR, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01) }}, - { "email:tester", ID_RFC822_ADDR, { .type = ENC_STRING, + { "email:tester", ID_RFC822_ADDR, { .type = ENC_STRING, .data.s = "tester" }}, - { "{1}:#c0a80101", ID_IPV4_ADDR, { .type = ENC_CHUNK, + { "{1}:#c0a80101", ID_IPV4_ADDR, { .type = ENC_CHUNK, .data.c = chunk_from_chars(0xc0,0xa8,0x01,0x01) }}, - { "{0x02}:tester", ID_FQDN, { .type = ENC_STRING, + { "{0x02}:tester", ID_FQDN, { .type = ENC_STRING, .data.s = "tester" }}, - { "{99}:somedata", 99, { .type = ENC_STRING, + { "{99}:somedata", 99, { .type = ENC_STRING, .data.s = "somedata" }}, }; @@ -264,14 +320,33 @@ START_TEST(test_printf_hook) string_equals("192.168.1.1", "192.168.1.1"); string_equals_id("(invalid ID_IPV4_ADDR)", - identification_create_from_encoding(ID_IPV4_ADDR, chunk_empty)); + identification_create_from_encoding(ID_IPV4_ADDR, chunk_empty)); + string_equals("192.168.1.1/32", "192.168.1.1/32"); + string_equals("192.168.1.2/31", "192.168.1.2/31"); + string_equals("192.168.1.0/24", "192.168.1.0/24"); + string_equals("192.168.2.0/23", "192.168.2.0/23"); + string_equals("0.0.0.0/0", "0.0.0.0/0"); + string_equals_id("(invalid ID_IPV4_ADDR_SUBNET)", + identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty)); + string_equals("192.168.1.1-192.168.1.254", "192.168.1.1-192.168.1.254"); + string_equals("0.0.0.0-255.255.255.255", "0.0.0.0-255.255.255.255"); + string_equals_id("(invalid ID_IPV4_ADDR_RANGE)", + identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty)); string_equals("fec0::1", "fec0::1"); string_equals("fec0::1", "fec0:0:0::1"); string_equals_id("(invalid ID_IPV6_ADDR)", - identification_create_from_encoding(ID_IPV6_ADDR, chunk_empty)); - + identification_create_from_encoding(ID_IPV6_ADDR, chunk_empty)); + string_equals("fec0::1/128", "fec0::1/128"); + string_equals("fec0::2/127", "fec0::2/127"); + string_equals("fec0::100/120", "fec0::100/120"); + string_equals("::/0", "::/0"); + string_equals_id("(invalid ID_IPV6_ADDR_SUBNET)", + identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty)); + string_equals("fec0::1-fec0::4fff", "fec0::1-fec0::4fff"); + string_equals_id("(invalid ID_IPV6_ADDR_RANGE)", + identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty)); string_equals_id("(unknown ID type: 255)", - identification_create_from_encoding(255, chunk_empty)); + identification_create_from_encoding(255, chunk_empty)); string_equals("moon@strongswan.org", "moon@strongswan.org"); string_equals("MOON@STRONGSWAN.ORG", "MOON@STRONGSWAN.ORG"); @@ -595,6 +670,89 @@ START_TEST(test_matches_binary) } END_TEST +START_TEST(test_matches_range) +{ + identification_t *a, *b; + + /* IPv4 addresses */ + a = identification_create_from_string("192.168.1.1"); + ck_assert(a->get_type(a) == ID_IPV4_ADDR); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.0/32", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.64", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.2-192.168.1.64", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.0.240-192.168.1.0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + + /* Malformed IPv4 subnet and range encoding */ + b = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, + chunk_from_chars(0xc0,0xa8,0x01,0x28,0xc0,0xa8,0x01,0x00)); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + + a->destroy(a); + + /* IPv6 addresses */ + a = identification_create_from_string("fec0::1"); + ck_assert(a->get_type(a) == ID_IPV6_ADDR); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/120", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::100/120", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1-fec0::1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::0-fec0::5", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::4001-fec0::4ffe", ID_MATCH_NONE)); + ck_assert(id_matches(a, "feb0::1-fec0::0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + + /* Malformed IPv6 subnet and range encoding */ + b = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, + chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0xff, + 0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 )); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + + a->destroy(a); + + /* Malformed IPv4 address encoding */ + a = identification_create_from_encoding(ID_IPV4_ADDR, chunk_empty); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "0.0.0.0-255.255.255.255", ID_MATCH_NONE)); + a->destroy(a); + + /* Malformed IPv6 address encoding */ + a = identification_create_from_encoding(ID_IPV6_ADDR, chunk_empty); + ck_assert(id_matches(a, "::/0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", ID_MATCH_NONE)); + a->destroy(a); +} +END_TEST + START_TEST(test_matches_string) { identification_t *a; @@ -929,6 +1087,7 @@ Suite *identification_suite_create() tcase_add_test(tc, test_matches); tcase_add_test(tc, test_matches_any); tcase_add_test(tc, test_matches_binary); + tcase_add_test(tc, test_matches_range); tcase_add_test(tc, test_matches_string); tcase_add_loop_test(tc, test_matches_empty, ID_ANY, ID_KEY_ID + 1); tcase_add_loop_test(tc, test_matches_empty_reverse, ID_ANY, ID_KEY_ID + 1); diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index 1b0d05517..98424586f 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -1,8 +1,9 @@ /* + * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2009-2015 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -823,6 +824,154 @@ METHOD(identification_t, matches_dn, id_match_t, return ID_MATCH_NONE; } +/** + * Transform netmask to CIDR bits + */ +static int netmask_to_cidr(char *netmask, size_t address_size) +{ + uint8_t byte; + int i, netbits = 0; + + for (i = 0; i < address_size; i++) + { + byte = netmask[i]; + + if (byte == 0x00) + { + break; + } + if (byte == 0xff) + { + netbits += 8; + } + else + { + while (byte & 0x80) + { + netbits++; + byte <<= 1; + } + } + } + return netbits; +} + +METHOD(identification_t, matches_range, id_match_t, + private_identification_t *this, identification_t *other) +{ + chunk_t other_encoding; + uint8_t *address, *from, *to, *network, *netmask; + size_t address_size = 0; + int netbits, range_sign, i; + + if (other->get_type(other) == ID_ANY) + { + return ID_MATCH_ANY; + } + if (this->type == other->get_type(other) && + chunk_equals(this->encoded, other->get_encoding(other))) + { + return ID_MATCH_PERFECT; + } + if ((this->type == ID_IPV4_ADDR && + other->get_type(other) == ID_IPV4_ADDR_SUBNET)) + { + address_size = sizeof(struct in_addr); + } + else if ((this->type == ID_IPV6_ADDR && + other->get_type(other) == ID_IPV6_ADDR_SUBNET)) + { + address_size = sizeof(struct in6_addr); + } + if (address_size) + { + other_encoding = other->get_encoding(other); + if (this->encoded.len != address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + address = this->encoded.ptr; + network = other_encoding.ptr; + netmask = other_encoding.ptr + address_size; + netbits = netmask_to_cidr(netmask, address_size); + + if (netbits == 0) + { + return ID_MATCH_MAX_WILDCARDS; + } + if (netbits == 8 * address_size) + { + return memeq(address, network, address_size) ? + ID_MATCH_PERFECT : ID_MATCH_NONE; + } + for (i = 0; i < (netbits + 7)/8; i++) + { + if ((address[i] ^ network[i]) & netmask[i]) + { + return ID_MATCH_NONE; + } + } + return ID_MATCH_ONE_WILDCARD; + } + if ((this->type == ID_IPV4_ADDR && + other->get_type(other) == ID_IPV4_ADDR_RANGE)) + { + address_size = sizeof(struct in_addr); + } + else if ((this->type == ID_IPV6_ADDR && + other->get_type(other) == ID_IPV6_ADDR_RANGE)) + { + address_size = sizeof(struct in6_addr); + } + if (address_size) + { + other_encoding = other->get_encoding(other); + if (this->encoded.len != address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + address = this->encoded.ptr; + from = other_encoding.ptr; + to = other_encoding.ptr + address_size; + + range_sign = memcmp(to, from, address_size); + if (range_sign < 0) + { /* to is smaller than from */ + return ID_MATCH_NONE; + } + + /* check lower bound */ + for (i = 0; i < address_size; i++) + { + if (address[i] != from[i]) + { + if (address[i] < from[i]) + { + return ID_MATCH_NONE; + } + break; + } + } + + /* check upper bound */ + for (i = 0; i < address_size; i++) + { + if (address[i] != to[i]) + { + if (address[i] > to[i]) + { + return ID_MATCH_NONE; + } + break; + } + } + return range_sign ? ID_MATCH_ONE_WILDCARD : ID_MATCH_PERFECT; + } + return ID_MATCH_NONE; +} + /** * Described in header. */ @@ -831,7 +980,9 @@ int identification_printf_hook(printf_hook_data_t *data, { private_identification_t *this = *((private_identification_t**)(args[0])); chunk_t proper; - char buf[512]; + char buf[BUF_LEN]; + char *pos; + size_t written, len, address_size; if (this == NULL) { @@ -841,49 +992,115 @@ int identification_printf_hook(printf_hook_data_t *data, switch (this->type) { case ID_ANY: - snprintf(buf, sizeof(buf), "%%any"); + snprintf(buf, BUF_LEN, "%%any"); break; case ID_IPV4_ADDR: if (this->encoded.len < sizeof(struct in_addr) || - inet_ntop(AF_INET, this->encoded.ptr, buf, sizeof(buf)) == NULL) + inet_ntop(AF_INET, this->encoded.ptr, buf, BUF_LEN) == NULL) { - snprintf(buf, sizeof(buf), "(invalid ID_IPV4_ADDR)"); + snprintf(buf, BUF_LEN, "(invalid ID_IPV4_ADDR)"); + } + break; + case ID_IPV4_ADDR_SUBNET: + address_size = sizeof(struct in_addr); + if (this->encoded.len < 2 * address_size || + inet_ntop(AF_INET, this->encoded.ptr, buf, BUF_LEN) == NULL) + { + snprintf(buf, BUF_LEN, "(invalid ID_IPV4_ADDR_SUBNET)"); + break; + } + written = strlen(buf); + snprintf(buf + written, BUF_LEN - written, "/%d", + netmask_to_cidr(this->encoded.ptr + address_size, + address_size)); + break; + case ID_IPV4_ADDR_RANGE: + address_size = sizeof(struct in_addr); + if (this->encoded.len < 2 * address_size || + inet_ntop(AF_INET, this->encoded.ptr, buf, BUF_LEN) == NULL) + { + snprintf(buf, BUF_LEN, "(invalid ID_IPV4_ADDR_RANGE)"); + break; + } + written = strlen(buf); + pos = buf + written; + len = BUF_LEN - written; + written = snprintf(pos, len, "-"); + if (written < 0 || written >= len || + inet_ntop(AF_INET, this->encoded.ptr + address_size, + pos + written, len - written) == NULL) + { + snprintf(buf, BUF_LEN, "(invalid ID_IPV4_ADDR_RANGE)"); } break; case ID_IPV6_ADDR: if (this->encoded.len < sizeof(struct in6_addr) || - inet_ntop(AF_INET6, this->encoded.ptr, buf, INET6_ADDRSTRLEN) == NULL) + inet_ntop(AF_INET6, this->encoded.ptr, buf, BUF_LEN) == NULL) { - snprintf(buf, sizeof(buf), "(invalid ID_IPV6_ADDR)"); + snprintf(buf, BUF_LEN, "(invalid ID_IPV6_ADDR)"); + } + break; + case ID_IPV6_ADDR_SUBNET: + address_size = sizeof(struct in6_addr); + if (this->encoded.len < 2 * address_size || + inet_ntop(AF_INET6, this->encoded.ptr, buf, BUF_LEN) == NULL) + { + snprintf(buf, BUF_LEN, "(invalid ID_IPV6_ADDR_SUBNET)"); + } + else + { + written = strlen(buf); + snprintf(buf + written, BUF_LEN - written, "/%d", + netmask_to_cidr(this->encoded.ptr + address_size, + address_size)); + } + break; + case ID_IPV6_ADDR_RANGE: + address_size = sizeof(struct in6_addr); + if (this->encoded.len < 2 * address_size || + inet_ntop(AF_INET6, this->encoded.ptr, buf, BUF_LEN) == NULL) + { + snprintf(buf, BUF_LEN, "(invalid ID_IPV6_ADDR_RANGE)"); + break; + } + written = strlen(buf); + pos = buf + written; + len = BUF_LEN - written; + written = snprintf(pos, len, "-"); + if (written < 0 || written >= len || + inet_ntop(AF_INET6, this->encoded.ptr + address_size, + pos + written, len - written) == NULL) + { + snprintf(buf, BUF_LEN, "(invalid ID_IPV6_ADDR_RANGE)"); } break; case ID_FQDN: case ID_RFC822_ADDR: case ID_DER_ASN1_GN_URI: chunk_printable(this->encoded, &proper, '?'); - snprintf(buf, sizeof(buf), "%.*s", (int)proper.len, proper.ptr); + snprintf(buf, BUF_LEN, "%.*s", (int)proper.len, proper.ptr); chunk_free(&proper); break; case ID_DER_ASN1_DN: - dntoa(this->encoded, buf, sizeof(buf)); + dntoa(this->encoded, buf, BUF_LEN); break; case ID_DER_ASN1_GN: - snprintf(buf, sizeof(buf), "(ASN.1 general name)"); + snprintf(buf, BUF_LEN, "(ASN.1 general name)"); break; case ID_KEY_ID: if (chunk_printable(this->encoded, NULL, '?') && this->encoded.len != HASH_SIZE_SHA1) { /* fully printable, use ascii version */ - snprintf(buf, sizeof(buf), "%.*s", (int)this->encoded.len, + snprintf(buf, BUF_LEN, "%.*s", (int)this->encoded.len, this->encoded.ptr); } else { /* not printable, hex dump */ - snprintf(buf, sizeof(buf), "%#B", &this->encoded); + snprintf(buf, BUF_LEN, "%#B", &this->encoded); } break; default: - snprintf(buf, sizeof(buf), "(unknown ID type: %d)", this->type); + snprintf(buf, BUF_LEN, "(unknown ID type: %d)", this->type); break; } if (spec->minus) @@ -952,6 +1169,13 @@ static private_identification_t *identification_create(id_type_t type) this->public.matches = _matches_dn; this->public.contains_wildcards = _contains_wildcards_dn; break; + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + this->public.hash = _hash_binary; + this->public.equals = _equals_binary; + this->public.matches = _matches_range; + this->public.contains_wildcards = return_false; + break; default: this->public.hash = _hash_binary; this->public.equals = _equals_binary; @@ -973,6 +1197,10 @@ static private_identification_t* create_from_string_with_prefix_type(char *str) } prefixes[] = { { "ipv4:", ID_IPV4_ADDR }, { "ipv6:", ID_IPV6_ADDR }, + { "ipv4net:", ID_IPV4_ADDR_SUBNET }, + { "ipv6net:", ID_IPV6_ADDR_SUBNET }, + { "ipv4range:", ID_IPV4_ADDR_RANGE }, + { "ipv6range:", ID_IPV6_ADDR_RANGE }, { "rfc822:", ID_RFC822_ADDR }, { "email:", ID_RFC822_ADDR }, { "userfqdn:", ID_USER_FQDN }, @@ -1038,6 +1266,115 @@ static private_identification_t* create_from_string_with_num_type(char *str) return this; } +/** + * Convert to an IPv4/IPv6 host address, subnet or address range + */ +static private_identification_t* create_ip_address_from_string(char *string, + bool is_ipv4) +{ + private_identification_t *this; + uint8_t encoding[32]; + uint8_t *str, *pos, *address, *to_address, *netmask; + size_t address_size; + int bits, bytes, i; + bool has_subnet = FALSE, has_range = FALSE; + + address = encoding; + address_size = is_ipv4 ? sizeof(struct in_addr) : sizeof(struct in6_addr); + + str = strdup(string); + pos = strchr(str, '/'); + if (pos) + { /* separate IP address from optional netmask */ + + *pos = '\0'; + has_subnet = TRUE; + } + else + { + pos = strchr(str, '-'); + if (pos) + { /* separate lower address from upper address of IP range */ + *pos = '\0'; + has_range = TRUE; + } + } + + if (inet_pton(is_ipv4 ? AF_INET : AF_INET6, str, address) != 1) + { + free(str); + return NULL; + } + + if (has_subnet) + { /* is IP subnet */ + bits = atoi(pos + 1); + if (bits > 8 * address_size) + { + free(str); + return NULL; + } + bytes = bits / 8; + bits -= 8 * bytes; + netmask = encoding + address_size; + + for (i = 0; i < address_size; i++) + { + if (bytes) + { + *netmask = 0xff; + bytes--; + } + else if (bits) + { + *netmask = 0xff << (8 - bits); + bits = 0; + } + else + { + *netmask = 0x00; + } + *address++ &= *netmask++; + } + this = identification_create(is_ipv4 ? ID_IPV4_ADDR_SUBNET : + ID_IPV6_ADDR_SUBNET); + this->encoded = chunk_clone(chunk_create(encoding, 2 * address_size)); + } + else if (has_range) + { /* is IP range */ + to_address = encoding + address_size; + + if (inet_pton(is_ipv4 ? AF_INET : AF_INET6, pos + 1, to_address) != 1) + { + free(str); + return NULL; + } + for (i = 0; i < address_size; i++) + { + if (address[i] != to_address[i]) + { + if (address[i] > to_address[i]) + { + free(str); + return NULL; + } + break; + } + } + this = identification_create(is_ipv4 ? ID_IPV4_ADDR_RANGE : + ID_IPV6_ADDR_RANGE); + this->encoded = chunk_clone(chunk_create(encoding, 2 * address_size)); + } + else + { /* is IP host address */ + this = identification_create(is_ipv4 ? ID_IPV4_ADDR : ID_IPV6_ADDR); + this->encoded = chunk_clone(chunk_create(encoding, address_size)); + } + free(str); + + return this; +} + /* * Described in header. */ @@ -1095,15 +1432,9 @@ identification_t *identification_create_from_string(char *string) { if (strchr(string, ':') == NULL) { - struct in_addr address; - chunk_t chunk = {(void*)&address, sizeof(address)}; - - if (inet_pton(AF_INET, string, &address) > 0) - { /* is IPv4 */ - this = identification_create(ID_IPV4_ADDR); - this->encoded = chunk_clone(chunk); - } - else + /* IPv4 address or subnet */ + this = create_ip_address_from_string(string, TRUE); + if (!this) { /* not IPv4, mostly FQDN */ this = identification_create(ID_FQDN); this->encoded = chunk_from_str(strdup(string)); @@ -1112,15 +1443,9 @@ identification_t *identification_create_from_string(char *string) } else { - struct in6_addr address; - chunk_t chunk = {(void*)&address, sizeof(address)}; - - if (inet_pton(AF_INET6, string, &address) > 0) - { /* is IPv6 */ - this = identification_create(ID_IPV6_ADDR); - this->encoded = chunk_clone(chunk); - } - else + /* IPv6 address or subnet */ + this = create_ip_address_from_string(string, FALSE); + if (!this) { /* not IPv4/6 fallback to KEY_ID */ this = identification_create(ID_KEY_ID); this->encoded = chunk_from_str(strdup(string));