diff options
Diffstat (limited to 'src/libcharon/encoding/message.c')
-rw-r--r-- | src/libcharon/encoding/message.c | 770 |
1 files changed, 663 insertions, 107 deletions
diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 2b5399294..d3b72ea95 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006-2011 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2010 revosec AG * Copyright (C) 2006 Daniel Roethlisberger @@ -24,37 +24,47 @@ #include <library.h> #include <daemon.h> -#include <sa/ike_sa_id.h> +#include <sa/ikev1/keymat_v1.h> #include <encoding/generator.h> #include <encoding/parser.h> -#include <utils/linked_list.h> #include <encoding/payloads/encodings.h> #include <encoding/payloads/payload.h> +#include <encoding/payloads/hash_payload.h> #include <encoding/payloads/encryption_payload.h> #include <encoding/payloads/unknown_payload.h> #include <encoding/payloads/cp_payload.h> /** - * Max number of notify payloads per IKEv2 Message + * Max number of notify payloads per IKEv2 message */ #define MAX_NOTIFY_PAYLOADS 20 /** - * Max number of delete payloads per IKEv2 Message + * Max number of delete payloads per IKEv2 message */ #define MAX_DELETE_PAYLOADS 20 /** - * Max number of certificate payloads per IKEv2 Message + * Max number of certificate payloads per IKEv2 message */ #define MAX_CERT_PAYLOADS 8 /** - * Max number of Vendor ID payloads per IKEv2 Message + * Max number of vendor ID payloads per IKEv2 message */ #define MAX_VID_PAYLOADS 20 /** + * Max number of certificate request payloads per IKEv1 message + */ +#define MAX_CERTREQ_PAYLOADS 5 + +/** + * Max number of NAT-D payloads per IKEv1 message + */ +#define MAX_NAT_D_PAYLOADS 5 + +/** * A payload rule defines the rules for a payload * in a specific message rule. It defines if and how * many times a payload must/can occur in a message @@ -414,6 +424,273 @@ static payload_order_t me_connect_r_order[] = { }; #endif /* ME */ +#ifdef USE_IKEV1 +/** + * Message rule for ID_PROT from initiator. + */ +static payload_rule_t id_prot_i_rules[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, + {SECURITY_ASSOCIATION_V1, 0, 1, FALSE, FALSE}, + {KEY_EXCHANGE_V1, 0, 1, FALSE, FALSE}, + {NONCE_V1, 0, 1, FALSE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, + {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, + {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {ID_V1, 0, 1, TRUE, FALSE}, + {CERTIFICATE_V1, 0, 2, TRUE, FALSE}, + {SIGNATURE_V1, 0, 1, TRUE, FALSE}, + {HASH_V1, 0, 1, TRUE, FALSE}, +}; + +/** + * payload order for ID_PROT from initiator. + */ +static payload_order_t id_prot_i_order[] = { +/* payload type notify type */ + {SECURITY_ASSOCIATION_V1, 0}, + {KEY_EXCHANGE_V1, 0}, + {NONCE_V1, 0}, + {ID_V1, 0}, + {CERTIFICATE_V1, 0}, + {SIGNATURE_V1, 0}, + {HASH_V1, 0}, + {CERTIFICATE_REQUEST_V1, 0}, + {NOTIFY_V1, 0}, + {VENDOR_ID_V1, 0}, + {NAT_D_V1, 0}, +}; + +/** + * Message rule for ID_PROT from responder. + */ +static payload_rule_t id_prot_r_rules[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, + {SECURITY_ASSOCIATION_V1, 0, 1, FALSE, FALSE}, + {KEY_EXCHANGE_V1, 0, 1, FALSE, FALSE}, + {NONCE_V1, 0, 1, FALSE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, + {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, + {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {ID_V1, 0, 1, TRUE, FALSE}, + {CERTIFICATE_V1, 0, 2, TRUE, FALSE}, + {SIGNATURE_V1, 0, 1, TRUE, FALSE}, + {HASH_V1, 0, 1, TRUE, FALSE}, +}; + +/** + * payload order for ID_PROT from responder. + */ +static payload_order_t id_prot_r_order[] = { +/* payload type notify type */ + {SECURITY_ASSOCIATION_V1, 0}, + {KEY_EXCHANGE_V1, 0}, + {NONCE_V1, 0}, + {ID_V1, 0}, + {CERTIFICATE_V1, 0}, + {SIGNATURE_V1, 0}, + {HASH_V1, 0}, + {CERTIFICATE_REQUEST_V1, 0}, + {NOTIFY_V1, 0}, + {VENDOR_ID_V1, 0}, + {NAT_D_V1, 0}, +}; + +/** + * Message rule for AGGRESSIVE from initiator. + */ +static payload_rule_t aggressive_i_rules[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, + {SECURITY_ASSOCIATION_V1, 0, 1, FALSE, FALSE}, + {KEY_EXCHANGE_V1, 0, 1, FALSE, FALSE}, + {NONCE_V1, 0, 1, FALSE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, + {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, + {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {ID_V1, 0, 1, FALSE, FALSE}, + {CERTIFICATE_V1, 0, 1, TRUE, FALSE}, + {SIGNATURE_V1, 0, 1, TRUE, FALSE}, + {HASH_V1, 0, 1, TRUE, FALSE}, +}; + +/** + * payload order for AGGRESSIVE from initiator. + */ +static payload_order_t aggressive_i_order[] = { +/* payload type notify type */ + {SECURITY_ASSOCIATION_V1, 0}, + {KEY_EXCHANGE_V1, 0}, + {NONCE_V1, 0}, + {ID_V1, 0}, + {CERTIFICATE_V1, 0}, + {NAT_D_V1, 0}, + {SIGNATURE_V1, 0}, + {HASH_V1, 0}, + {CERTIFICATE_REQUEST_V1, 0}, + {NOTIFY_V1, 0}, + {VENDOR_ID_V1, 0}, +}; + +/** + * Message rule for AGGRESSIVE from responder. + */ +static payload_rule_t aggressive_r_rules[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, + {SECURITY_ASSOCIATION_V1, 0, 1, FALSE, FALSE}, + {KEY_EXCHANGE_V1, 0, 1, FALSE, FALSE}, + {NONCE_V1, 0, 1, FALSE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, + {CERTIFICATE_REQUEST_V1, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, + {NAT_D_V1, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, + {ID_V1, 0, 1, FALSE, FALSE}, + {CERTIFICATE_V1, 0, 1, FALSE, FALSE}, + {SIGNATURE_V1, 0, 1, FALSE, FALSE}, + {HASH_V1, 0, 1, FALSE, FALSE}, +}; + +/** + * payload order for AGGRESSIVE from responder. + */ +static payload_order_t aggressive_r_order[] = { +/* payload type notify type */ + {SECURITY_ASSOCIATION_V1, 0}, + {KEY_EXCHANGE_V1, 0}, + {NONCE_V1, 0}, + {ID_V1, 0}, + {CERTIFICATE_V1, 0}, + {NAT_D_V1, 0}, + {SIGNATURE_V1, 0}, + {HASH_V1, 0}, + {CERTIFICATE_REQUEST_V1, 0}, + {NOTIFY_V1, 0}, + {VENDOR_ID_V1, 0}, +}; + +/** + * Message rule for INFORMATIONAL_V1 from initiator. + */ +static payload_rule_t informational_i_rules_v1[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, + {DELETE_V1, 0, MAX_DELETE_PAYLOADS, TRUE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, +}; + +/** + * payload order for INFORMATIONAL_V1 from initiator. + */ +static payload_order_t informational_i_order_v1[] = { +/* payload type notify type */ + {NOTIFY_V1, 0}, + {DELETE_V1, 0}, + {VENDOR_ID_V1, 0}, +}; + +/** + * Message rule for INFORMATIONAL_V1 from responder. + */ +static payload_rule_t informational_r_rules_v1[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, + {DELETE_V1, 0, MAX_DELETE_PAYLOADS, TRUE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, +}; + +/** + * payload order for INFORMATIONAL_V1 from responder. + */ +static payload_order_t informational_r_order_v1[] = { +/* payload type notify type */ + {NOTIFY_V1, 0}, + {DELETE_V1, 0}, + {VENDOR_ID_V1, 0}, +}; + +/** + * Message rule for QUICK_MODE from initiator. + */ +static payload_rule_t quick_mode_i_rules[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, + {HASH_V1, 0, 1, TRUE, FALSE}, + {SECURITY_ASSOCIATION_V1, 0, 2, TRUE, FALSE}, + {NONCE_V1, 0, 1, TRUE, FALSE}, + {KEY_EXCHANGE_V1, 0, 1, TRUE, FALSE}, + {ID_V1, 0, 2, TRUE, FALSE}, + {NAT_OA_V1, 0, 2, TRUE, FALSE}, +}; + +/** + * payload order for QUICK_MODE from initiator. + */ +static payload_order_t quick_mode_i_order[] = { +/* payload type notify type */ + {NOTIFY_V1, 0}, + {VENDOR_ID_V1, 0}, + {HASH_V1, 0}, + {SECURITY_ASSOCIATION_V1, 0}, + {NONCE_V1, 0}, + {KEY_EXCHANGE_V1, 0}, + {ID_V1, 0}, + {NAT_OA_V1, 0}, +}; + +/** + * Message rule for QUICK_MODE from responder. + */ +static payload_rule_t quick_mode_r_rules[] = { +/* payload type min max encr suff */ + {NOTIFY_V1, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, + {VENDOR_ID_V1, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, + {HASH_V1, 0, 1, TRUE, FALSE}, + {SECURITY_ASSOCIATION_V1, 0, 2, TRUE, FALSE}, + {NONCE_V1, 0, 1, TRUE, FALSE}, + {KEY_EXCHANGE_V1, 0, 1, TRUE, FALSE}, + {ID_V1, 0, 2, TRUE, FALSE}, + {NAT_OA_V1, 0, 2, TRUE, FALSE}, +}; + +/** + * payload order for QUICK_MODE from responder. + */ +static payload_order_t quick_mode_r_order[] = { +/* payload type notify type */ + {NOTIFY_V1, 0}, + {VENDOR_ID_V1, 0}, + {HASH_V1, 0}, + {SECURITY_ASSOCIATION_V1, 0}, + {NONCE_V1, 0}, + {KEY_EXCHANGE_V1, 0}, + {ID_V1, 0}, + {NAT_OA_V1, 0}, +}; + +/** + * Message rule for TRANSACTION. + */ +static payload_rule_t transaction_payload_rules_v1[] = { +/* payload type min max encr suff */ + {HASH_V1, 0, 1, TRUE, FALSE}, + {CONFIGURATION_V1, 1, 1, FALSE, FALSE}, +}; + +/** + * Payload order for TRANSACTION. + */ +static payload_order_t transaction_payload_order_v1[] = { +/* payload type notify type */ + {HASH_V1, 0}, + {CONFIGURATION_V1, 0}, +}; + +#endif /* USE_IKEV1 */ + /** * Message rules, defines allowed payloads. */ @@ -460,6 +737,49 @@ static message_rule_t message_rules[] = { countof(me_connect_r_order), me_connect_r_order, }, #endif /* ME */ +#ifdef USE_IKEV1 + {ID_PROT, TRUE, FALSE, + countof(id_prot_i_rules), id_prot_i_rules, + countof(id_prot_i_order), id_prot_i_order, + }, + {ID_PROT, FALSE, FALSE, + countof(id_prot_r_rules), id_prot_r_rules, + countof(id_prot_r_order), id_prot_r_order, + }, + {AGGRESSIVE, TRUE, FALSE, + countof(aggressive_i_rules), aggressive_i_rules, + countof(aggressive_i_order), aggressive_i_order, + }, + {AGGRESSIVE, FALSE, FALSE, + countof(aggressive_r_rules), aggressive_r_rules, + countof(aggressive_r_order), aggressive_r_order, + }, + {INFORMATIONAL_V1, TRUE, TRUE, + countof(informational_i_rules_v1), informational_i_rules_v1, + countof(informational_i_order_v1), informational_i_order_v1, + }, + {INFORMATIONAL_V1, FALSE, TRUE, + countof(informational_r_rules_v1), informational_r_rules_v1, + countof(informational_r_order_v1), informational_r_order_v1, + }, + {QUICK_MODE, TRUE, TRUE, + countof(quick_mode_i_rules), quick_mode_i_rules, + countof(quick_mode_i_order), quick_mode_i_order, + }, + {QUICK_MODE, FALSE, TRUE, + countof(quick_mode_r_rules), quick_mode_r_rules, + countof(quick_mode_r_order), quick_mode_r_order, + }, + {TRANSACTION, TRUE, TRUE, + countof(transaction_payload_rules_v1), transaction_payload_rules_v1, + countof(transaction_payload_order_v1), transaction_payload_order_v1, + }, + {TRANSACTION, FALSE, TRUE, + countof(transaction_payload_rules_v1), transaction_payload_rules_v1, + countof(transaction_payload_order_v1), transaction_payload_order_v1, + }, + /* TODO-IKEv1: define rules for other exchanges */ +#endif /* USE_IKEV1 */ }; @@ -501,6 +821,11 @@ struct private_message_t { bool is_request; /** + * The message is encrypted (IKEv1) + */ + bool is_encrypted; + + /** * Higher version supported? */ bool version_flag; @@ -508,7 +833,7 @@ struct private_message_t { /** * Reserved bits in IKE header */ - bool reserved[5]; + bool reserved[2]; /** * Sorting of message disabled? @@ -739,7 +1064,14 @@ METHOD(message_t, add_notify, void, payload->destroy(payload); } } - notify = notify_payload_create(); + if (this->major_version == IKEV2_MAJOR_VERSION) + { + notify = notify_payload_create(NOTIFY); + } + else + { + notify = notify_payload_create(NOTIFY_V1); + } notify->set_notify_type(notify, type); notify->set_notification_data(notify, data); add_payload(this, (payload_t*)notify); @@ -810,7 +1142,8 @@ METHOD(message_t, get_notify, notify_payload_t*, enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &payload)) { - if (payload->get_type(payload) == NOTIFY) + if (payload->get_type(payload) == NOTIFY || + payload->get_type(payload) == NOTIFY_V1) { notify = (notify_payload_t*)payload; if (notify->get_notify_type(notify) == type) @@ -837,7 +1170,7 @@ static char* get_string(private_message_t *this, char *buf, int len) memset(buf, 0, len); len--; - written = snprintf(pos, len, "%N %s %d [", + written = snprintf(pos, len, "%N %s %u [", exchange_type_names, this->exchange_type, this->is_request ? "request" : "response", this->message_id); @@ -859,7 +1192,8 @@ static char* get_string(private_message_t *this, char *buf, int len) } pos += written; len -= written; - if (payload->get_type(payload) == NOTIFY) + if (payload->get_type(payload) == NOTIFY || + payload->get_type(payload) == NOTIFY_V1) { notify_payload_t *notify; notify_type_t type; @@ -1017,7 +1351,7 @@ static void order_payloads(private_message_t *this) } /** - * Wrap payloads in a encryption payload + * Wrap payloads in an encryption payload */ static encryption_payload_t* wrap_payloads(private_message_t *this) { @@ -1033,7 +1367,14 @@ static encryption_payload_t* wrap_payloads(private_message_t *this) payloads->insert_last(payloads, current); } - encryption = encryption_payload_create(); + if (this->is_encrypted) + { + encryption = encryption_payload_create(ENCRYPTED_V1); + } + else + { + encryption = encryption_payload_create(ENCRYPTED); + } while (payloads->remove_first(payloads, (void**)¤t) == SUCCESS) { payload_rule_t *rule; @@ -1046,8 +1387,8 @@ static encryption_payload_t* wrap_payloads(private_message_t *this) { encrypt = rule->encrypted; } - if (encrypt) - { + if (encrypt || this->is_encrypted) + { /* encryption is forced for IKEv1 */ DBG2(DBG_ENC, "insert payload %N to encryption payload", payload_type_names, type); encryption->add_payload(encryption, current); @@ -1071,17 +1412,20 @@ METHOD(message_t, disable_sort, void, } METHOD(message_t, generate, status_t, - private_message_t *this, aead_t *aead, packet_t **packet) + private_message_t *this, keymat_t *keymat, packet_t **packet) { + keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; generator_t *generator; ike_header_t *ike_header; payload_t *payload, *next; encryption_payload_t *encryption = NULL; + payload_type_t next_type; enumerator_t *enumerator; - chunk_t chunk; + aead_t *aead = NULL; + chunk_t chunk, hash = chunk_empty; char str[BUF_LEN]; u_int32_t *lenpos; - bool *reserved; + bool encrypted = FALSE, *reserved; int i; if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) @@ -1108,27 +1452,77 @@ METHOD(message_t, generate, status_t, { order_payloads(this); } + if (keymat && keymat->get_version(keymat) == IKEV1) + { + /* get a hash for this message, if any is required */ + if (keymat_v1->get_hash_phase2(keymat_v1, &this->public, &hash)) + { /* insert a HASH payload as first payload */ + hash_payload_t *hash_payload; + + hash_payload = hash_payload_create(HASH_V1); + hash_payload->set_hash(hash_payload, hash); + this->payloads->insert_first(this->payloads, hash_payload); + if (this->exchange_type == INFORMATIONAL_V1) + { + this->is_encrypted = encrypted = TRUE; + } + chunk_free(&hash); + } + } + if (this->major_version == IKEV2_MAJOR_VERSION) + { + encrypted = this->rule->encrypted; + } + else if (!encrypted) + { + /* If at least one payload requires encryption, encrypt the message. + * If no key material is available, the flag will be reset below. */ + enumerator = this->payloads->create_enumerator(this->payloads); + while (enumerator->enumerate(enumerator, (void**)&payload)) + { + payload_rule_t *rule; + + rule = get_payload_rule(this, payload->get_type(payload)); + if (rule && rule->encrypted) + { + this->is_encrypted = encrypted = TRUE; + break; + } + } + enumerator->destroy(enumerator); + } DBG1(DBG_ENC, "generating %s", get_string(this, str, sizeof(str))); - if (aead && this->rule->encrypted) + if (keymat) + { + aead = keymat->get_aead(keymat, FALSE); + } + if (aead && encrypted) { encryption = wrap_payloads(this); } else { DBG2(DBG_ENC, "not encrypting payloads"); + this->is_encrypted = FALSE; } - ike_header = ike_header_create(); - ike_header->set_maj_version(ike_header, this->major_version); - ike_header->set_min_version(ike_header, this->minor_version); + ike_header = ike_header_create_version(this->major_version, + this->minor_version); ike_header->set_exchange_type(ike_header, this->exchange_type); ike_header->set_message_id(ike_header, this->message_id); - ike_header->set_response_flag(ike_header, !this->is_request); - ike_header->set_version_flag(ike_header, this->version_flag); - ike_header->set_initiator_flag(ike_header, + if (this->major_version == IKEV2_MAJOR_VERSION) + { + ike_header->set_response_flag(ike_header, !this->is_request); + ike_header->set_version_flag(ike_header, this->version_flag); + ike_header->set_initiator_flag(ike_header, this->ike_sa_id->is_initiator(this->ike_sa_id)); + } + else + { + ike_header->set_encryption_flag(ike_header, this->is_encrypted); + } ike_header->set_initiator_spi(ike_header, this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); ike_header->set_responder_spi(ike_header, @@ -1156,22 +1550,38 @@ METHOD(message_t, generate, status_t, payload = next; } enumerator->destroy(enumerator); - payload->set_next_type(payload, encryption ? ENCRYPTED : NO_PAYLOAD); + if (this->is_encrypted) + { /* for encrypted IKEv1 messages */ + next_type = encryption->payload_interface.get_next_type( + (payload_t*)encryption); + } + else + { + next_type = encryption ? ENCRYPTED : NO_PAYLOAD; + } + payload->set_next_type(payload, next_type); generator->generate_payload(generator, payload); ike_header->destroy(ike_header); if (encryption) - { - u_int32_t *lenpos; - - /* build associated data (without header of encryption payload) */ - chunk = generator->get_chunk(generator, &lenpos); + { /* set_transform() has to be called before get_length() */ encryption->set_transform(encryption, aead); - /* fill in length, including encryption payload */ - htoun32(lenpos, chunk.len + encryption->get_length(encryption)); - + if (this->is_encrypted) + { /* for IKEv1 instead of associated data we provide the IV */ + if (!keymat_v1->get_iv(keymat_v1, this->message_id, &chunk)) + { + generator->destroy(generator); + return FAILED; + } + } + else + { /* build associated data (without header of encryption payload) */ + chunk = generator->get_chunk(generator, &lenpos); + /* fill in length, including encryption payload */ + htoun32(lenpos, chunk.len + encryption->get_length(encryption)); + } this->payloads->insert_last(this->payloads, encryption); - if (!encryption->encrypt(encryption, chunk)) + if (encryption->encrypt(encryption, chunk) != SUCCESS) { generator->destroy(generator); return INVALID_STATE; @@ -1181,8 +1591,22 @@ METHOD(message_t, generate, status_t, chunk = generator->get_chunk(generator, &lenpos); htoun32(lenpos, chunk.len); this->packet->set_data(this->packet, chunk_clone(chunk)); + if (this->is_encrypted) + { + /* update the IV for the next IKEv1 message */ + chunk_t last_block; + size_t bs; + + bs = aead->get_block_size(aead); + last_block = chunk_create(chunk.ptr + chunk.len - bs, bs); + if (!keymat_v1->update_iv(keymat_v1, this->message_id, last_block) || + !keymat_v1->confirm_iv(keymat_v1, this->message_id)) + { + generator->destroy(generator); + return FAILED; + } + } generator->destroy(generator); - *packet = this->packet->clone(this->packet); return SUCCESS; } @@ -1204,7 +1628,7 @@ METHOD(message_t, get_packet_data, chunk_t, { return chunk_empty; } - return chunk_clone(this->packet->get_data(this->packet)); + return this->packet->get_data(this->packet); } METHOD(message_t, parse_header, status_t, @@ -1237,15 +1661,24 @@ METHOD(message_t, parse_header, status_t, } DESTROY_IF(this->ike_sa_id); - this->ike_sa_id = ike_sa_id_create(ike_header->get_initiator_spi(ike_header), + this->ike_sa_id = ike_sa_id_create( + ike_header->get_maj_version(ike_header), + ike_header->get_initiator_spi(ike_header), ike_header->get_responder_spi(ike_header), ike_header->get_initiator_flag(ike_header)); this->exchange_type = ike_header->get_exchange_type(ike_header); this->message_id = ike_header->get_message_id(ike_header); - this->is_request = !ike_header->get_response_flag(ike_header); this->major_version = ike_header->get_maj_version(ike_header); this->minor_version = ike_header->get_min_version(ike_header); + if (this->major_version == IKEV2_MAJOR_VERSION) + { + this->is_request = !ike_header->get_response_flag(ike_header); + } + else + { + this->is_encrypted = ike_header->get_encryption_flag(ike_header); + } this->first_payload = ike_header->payload_interface.get_next_type( &ike_header->payload_interface); for (i = 0; i < countof(this->reserved); i++) @@ -1257,19 +1690,12 @@ METHOD(message_t, parse_header, status_t, this->reserved[i] = *reserved; } } - DBG2(DBG_ENC, "parsed a %N %s", exchange_type_names, this->exchange_type, - this->is_request ? "request" : "response"); - ike_header->destroy(ike_header); - this->rule = get_message_rule(this); - if (!this->rule) - { - DBG1(DBG_ENC, "no message rules specified for a %N %s", - exchange_type_names, this->exchange_type, - this->is_request ? "request" : "response"); - } - return status; + DBG2(DBG_ENC, "parsed a %N %s header", exchange_type_names, + this->exchange_type, this->major_version == IKEV1_MAJOR_VERSION ? + "message" : (this->is_request ? "request" : "response")); + return SUCCESS; } /** @@ -1298,15 +1724,83 @@ static bool is_connectivity_check(private_message_t *this, payload_t *payload) } /** + * Parses and verifies the unencrypted payloads contained in the message + */ +static status_t parse_payloads(private_message_t *this) +{ + payload_type_t type = this->first_payload; + payload_t *payload; + status_t status; + + if (this->is_encrypted) + { /* wrap the whole encrypted IKEv1 message in a special encryption + * payload which is then handled just like a regular payload */ + encryption_payload_t *encryption; + + status = this->parser->parse_payload(this->parser, ENCRYPTED_V1, + (payload_t**)&encryption); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "failed to wrap encrypted IKEv1 message"); + return PARSE_ERROR; + } + encryption->payload_interface.set_next_type((payload_t*)encryption, + this->first_payload); + this->payloads->insert_last(this->payloads, encryption); + return SUCCESS; + } + + while (type != NO_PAYLOAD) + { + DBG2(DBG_ENC, "starting parsing a %N payload", + payload_type_names, type); + + status = this->parser->parse_payload(this->parser, type, &payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "payload type %N could not be parsed", + payload_type_names, type); + return PARSE_ERROR; + } + + DBG2(DBG_ENC, "verifying payload of type %N", payload_type_names, type); + status = payload->verify(payload); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "%N payload verification failed", + payload_type_names, type); + payload->destroy(payload); + return VERIFY_ERROR; + } + + DBG2(DBG_ENC, "%N payload verified. Adding to payload list", + payload_type_names, type); + this->payloads->insert_last(this->payloads, payload); + + /* an encryption payload is the last one, so STOP here. decryption is + * done later */ + if (type == ENCRYPTED) + { + DBG2(DBG_ENC, "%N payload found. Stop parsing", + payload_type_names, type); + break; + } + type = payload->get_next_type(payload); + } + return SUCCESS; +} + +/** * Decrypt payload from the encryption payload */ -static status_t decrypt_payloads(private_message_t *this, aead_t *aead) +static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) { bool was_encrypted = FALSE; payload_t *payload, *previous = NULL; enumerator_t *enumerator; payload_rule_t *rule; payload_type_t type; + aead_t *aead; status_t status = SUCCESS; enumerator = this->payloads->create_enumerator(this->payloads); @@ -1316,11 +1810,12 @@ static status_t decrypt_payloads(private_message_t *this, aead_t *aead) DBG2(DBG_ENC, "process payload of type %N", payload_type_names, type); - if (type == ENCRYPTED) + if (type == ENCRYPTED || type == ENCRYPTED_V1) { encryption_payload_t *encryption; payload_t *encrypted; chunk_t chunk; + size_t bs; encryption = (encryption_payload_t*)payload; @@ -1332,16 +1827,57 @@ static status_t decrypt_payloads(private_message_t *this, aead_t *aead) status = VERIFY_ERROR; break; } + if (!keymat) + { + DBG1(DBG_ENC, "found encryption payload, but no keymat"); + status = INVALID_ARG; + break; + } + aead = keymat->get_aead(keymat, TRUE); + if (!aead) + { + DBG1(DBG_ENC, "found encryption payload, but no transform set"); + status = INVALID_ARG; + break; + } + bs = aead->get_block_size(aead); encryption->set_transform(encryption, aead); chunk = this->packet->get_data(this->packet); - if (chunk.len < encryption->get_length(encryption)) + if (chunk.len < encryption->get_length(encryption) || + chunk.len < bs) { DBG1(DBG_ENC, "invalid payload length"); status = VERIFY_ERROR; break; } - chunk.len -= encryption->get_length(encryption); - status = encryption->decrypt(encryption, chunk); + if (keymat->get_version(keymat) == IKEV1) + { /* instead of associated data we provide the IV, we also update + * the IV with the last encrypted block */ + keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; + chunk_t iv; + + if (keymat_v1->get_iv(keymat_v1, this->message_id, &iv)) + { + status = encryption->decrypt(encryption, iv); + if (status == SUCCESS) + { + if (!keymat_v1->update_iv(keymat_v1, this->message_id, + chunk_create(chunk.ptr + chunk.len - bs, bs))) + { + status = FAILED; + } + } + } + else + { + status = FAILED; + } + } + else + { + chunk.len -= encryption->get_length(encryption); + status = encryption->decrypt(encryption, chunk); + } if (status != SUCCESS) { break; @@ -1369,7 +1905,8 @@ static status_t decrypt_payloads(private_message_t *this, aead_t *aead) encryption->destroy(encryption); } if (payload_is_known(type) && !was_encrypted && - !is_connectivity_check(this, payload)) + !is_connectivity_check(this, payload) && + this->exchange_type != AGGRESSIVE) { rule = get_payload_rule(this, type); if (!rule || rule->encrypted) @@ -1396,7 +1933,7 @@ static status_t verify(private_message_t *this) DBG2(DBG_ENC, "verifying message structure"); - /* check for payloads with wrong count*/ + /* check for payloads with wrong count */ for (i = 0; i < this->rule->rule_count; i++) { enumerator_t *enumerator; @@ -1443,57 +1980,30 @@ static status_t verify(private_message_t *this) } METHOD(message_t, parse_body, status_t, - private_message_t *this, aead_t *aead) + private_message_t *this, keymat_t *keymat) { status_t status = SUCCESS; - payload_t *payload; - payload_type_t type; char str[BUF_LEN]; - type = this->first_payload; - DBG2(DBG_ENC, "parsing body of message, first payload is %N", - payload_type_names, type); + payload_type_names, this->first_payload); - while (type != NO_PAYLOAD) + this->rule = get_message_rule(this); + if (!this->rule) { - DBG2(DBG_ENC, "starting parsing a %N payload", - payload_type_names, type); - - status = this->parser->parse_payload(this->parser, type, &payload); - if (status != SUCCESS) - { - DBG1(DBG_ENC, "payload type %N could not be parsed", - payload_type_names, type); - return this->exchange_type == IKE_SA_INIT ? PARSE_ERROR : FAILED; - } - - DBG2(DBG_ENC, "verifying payload of type %N", payload_type_names, type); - status = payload->verify(payload); - if (status != SUCCESS) - { - DBG1(DBG_ENC, "%N payload verification failed", - payload_type_names, type); - payload->destroy(payload); - return this->exchange_type == IKE_SA_INIT ? VERIFY_ERROR : FAILED; - } - - DBG2(DBG_ENC, "%N payload verified. Adding to payload list", - payload_type_names, type); - this->payloads->insert_last(this->payloads, payload); + DBG1(DBG_ENC, "no message rules specified for a %N %s", + exchange_type_names, this->exchange_type, + this->is_request ? "request" : "response"); + return NOT_SUPPORTED; + } - /* an encryption payload is the last one, so STOP here. decryption is - * done later */ - if (type == ENCRYPTED) - { - DBG2(DBG_ENC, "%N payload found. Stop parsing", - payload_type_names, type); - break; - } - type = payload->get_next_type(payload); + status = parse_payloads(this); + if (status != SUCCESS) + { /* error is already logged */ + return status; } - status = decrypt_payloads(this, aead); + status = decrypt_payloads(this, keymat); if (status != SUCCESS) { DBG1(DBG_ENC, "could not decrypt payloads"); @@ -1508,6 +2018,50 @@ METHOD(message_t, parse_body, status_t, DBG1(DBG_ENC, "parsed %s", get_string(this, str, sizeof(str))); + if (keymat && keymat->get_version(keymat) == IKEV1) + { + keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; + chunk_t hash; + + if (keymat_v1->get_hash_phase2(keymat_v1, &this->public, &hash)) + { + hash_payload_t *hash_payload; + chunk_t other_hash; + + if (this->first_payload != HASH_V1) + { + if (this->exchange_type == INFORMATIONAL_V1) + { + DBG1(DBG_ENC, "ignoring unprotected INFORMATIONAL from %H", + this->packet->get_source(this->packet)); + } + else + { + DBG1(DBG_ENC, "expected HASH payload as first payload"); + } + chunk_free(&hash); + return VERIFY_ERROR; + } + hash_payload = (hash_payload_t*)get_payload(this, HASH_V1); + other_hash = hash_payload->get_hash(hash_payload); + DBG3(DBG_ENC, "HASH received %B\nHASH expected %B", + &other_hash, &hash); + if (!chunk_equals(hash, other_hash)) + { + DBG1(DBG_ENC, "received HASH payload does not match"); + chunk_free(&hash); + return FAILED; + } + chunk_free(&hash); + } + if (this->is_encrypted) + { /* message verified, confirm IV */ + if (!keymat_v1->confirm_iv(keymat_v1, this->message_id)) + { + return FAILED; + } + } + } return SUCCESS; } @@ -1522,7 +2076,7 @@ METHOD(message_t, destroy, void, } /* - * Described in Header-File + * Described in header. */ message_t *message_create_from_packet(packet_t *packet) { @@ -1567,8 +2121,6 @@ message_t *message_create_from_packet(packet_t *packet) .get_packet_data = _get_packet_data, .destroy = _destroy, }, - .major_version = IKE_MAJOR_VERSION, - .minor_version = IKE_MINOR_VERSION, .exchange_type = EXCHANGE_TYPE_UNDEFINED, .is_request = TRUE, .first_payload = NO_PAYLOAD, @@ -1577,14 +2129,18 @@ message_t *message_create_from_packet(packet_t *packet) .parser = parser_create(packet->get_data(packet)), ); - return (&this->public); + return &this->public; } /* - * Described in Header. + * Described in header. */ -message_t *message_create() +message_t *message_create(int major, int minor) { - return message_create_from_packet(packet_create()); -} + message_t *this = message_create_from_packet(packet_create()); + this->set_major_version(this, major); + this->set_minor_version(this, minor); + + return this; +} |