diff options
Diffstat (limited to 'src/libcharon/encoding/payloads/proposal_substructure.c')
-rw-r--r-- | src/libcharon/encoding/payloads/proposal_substructure.c | 954 |
1 files changed, 891 insertions, 63 deletions
diff --git a/src/libcharon/encoding/payloads/proposal_substructure.c b/src/libcharon/encoding/payloads/proposal_substructure.c index 4753d574d..3a113c123 100644 --- a/src/libcharon/encoding/payloads/proposal_substructure.c +++ b/src/libcharon/encoding/payloads/proposal_substructure.c @@ -25,7 +25,7 @@ #include <daemon.h> /** - * IKEv1 Value for a proposal payload. + * IKEv2 Value for a proposal payload. */ #define PROPOSAL_TYPE_VALUE 2 @@ -84,16 +84,18 @@ struct private_proposal_substructure_t { /** * Transforms are stored in a linked_list_t. */ - linked_list_t * transforms; + linked_list_t *transforms; + + /** + * Type of this payload, PROPOSAL_SUBSTRUCTURE or PROPOSAL_SUBSTRUCTURE_V1 + */ + payload_type_t type; }; /** - * Encoding rules to parse or generate a Proposal substructure. - * - * The defined offsets are the positions in a object of type - * private_proposal_substructure_t. + * Encoding rules for a IKEv1 Proposal substructure. */ -encoding_rule_t proposal_substructure_encodings[] = { +static encoding_rule_t encodings_v1[] = { /* 1 Byte next payload type, stored in the field next_payload */ { U_INT_8, offsetof(private_proposal_substructure_t, next_payload) }, /* 1 Reserved Byte */ @@ -110,9 +112,34 @@ encoding_rule_t proposal_substructure_encodings[] = { { U_INT_8, offsetof(private_proposal_substructure_t, transforms_count) }, /* SPI is a chunk of variable size*/ { SPI, offsetof(private_proposal_substructure_t, spi) }, - /* Transforms are stored in a transform substructure, - offset points to a linked_list_t pointer */ - { TRANSFORMS, offsetof(private_proposal_substructure_t, transforms) } + /* Transforms are stored in a transform substructure list */ + { PAYLOAD_LIST + TRANSFORM_SUBSTRUCTURE_V1, + offsetof(private_proposal_substructure_t, transforms) }, +}; + +/** + * Encoding rules for a IKEv2 Proposal substructure. + */ +static encoding_rule_t encodings_v2[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_proposal_substructure_t, next_payload) }, + /* 1 Reserved Byte */ + { RESERVED_BYTE, offsetof(private_proposal_substructure_t, reserved) }, + /* Length of the whole proposal substructure payload*/ + { PAYLOAD_LENGTH, offsetof(private_proposal_substructure_t, proposal_length) }, + /* proposal number is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, proposal_number) }, + /* protocol ID is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, protocol_id) }, + /* SPI Size has its own type */ + { SPI_SIZE, offsetof(private_proposal_substructure_t, spi_size) }, + /* Number of transforms is a number of 8 bit */ + { U_INT_8, offsetof(private_proposal_substructure_t, transforms_count) }, + /* SPI is a chunk of variable size*/ + { SPI, offsetof(private_proposal_substructure_t, spi) }, + /* Transforms are stored in a transform substructure list */ + { PAYLOAD_LIST + TRANSFORM_SUBSTRUCTURE, + offsetof(private_proposal_substructure_t, transforms) }, }; /* @@ -131,6 +158,105 @@ encoding_rule_t proposal_substructure_encodings[] = { +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ +/** + * Encryption. + */ +typedef enum { + IKEV1_ENCR_DES_CBC = 1, + IKEV1_ENCR_IDEA_CBC = 2, + IKEV1_ENCR_BLOWFISH_CBC = 3, + IKEV1_ENCR_RC5_R16_B64_CBC = 4, + IKEV1_ENCR_3DES_CBC = 5, + IKEV1_ENCR_CAST_CBC = 6, + IKEV1_ENCR_AES_CBC = 7, + IKEV1_ENCR_CAMELLIA_CBC = 8, + IKEV1_ENCR_LAST = 9, +} ikev1_encryption_t; + +/** + * IKEv1 hash. + */ +typedef enum { + IKEV1_HASH_MD5 = 1, + IKEV1_HASH_SHA1 = 2, + IKEV1_HASH_TIGER = 3, + IKEV1_HASH_SHA2_256 = 4, + IKEV1_HASH_SHA2_384 = 5, + IKEV1_HASH_SHA2_512 = 6, +} ikev1_hash_t; + +/** + * IKEv1 Transform ID IKE. + */ +typedef enum { + IKEV1_TRANSID_KEY_IKE = 1, +} ikev1_ike_transid_t; + +/** + * IKEv1 Transform ID ESP. + */ +typedef enum { + IKEV1_TRANSID_ESP_DES_IV64 = 1, + IKEV1_TRANSID_ESP_DES = 2, + IKEV1_TRANSID_ESP_3DES = 3, + IKEV1_TRANSID_ESP_RC5 = 4, + IKEV1_TRANSID_ESP_IDEA = 5, + IKEV1_TRANSID_ESP_CAST = 6, + IKEV1_TRANSID_ESP_BLOWFISH = 7, + IKEV1_TRANSID_ESP_3IDEA = 8, + IKEV1_TRANSID_ESP_DES_IV32 = 9, + IKEV1_TRANSID_ESP_RC4 = 10, + IKEV1_TRANSID_ESP_NULL = 11, + IKEV1_TRANSID_ESP_AES_CBC = 12, +} ikev1_esp_transid_t; + +/** + * IKEv1 ESP Encapsulation mode. + */ +typedef enum { + IKEV1_ENCAP_TUNNEL = 1, + IKEV1_ENCAP_TRANSPORT = 2, + IKEV1_ENCAP_UDP_TUNNEL = 3, + IKEV1_ENCAP_UDP_TRANSPORT = 4, +} ikev1_esp_encap_t; + +/** + * IKEv1 Life duration types. + */ +typedef enum { + IKEV1_LIFE_TYPE_SECONDS = 1, + IKEV1_LIFE_TYPE_KILOBYTES = 2, +} ikev1_life_type_t; + +/** + * IKEv1 authenticaiton methods + */ +typedef enum { + IKEV1_AUTH_PSK = 1, + IKEV1_AUTH_DSS_SIG = 2, + IKEV1_AUTH_RSA_SIG = 3, + IKEV1_AUTH_RSA_ENC = 4, + IKEV1_AUTH_RSA_ENC_REV = 5, + IKEV1_AUTH_ECDSA_256 = 9, + IKEV1_AUTH_ECDSA_384 = 10, + IKEV1_AUTH_ECDSA_521 = 11, + IKEV1_AUTH_XAUTH_INIT_PSK = 65001, + IKEV1_AUTH_XAUTH_RESP_PSK = 65002, + IKEV1_AUTH_XAUTH_INIT_DSS = 65003, + IKEV1_AUTH_XAUTH_RESP_DSS = 65004, + IKEV1_AUTH_XAUTH_INIT_RSA = 65005, + IKEV1_AUTH_XAUTH_RESP_RSA = 65006, + IKEV1_AUTH_XAUTH_INIT_RSA_ENC = 65007, + IKEV1_AUTH_XAUTH_RESP_RSA_ENC = 65008, + IKEV1_AUTH_XAUTH_INIT_RSA_ENC_REV = 65009, + IKEV1_AUTH_XAUTH_RESP_RSA_ENC_REV = 65010, + IKEV1_AUTH_HYBRID_INIT_RSA = 64221, + IKEV1_AUTH_HYBRID_RESP_RSA = 64222, + IKEV1_AUTH_HYBRID_INIT_DSS = 64223, + IKEV1_AUTH_HYBRID_RESP_DSS = 64224, + +} ikev1_auth_method_t; + METHOD(payload_t, verify, status_t, private_proposal_substructure_t *this) { @@ -188,18 +314,28 @@ METHOD(payload_t, verify, status_t, return status; } -METHOD(payload_t, get_encoding_rules, void, - private_proposal_substructure_t *this, encoding_rule_t **rules, - size_t *rule_count) +METHOD(payload_t, get_encoding_rules, int, + private_proposal_substructure_t *this, encoding_rule_t **rules) { - *rules = proposal_substructure_encodings; - *rule_count = countof(proposal_substructure_encodings); + if (this->type == PROPOSAL_SUBSTRUCTURE) + { + *rules = encodings_v2; + return countof(encodings_v2); + } + *rules = encodings_v1; + return countof(encodings_v1); +} + +METHOD(payload_t, get_header_length, int, + private_proposal_substructure_t *this) +{ + return 8 + this->spi_size; } METHOD(payload_t, get_type, payload_type_t, private_proposal_substructure_t *this) { - return PROPOSAL_SUBSTRUCTURE; + return this->type; } METHOD(payload_t, get_next_type, payload_type_t, @@ -222,7 +358,7 @@ static void compute_length(private_proposal_substructure_t *this) payload_t *transform; this->transforms_count = 0; - this->proposal_length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH + this->spi.len; + this->proposal_length = get_header_length(this); enumerator = this->transforms->create_enumerator(this->transforms); while (enumerator->enumerate(enumerator, &transform)) { @@ -301,45 +437,335 @@ METHOD(proposal_substructure_t, get_spi, chunk_t, return this->spi; } -METHOD(proposal_substructure_t, get_proposal, proposal_t*, - private_proposal_substructure_t *this) +/** + * Add a transform to a proposal for IKEv2 + */ +static void add_to_proposal_v2(proposal_t *proposal, + transform_substructure_t *transform) { + transform_attribute_t *tattr; enumerator_t *enumerator; - transform_substructure_t *transform; - proposal_t *proposal; - u_int64_t spi; + u_int16_t key_length = 0; - proposal = proposal_create(this->protocol_id, this->proposal_number); + enumerator = transform->create_attribute_enumerator(transform); + while (enumerator->enumerate(enumerator, &tattr)) + { + if (tattr->get_attribute_type(tattr) == TATTR_IKEV2_KEY_LENGTH) + { + key_length = tattr->get_value(tattr); + break; + } + } + enumerator->destroy(enumerator); - enumerator = this->transforms->create_enumerator(this->transforms); - while (enumerator->enumerate(enumerator, &transform)) + proposal->add_algorithm(proposal, + transform->get_transform_type_or_number(transform), + transform->get_transform_id(transform), key_length); +} + +/** + * Map IKEv1 to IKEv2 algorithms + */ +typedef struct { + u_int16_t ikev1; + u_int16_t ikev2; +} algo_map_t; + +/** + * Encryption algorithm mapping + */ +static algo_map_t map_encr[] = { + { IKEV1_ENCR_DES_CBC, ENCR_DES }, + { IKEV1_ENCR_IDEA_CBC, ENCR_IDEA }, + { IKEV1_ENCR_BLOWFISH_CBC, ENCR_BLOWFISH }, + { IKEV1_ENCR_3DES_CBC, ENCR_3DES }, + { IKEV1_ENCR_CAST_CBC, ENCR_CAST }, + { IKEV1_ENCR_AES_CBC, ENCR_AES_CBC }, + { IKEV1_ENCR_CAMELLIA_CBC, ENCR_CAMELLIA_CBC }, +}; + +/** + * Integrity algorithm mapping + */ +static algo_map_t map_integ[] = { + { IKEV1_HASH_MD5, AUTH_HMAC_MD5_96 }, + { IKEV1_HASH_SHA1, AUTH_HMAC_SHA1_96 }, + { IKEV1_HASH_SHA2_256, AUTH_HMAC_SHA2_256_128 }, + { IKEV1_HASH_SHA2_384, AUTH_HMAC_SHA2_384_192 }, + { IKEV1_HASH_SHA2_512, AUTH_HMAC_SHA2_512_256 }, +}; + +/** + * PRF algorithm mapping + */ +static algo_map_t map_prf[] = { + { IKEV1_HASH_MD5, PRF_HMAC_MD5 }, + { IKEV1_HASH_SHA1, PRF_HMAC_SHA1 }, + { IKEV1_HASH_SHA2_256, PRF_HMAC_SHA2_256 }, + { IKEV1_HASH_SHA2_384, PRF_HMAC_SHA2_384 }, + { IKEV1_HASH_SHA2_512, PRF_HMAC_SHA2_512 }, +}; + +/** + * Get IKEv2 algorithm from IKEv1 identifier + */ +static u_int16_t get_alg_from_ikev1(transform_type_t type, u_int16_t value) +{ + algo_map_t *map; + u_int16_t def; + int i, count; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + map = map_encr; + count = countof(map_encr); + def = ENCR_UNDEFINED; + break; + case INTEGRITY_ALGORITHM: + map = map_integ; + count = countof(map_integ); + def = AUTH_UNDEFINED; + break; + case PSEUDO_RANDOM_FUNCTION: + map = map_prf; + count = countof(map_prf); + def = PRF_UNDEFINED; + break; + default: + return 0; + } + for (i = 0; i < count; i++) { - transform_type_t transform_type; - u_int16_t transform_id; - u_int16_t key_length = 0; + if (map[i].ikev1 == value) + { + return map[i].ikev2; + } + } + return def; +} - transform_type = transform->get_transform_type(transform); - transform_id = transform->get_transform_id(transform); - transform->get_key_length(transform, &key_length); +/** + * Get IKEv1 algorithm from IKEv2 identifier + */ +static u_int16_t get_ikev1_from_alg(transform_type_t type, u_int16_t value) +{ + algo_map_t *map; + int i, count; - proposal->add_algorithm(proposal, transform_type, transform_id, key_length); + switch (type) + { + case ENCRYPTION_ALGORITHM: + map = map_encr; + count = countof(map_encr); + break; + case INTEGRITY_ALGORITHM: + map = map_integ; + count = countof(map_integ); + break; + case PSEUDO_RANDOM_FUNCTION: + map = map_prf; + count = countof(map_prf); + break; + default: + return 0; + } + for (i = 0; i < count; i++) + { + if (map[i].ikev2 == value) + { + return map[i].ikev1; + } + } + return 0; +} + +/** + * Get IKEv1 authentication attribute from auth_method_t + */ +static u_int16_t get_ikev1_auth(auth_method_t method) +{ + switch (method) + { + case AUTH_RSA: + return IKEV1_AUTH_RSA_SIG; + case AUTH_DSS: + return IKEV1_AUTH_DSS_SIG; + case AUTH_XAUTH_INIT_PSK: + return IKEV1_AUTH_XAUTH_INIT_PSK; + case AUTH_XAUTH_INIT_RSA: + return IKEV1_AUTH_XAUTH_INIT_RSA; + case AUTH_HYBRID_INIT_RSA: + return IKEV1_AUTH_HYBRID_INIT_RSA; + case AUTH_ECDSA_256: + return IKEV1_AUTH_ECDSA_256; + case AUTH_ECDSA_384: + return IKEV1_AUTH_ECDSA_384; + case AUTH_ECDSA_521: + return IKEV1_AUTH_ECDSA_521; + case AUTH_PSK: + default: + return IKEV1_AUTH_PSK; + } +} + +/** + * Get IKEv1 encapsulation mode + */ +static u_int16_t get_ikev1_mode(ipsec_mode_t mode, bool udp) +{ + switch (mode) + { + case MODE_TUNNEL: + return udp ? IKEV1_ENCAP_UDP_TUNNEL : IKEV1_ENCAP_TUNNEL; + case MODE_TRANSPORT: + return udp ? IKEV1_ENCAP_UDP_TRANSPORT : IKEV1_ENCAP_TRANSPORT; + default: + return IKEV1_ENCAP_TUNNEL; + } +} + +/** + * Add an IKE transform to a proposal for IKEv1 + */ +static void add_to_proposal_v1_ike(proposal_t *proposal, + transform_substructure_t *transform) +{ + transform_attribute_type_t type; + transform_attribute_t *tattr; + enumerator_t *enumerator; + u_int16_t value, key_length = 0; + u_int16_t encr = ENCR_UNDEFINED; + + enumerator = transform->create_attribute_enumerator(transform); + while (enumerator->enumerate(enumerator, &tattr)) + { + type = tattr->get_attribute_type(tattr); + value = tattr->get_value(tattr); + switch (type) + { + case TATTR_PH1_ENCRYPTION_ALGORITHM: + encr = get_alg_from_ikev1(ENCRYPTION_ALGORITHM, value); + break; + case TATTR_PH1_KEY_LENGTH: + key_length = value; + break; + case TATTR_PH1_HASH_ALGORITHM: + proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, + get_alg_from_ikev1(INTEGRITY_ALGORITHM, value), 0); + proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, + get_alg_from_ikev1(PSEUDO_RANDOM_FUNCTION, value), 0); + break; + case TATTR_PH1_GROUP: + proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP, + value, 0); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + if (encr != ENCR_UNDEFINED) + { + proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, key_length); + } +} + +/** + * Add an ESP transform to a proposal for IKEv1 + */ +static void add_to_proposal_v1_esp(proposal_t *proposal, + transform_substructure_t *transform) +{ + transform_attribute_type_t type; + transform_attribute_t *tattr; + enumerator_t *enumerator; + u_int16_t value, key_length = 0; + + enumerator = transform->create_attribute_enumerator(transform); + while (enumerator->enumerate(enumerator, &tattr)) + { + type = tattr->get_attribute_type(tattr); + value = tattr->get_value(tattr); + switch (type) + { + case TATTR_PH2_KEY_LENGTH: + key_length = value; + break; + case TATTR_PH2_AUTH_ALGORITHM: + proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, + get_alg_from_ikev1(INTEGRITY_ALGORITHM, value), 0); + break; + case TATTR_PH2_GROUP: + proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP, + value, 0); + break; + default: + break; + } } enumerator->destroy(enumerator); + /* TODO-IKEv1: handle ESN attribute */ + proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, + NO_EXT_SEQ_NUMBERS, 0); + + proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, + transform->get_transform_id(transform), key_length); +} + +METHOD(proposal_substructure_t, get_proposals, void, + private_proposal_substructure_t *this, linked_list_t *proposals) +{ + transform_substructure_t *transform; + enumerator_t *enumerator; + proposal_t *proposal = NULL; + u_int64_t spi = 0; + switch (this->spi.len) { case 4: - spi = *((u_int32_t*)this->spi.ptr); + spi = *((u_int32_t*)this->spi.ptr); break; case 8: spi = *((u_int64_t*)this->spi.ptr); break; default: - spi = 0; + break; } - proposal->set_spi(proposal, spi); - return proposal; + enumerator = this->transforms->create_enumerator(this->transforms); + while (enumerator->enumerate(enumerator, &transform)) + { + if (!proposal) + { + proposal = proposal_create(this->protocol_id, this->proposal_number); + proposal->set_spi(proposal, spi); + proposals->insert_last(proposals, proposal); + } + if (this->type == PROPOSAL_SUBSTRUCTURE) + { + add_to_proposal_v2(proposal, transform); + } + else + { + switch (this->protocol_id) + { + case PROTO_IKE: + add_to_proposal_v1_ike(proposal, transform); + break; + case PROTO_ESP: + add_to_proposal_v1_esp(proposal, transform); + break; + default: + break; + } + /* create a new proposal for each transform in IKEv1 */ + proposal = NULL; + } + } + enumerator->destroy(enumerator); } METHOD(proposal_substructure_t, create_substructure_enumerator, enumerator_t*, @@ -348,11 +774,164 @@ METHOD(proposal_substructure_t, create_substructure_enumerator, enumerator_t*, return this->transforms->create_enumerator(this->transforms); } +/** + * Get an attribute from any transform, 0 if not found + */ +static u_int64_t get_attr(private_proposal_substructure_t *this, + transform_attribute_type_t type) +{ + enumerator_t *transforms, *attributes; + transform_substructure_t *transform; + transform_attribute_t *attr; + + transforms = this->transforms->create_enumerator(this->transforms); + while (transforms->enumerate(transforms, &transform)) + { + attributes = transform->create_attribute_enumerator(transform); + while (attributes->enumerate(attributes, &attr)) + { + if (attr->get_attribute_type(attr) == type) + { + attributes->destroy(attributes); + transforms->destroy(transforms); + return attr->get_value(attr); + } + } + attributes->destroy(attributes); + } + transforms->destroy(transforms); + return 0; +} + +/** + * Look up a lifetime duration of a given kind in all transforms + */ +static u_int64_t get_life_duration(private_proposal_substructure_t *this, + transform_attribute_type_t type_attr, ikev1_life_type_t type, + transform_attribute_type_t dur_attr) +{ + enumerator_t *transforms, *attributes; + transform_substructure_t *transform; + transform_attribute_t *attr; + + transforms = this->transforms->create_enumerator(this->transforms); + while (transforms->enumerate(transforms, &transform)) + { + attributes = transform->create_attribute_enumerator(transform); + while (attributes->enumerate(attributes, &attr)) + { + if (attr->get_attribute_type(attr) == type_attr && + attr->get_value(attr) == type) + { /* got type attribute, look for duration following next */ + while (attributes->enumerate(attributes, &attr)) + { + if (attr->get_attribute_type(attr) == dur_attr) + { + attributes->destroy(attributes); + transforms->destroy(transforms); + return attr->get_value(attr); + } + } + } + } + attributes->destroy(attributes); + } + transforms->destroy(transforms); + return 0; +} + +METHOD(proposal_substructure_t, get_lifetime, u_int32_t, + private_proposal_substructure_t *this) +{ + u_int32_t duration; + + switch (this->protocol_id) + { + case PROTO_IKE: + return get_life_duration(this, TATTR_PH1_LIFE_TYPE, + IKEV1_LIFE_TYPE_SECONDS, TATTR_PH1_LIFE_DURATION); + case PROTO_ESP: + duration = get_life_duration(this, TATTR_PH2_SA_LIFE_TYPE, + IKEV1_LIFE_TYPE_SECONDS, TATTR_PH2_SA_LIFE_DURATION); + if (!duration) + { /* default to 8 hours, RFC 2407 */ + return 28800; + } + return duration; + default: + return 0; + } +} + +METHOD(proposal_substructure_t, get_lifebytes, u_int64_t, + private_proposal_substructure_t *this) +{ + switch (this->protocol_id) + { + case PROTO_ESP: + return 1000 * get_life_duration(this, TATTR_PH2_SA_LIFE_TYPE, + IKEV1_LIFE_TYPE_KILOBYTES, TATTR_PH2_SA_LIFE_DURATION); + case PROTO_IKE: + default: + return 0; + } +} + +METHOD(proposal_substructure_t, get_auth_method, auth_method_t, + private_proposal_substructure_t *this) +{ + switch (get_attr(this, TATTR_PH1_AUTH_METHOD)) + { + case IKEV1_AUTH_PSK: + return AUTH_PSK; + case IKEV1_AUTH_RSA_SIG: + return AUTH_RSA; + case IKEV1_AUTH_DSS_SIG: + return AUTH_DSS; + case IKEV1_AUTH_XAUTH_INIT_PSK: + return AUTH_XAUTH_INIT_PSK; + case IKEV1_AUTH_XAUTH_INIT_RSA: + return AUTH_XAUTH_INIT_RSA; + case IKEV1_AUTH_HYBRID_INIT_RSA: + return AUTH_HYBRID_INIT_RSA; + case IKEV1_AUTH_ECDSA_256: + return AUTH_ECDSA_256; + case IKEV1_AUTH_ECDSA_384: + return AUTH_ECDSA_384; + case IKEV1_AUTH_ECDSA_521: + return AUTH_ECDSA_521; + default: + return AUTH_NONE; + } +} + +METHOD(proposal_substructure_t, get_encap_mode, ipsec_mode_t, + private_proposal_substructure_t *this, bool *udp) +{ + *udp = FALSE; + switch (get_attr(this, TATTR_PH2_ENCAP_MODE)) + { + case IKEV1_ENCAP_TRANSPORT: + return MODE_TRANSPORT; + case IKEV1_ENCAP_TUNNEL: + return MODE_TUNNEL; + case IKEV1_ENCAP_UDP_TRANSPORT: + *udp = TRUE; + return MODE_TRANSPORT; + case IKEV1_ENCAP_UDP_TUNNEL: + *udp = TRUE; + return MODE_TUNNEL; + default: + /* default to TUNNEL, RFC 2407 says implementation specific */ + return MODE_TUNNEL; + } +} + METHOD2(payload_t, proposal_substructure_t, destroy, void, private_proposal_substructure_t *this) { this->transforms->destroy_offset(this->transforms, - offsetof(transform_substructure_t, destroy)); + offsetof(payload_t, destroy)); chunk_free(&this->spi); free(this); } @@ -360,7 +939,7 @@ METHOD2(payload_t, proposal_substructure_t, destroy, void, /* * Described in header. */ -proposal_substructure_t *proposal_substructure_create() +proposal_substructure_t *proposal_substructure_create(payload_type_t type) { private_proposal_substructure_t *this; @@ -369,6 +948,7 @@ proposal_substructure_t *proposal_substructure_create() .payload_interface = { .verify = _verify, .get_encoding_rules = _get_encoding_rules, + .get_header_length = _get_header_length, .get_length = _get_length, .get_next_type = _get_next_type, .set_next_type = _set_next_type, @@ -380,39 +960,192 @@ proposal_substructure_t *proposal_substructure_create() .set_protocol_id = _set_protocol_id, .get_protocol_id = _get_protocol_id, .set_is_last_proposal = _set_is_last_proposal, - .get_proposal = _get_proposal, + .get_proposals = _get_proposals, .create_substructure_enumerator = _create_substructure_enumerator, .set_spi = _set_spi, .get_spi = _get_spi, + .get_lifetime = _get_lifetime, + .get_lifebytes = _get_lifebytes, + .get_auth_method = _get_auth_method, + .get_encap_mode = _get_encap_mode, .destroy = _destroy, }, .next_payload = NO_PAYLOAD, - .proposal_length = PROPOSAL_SUBSTRUCTURE_HEADER_LENGTH, .transforms = linked_list_create(), + .type = type, ); + compute_length(this); return &this->public; } -/* - * Described in header. +/** + * Add an IKEv1 IKE proposal to the substructure */ -proposal_substructure_t *proposal_substructure_create_from_proposal( - proposal_t *proposal) +static void set_from_proposal_v1_ike(private_proposal_substructure_t *this, + proposal_t *proposal, u_int32_t lifetime, + auth_method_t method, int number) { transform_substructure_t *transform; - private_proposal_substructure_t *this; u_int16_t alg, key_size; enumerator_t *enumerator; - this = (private_proposal_substructure_t*)proposal_substructure_create(); + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE_V1, + number, IKEV1_TRANSID_KEY_IKE); + + enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM); + if (enumerator->enumerate(enumerator, &alg, &key_size)) + { + alg = get_ikev1_from_alg(ENCRYPTION_ALGORITHM, alg); + if (alg) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_ENCRYPTION_ALGORITHM, alg)); + if (key_size) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_KEY_LENGTH, key_size)); + } + } + } + enumerator->destroy(enumerator); + + /* encode the integrity algorithm as hash and assume use the same PRF */ + enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM); + if (enumerator->enumerate(enumerator, &alg, &key_size)) + { + alg = get_ikev1_from_alg(INTEGRITY_ALGORITHM, alg); + if (alg) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_HASH_ALGORITHM, alg)); + } + } + enumerator->destroy(enumerator); + + enumerator = proposal->create_enumerator(proposal, DIFFIE_HELLMAN_GROUP); + if (enumerator->enumerate(enumerator, &alg, &key_size)) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_GROUP, alg)); + } + enumerator->destroy(enumerator); + + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_AUTH_METHOD, get_ikev1_auth(method))); + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_LIFE_TYPE, IKEV1_LIFE_TYPE_SECONDS)); + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH1_LIFE_DURATION, lifetime)); + + add_transform_substructure(this, transform); +} + +/** + * Add an IKEv1 ESP proposal to the substructure + */ +static void set_from_proposal_v1_esp(private_proposal_substructure_t *this, + proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, + ipsec_mode_t mode, bool udp, int number) +{ + transform_substructure_t *transform = NULL; + u_int16_t alg, key_size; + enumerator_t *enumerator; + + enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM); + if (enumerator->enumerate(enumerator, &alg, &key_size)) + { + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE_V1, + number, alg); + if (key_size) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_KEY_LENGTH, key_size)); + } + } + enumerator->destroy(enumerator); + if (!transform) + { + return; + } + + enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM); + if (enumerator->enumerate(enumerator, &alg, &key_size)) + { + alg = get_ikev1_from_alg(INTEGRITY_ALGORITHM, alg); + if (alg) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_AUTH_ALGORITHM, alg)); + } + } + enumerator->destroy(enumerator); + + enumerator = proposal->create_enumerator(proposal, DIFFIE_HELLMAN_GROUP); + if (enumerator->enumerate(enumerator, &alg, &key_size)) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_GROUP, alg)); + } + enumerator->destroy(enumerator); + + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_ENCAP_MODE, get_ikev1_mode(mode, udp))); + if (lifetime) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_SA_LIFE_TYPE, IKEV1_LIFE_TYPE_SECONDS)); + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_SA_LIFE_DURATION, lifetime)); + } + if (lifebytes) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_SA_LIFE_TYPE, IKEV1_LIFE_TYPE_KILOBYTES)); + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1, + TATTR_PH2_SA_LIFE_DURATION, lifebytes / 1000)); + } + + add_transform_substructure(this, transform); +} + +/** + * Add an IKEv2 proposal to the substructure + */ +static void set_from_proposal_v2(private_proposal_substructure_t *this, + proposal_t *proposal) +{ + transform_substructure_t *transform; + u_int16_t alg, key_size; + enumerator_t *enumerator; /* encryption algorithm is only available in ESP */ enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM); while (enumerator->enumerate(enumerator, &alg, &key_size)) { - transform = transform_substructure_create_type(ENCRYPTION_ALGORITHM, - alg, key_size); + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE, + ENCRYPTION_ALGORITHM, alg); + if (key_size) + { + transform->add_transform_attribute(transform, + transform_attribute_create_value(TRANSFORM_ATTRIBUTE, + TATTR_IKEV2_KEY_LENGTH, key_size)); + } add_transform_substructure(this, transform); } enumerator->destroy(enumerator); @@ -421,8 +1154,8 @@ proposal_substructure_t *proposal_substructure_create_from_proposal( enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM); while (enumerator->enumerate(enumerator, &alg, &key_size)) { - transform = transform_substructure_create_type(INTEGRITY_ALGORITHM, - alg, key_size); + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE, + INTEGRITY_ALGORITHM, alg); add_transform_substructure(this, transform); } enumerator->destroy(enumerator); @@ -431,8 +1164,8 @@ proposal_substructure_t *proposal_substructure_create_from_proposal( enumerator = proposal->create_enumerator(proposal, PSEUDO_RANDOM_FUNCTION); while (enumerator->enumerate(enumerator, &alg, &key_size)) { - transform = transform_substructure_create_type(PSEUDO_RANDOM_FUNCTION, - alg, key_size); + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE, + PSEUDO_RANDOM_FUNCTION, alg); add_transform_substructure(this, transform); } enumerator->destroy(enumerator); @@ -441,8 +1174,8 @@ proposal_substructure_t *proposal_substructure_create_from_proposal( enumerator = proposal->create_enumerator(proposal, DIFFIE_HELLMAN_GROUP); while (enumerator->enumerate(enumerator, &alg, NULL)) { - transform = transform_substructure_create_type(DIFFIE_HELLMAN_GROUP, - alg, 0); + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE, + DIFFIE_HELLMAN_GROUP, alg); add_transform_substructure(this, transform); } enumerator->destroy(enumerator); @@ -451,27 +1184,36 @@ proposal_substructure_t *proposal_substructure_create_from_proposal( enumerator = proposal->create_enumerator(proposal, EXTENDED_SEQUENCE_NUMBERS); while (enumerator->enumerate(enumerator, &alg, NULL)) { - transform = transform_substructure_create_type(EXTENDED_SEQUENCE_NUMBERS, - alg, 0); + transform = transform_substructure_create_type(TRANSFORM_SUBSTRUCTURE, + EXTENDED_SEQUENCE_NUMBERS, alg); add_transform_substructure(this, transform); } enumerator->destroy(enumerator); +} + +/** + * Set SPI and other data from proposal, compute length + */ +static void set_data(private_proposal_substructure_t *this, proposal_t *proposal) +{ + u_int64_t spi64; + u_int32_t spi32; /* add SPI, if necessary */ switch (proposal->get_protocol(proposal)) { case PROTO_AH: case PROTO_ESP: - this->spi_size = this->spi.len = 4; - this->spi.ptr = malloc(this->spi_size); - *((u_int32_t*)this->spi.ptr) = proposal->get_spi(proposal); + spi32 = proposal->get_spi(proposal); + this->spi = chunk_clone(chunk_from_thing(spi32)); + this->spi_size = this->spi.len; break; case PROTO_IKE: - if (proposal->get_spi(proposal)) + spi64 = proposal->get_spi(proposal); + if (spi64) { /* IKE only uses SPIS when rekeying, but on initial setup */ - this->spi_size = this->spi.len = 8; - this->spi.ptr = malloc(this->spi_size); - *((u_int64_t*)this->spi.ptr) = proposal->get_spi(proposal); + this->spi = chunk_clone(chunk_from_thing(spi64)); + this->spi_size = this->spi.len; } break; default: @@ -480,6 +1222,92 @@ proposal_substructure_t *proposal_substructure_create_from_proposal( this->proposal_number = proposal->get_number(proposal); this->protocol_id = proposal->get_protocol(proposal); compute_length(this); +} + +/* + * Described in header. + */ +proposal_substructure_t *proposal_substructure_create_from_proposal_v2( + proposal_t *proposal) +{ + private_proposal_substructure_t *this; + + this = (private_proposal_substructure_t*) + proposal_substructure_create(SECURITY_ASSOCIATION); + set_from_proposal_v2(this, proposal); + set_data(this, proposal); + + return &this->public; +} + +/** + * See header. + */ +proposal_substructure_t *proposal_substructure_create_from_proposal_v1( + proposal_t *proposal, u_int32_t lifetime, u_int64_t lifebytes, + auth_method_t auth, ipsec_mode_t mode, bool udp) +{ + private_proposal_substructure_t *this; + + this = (private_proposal_substructure_t*) + proposal_substructure_create(PROPOSAL_SUBSTRUCTURE_V1); + switch (proposal->get_protocol(proposal)) + { + case PROTO_IKE: + set_from_proposal_v1_ike(this, proposal, lifetime, auth, 1); + break; + case PROTO_ESP: + set_from_proposal_v1_esp(this, proposal, lifetime, + lifebytes, mode, udp, 1); + break; + default: + break; + } + set_data(this, proposal); + + return &this->public; +} + +/** + * See header. + */ +proposal_substructure_t *proposal_substructure_create_from_proposals_v1( + linked_list_t *proposals, u_int32_t lifetime, u_int64_t lifebytes, + auth_method_t auth, ipsec_mode_t mode, bool udp) +{ + private_proposal_substructure_t *this = NULL; + enumerator_t *enumerator; + proposal_t *proposal; + int number = 0; + + enumerator = proposals->create_enumerator(proposals); + while (enumerator->enumerate(enumerator, &proposal)) + { + if (!this) + { + this = (private_proposal_substructure_t*) + proposal_substructure_create_from_proposal_v1( + proposal, lifetime, lifebytes, auth, mode, udp); + ++number; + } + else + { + switch (proposal->get_protocol(proposal)) + { + case PROTO_IKE: + set_from_proposal_v1_ike(this, proposal, lifetime, + auth, ++number); + break; + case PROTO_ESP: + set_from_proposal_v1_esp(this, proposal, lifetime, + lifebytes, mode, udp, ++number); + break; + default: + break; + } + } + } + enumerator->destroy(enumerator); return &this->public; } |