/* * Copyright (C) 2006-2018 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2010 revosec AG * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter * 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 * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include "message.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * Max number of notify payloads per IKEv2 message */ #define MAX_NOTIFY_PAYLOADS 20 /** * Max number of delete payloads per IKEv2 message */ #define MAX_DELETE_PAYLOADS 20 /** * Max number of certificate payloads per IKEv2 message */ #define MAX_CERT_PAYLOADS 8 /** * 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 20 /** * Max number of NAT-D payloads per IKEv1 message */ #define MAX_NAT_D_PAYLOADS 10 /** * 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 * and if it must be encrypted. */ typedef struct { /* Payload type */ payload_type_t type; /* Minimal occurrence of this payload. */ size_t min_occurrence; /* Max occurrence of this payload. */ size_t max_occurrence; /* TRUE if payload must be encrypted */ bool encrypted; /* If payload occurs, the message rule is fulfilled */ bool sufficient; } payload_rule_t; /** * payload ordering structure allows us to reorder payloads according to RFC. */ typedef struct { /** payload type */ payload_type_t type; /** notify type, if payload == PLV2_NOTIFY */ notify_type_t notify; } payload_order_t; /** * A message rule defines the kind of a message, * if it has encrypted contents and a list * of payload ordering rules and payload parsing rules. */ typedef struct { /** Type of message. */ exchange_type_t exchange_type; /** Is message a request or response. */ bool is_request; /** Message contains encrypted payloads. */ bool encrypted; /** Number of payload rules which will follow */ int rule_count; /** Pointer to first payload rule */ payload_rule_t *rules; /** Number of payload order rules */ int order_count; /** payload ordering rules */ payload_order_t *order; } message_rule_t; /** * Message rule for IKE_SA_INIT from initiator. */ static payload_rule_t ike_sa_init_i_rules[] = { /* payload type min max encr suff */ {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV2_SECURITY_ASSOCIATION, 1, 1, FALSE, FALSE}, {PLV2_KEY_EXCHANGE, 1, 1, FALSE, FALSE}, {PLV2_NONCE, 1, 1, FALSE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, }; /** * payload order for IKE_SA_INIT initiator */ static payload_order_t ike_sa_init_i_order[] = { /* payload type notify type */ {PLV2_NOTIFY, COOKIE}, {PLV2_SECURITY_ASSOCIATION, 0}, {PLV2_KEY_EXCHANGE, 0}, {PLV2_NONCE, 0}, {PLV2_NOTIFY, NAT_DETECTION_SOURCE_IP}, {PLV2_NOTIFY, NAT_DETECTION_DESTINATION_IP}, {PLV2_NOTIFY, 0}, {PLV2_VENDOR_ID, 0}, }; /** * Message rule for IKE_SA_INIT from responder. */ static payload_rule_t ike_sa_init_r_rules[] = { /* payload type min max encr suff */ {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, TRUE}, {PLV2_SECURITY_ASSOCIATION, 1, 1, FALSE, FALSE}, {PLV2_KEY_EXCHANGE, 1, 1, FALSE, FALSE}, {PLV2_NONCE, 1, 1, FALSE, FALSE}, {PLV2_CERTREQ, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, }; /** * payload order for IKE_SA_INIT responder */ static payload_order_t ike_sa_init_r_order[] = { /* payload type notify type */ {PLV2_SECURITY_ASSOCIATION, 0}, {PLV2_KEY_EXCHANGE, 0}, {PLV2_NONCE, 0}, {PLV2_NOTIFY, NAT_DETECTION_SOURCE_IP}, {PLV2_NOTIFY, NAT_DETECTION_DESTINATION_IP}, {PLV2_NOTIFY, HTTP_CERT_LOOKUP_SUPPORTED}, {PLV2_CERTREQ, 0}, {PLV2_NOTIFY, 0}, {PLV2_VENDOR_ID, 0}, }; /** * Message rule for IKE_AUTH from initiator. */ static payload_rule_t ike_auth_i_rules[] = { /* payload type min max encr suff */ {PLV2_FRAGMENT, 0, 1, TRUE, TRUE}, {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV2_EAP, 0, 1, TRUE, TRUE}, {PLV2_AUTH, 0, 1, TRUE, TRUE}, {PLV2_ID_INITIATOR, 0, 1, TRUE, FALSE}, {PLV2_CERTIFICATE, 0, MAX_CERT_PAYLOADS, TRUE, FALSE}, {PLV2_CERTREQ, 0, MAX_CERTREQ_PAYLOADS, TRUE, FALSE}, {PLV2_ID_RESPONDER, 0, 1, TRUE, FALSE}, #ifdef ME {PLV2_SECURITY_ASSOCIATION, 0, 1, TRUE, FALSE}, {PLV2_TS_INITIATOR, 0, 1, TRUE, FALSE}, {PLV2_TS_RESPONDER, 0, 1, TRUE, FALSE}, #else {PLV2_SECURITY_ASSOCIATION, 0, 1, TRUE, FALSE}, {PLV2_TS_INITIATOR, 0, 1, TRUE, FALSE}, {PLV2_TS_RESPONDER, 0, 1, TRUE, FALSE}, #endif /* ME */ {PLV2_CONFIGURATION, 0, 1, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, }; /** * payload order for IKE_AUTH initiator */ static payload_order_t ike_auth_i_order[] = { /* payload type notify type */ {PLV2_ID_INITIATOR, 0}, {PLV2_CERTIFICATE, 0}, {PLV2_NOTIFY, INITIAL_CONTACT}, {PLV2_NOTIFY, HTTP_CERT_LOOKUP_SUPPORTED}, {PLV2_CERTREQ, 0}, {PLV2_ID_RESPONDER, 0}, {PLV2_AUTH, 0}, {PLV2_EAP, 0}, {PLV2_CONFIGURATION, 0}, {PLV2_NOTIFY, IPCOMP_SUPPORTED}, {PLV2_NOTIFY, USE_TRANSPORT_MODE}, {PLV2_NOTIFY, ESP_TFC_PADDING_NOT_SUPPORTED}, {PLV2_NOTIFY, NON_FIRST_FRAGMENTS_ALSO}, {PLV2_SECURITY_ASSOCIATION, 0}, {PLV2_TS_INITIATOR, 0}, {PLV2_TS_RESPONDER, 0}, {PLV2_NOTIFY, MOBIKE_SUPPORTED}, {PLV2_NOTIFY, ADDITIONAL_IP4_ADDRESS}, {PLV2_NOTIFY, ADDITIONAL_IP6_ADDRESS}, {PLV2_NOTIFY, NO_ADDITIONAL_ADDRESSES}, {PLV2_NOTIFY, 0}, {PLV2_VENDOR_ID, 0}, {PLV2_FRAGMENT, 0}, }; /** * Message rule for IKE_AUTH from responder. */ static payload_rule_t ike_auth_r_rules[] = { /* payload type min max encr suff */ {PLV2_FRAGMENT, 0, 1, TRUE, TRUE}, {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, TRUE}, {PLV2_EAP, 0, 1, TRUE, TRUE}, {PLV2_AUTH, 0, 1, TRUE, TRUE}, {PLV2_CERTIFICATE, 0, MAX_CERT_PAYLOADS, TRUE, FALSE}, {PLV2_ID_RESPONDER, 0, 1, TRUE, FALSE}, {PLV2_SECURITY_ASSOCIATION, 0, 1, TRUE, FALSE}, {PLV2_TS_INITIATOR, 0, 1, TRUE, FALSE}, {PLV2_TS_RESPONDER, 0, 1, TRUE, FALSE}, {PLV2_CONFIGURATION, 0, 1, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, }; /** * payload order for IKE_AUTH responder */ static payload_order_t ike_auth_r_order[] = { /* payload type notify type */ {PLV2_ID_RESPONDER, 0}, {PLV2_CERTIFICATE, 0}, {PLV2_AUTH, 0}, {PLV2_EAP, 0}, {PLV2_CONFIGURATION, 0}, {PLV2_NOTIFY, IPCOMP_SUPPORTED}, {PLV2_NOTIFY, USE_TRANSPORT_MODE}, {PLV2_NOTIFY, ESP_TFC_PADDING_NOT_SUPPORTED}, {PLV2_NOTIFY, NON_FIRST_FRAGMENTS_ALSO}, {PLV2_SECURITY_ASSOCIATION, 0}, {PLV2_TS_INITIATOR, 0}, {PLV2_TS_RESPONDER, 0}, {PLV2_NOTIFY, AUTH_LIFETIME}, {PLV2_NOTIFY, MOBIKE_SUPPORTED}, {PLV2_NOTIFY, ADDITIONAL_IP4_ADDRESS}, {PLV2_NOTIFY, ADDITIONAL_IP6_ADDRESS}, {PLV2_NOTIFY, NO_ADDITIONAL_ADDRESSES}, {PLV2_NOTIFY, 0}, {PLV2_VENDOR_ID, 0}, {PLV2_FRAGMENT, 0}, }; /** * Message rule for INFORMATIONAL from initiator. */ static payload_rule_t informational_i_rules[] = { /* payload type min max encr suff */ {PLV2_FRAGMENT, 0, 1, TRUE, TRUE}, {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV2_CONFIGURATION, 0, 1, TRUE, FALSE}, {PLV2_DELETE, 0, MAX_DELETE_PAYLOADS, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, }; /** * payload order for INFORMATIONAL initiator */ static payload_order_t informational_i_order[] = { /* payload type notify type */ {PLV2_NOTIFY, UPDATE_SA_ADDRESSES}, {PLV2_NOTIFY, NAT_DETECTION_SOURCE_IP}, {PLV2_NOTIFY, NAT_DETECTION_DESTINATION_IP}, {PLV2_NOTIFY, COOKIE2}, {PLV2_NOTIFY, 0}, {PLV2_DELETE, 0}, {PLV2_CONFIGURATION, 0}, {PLV2_FRAGMENT, 0}, }; /** * Message rule for INFORMATIONAL from responder. */ static payload_rule_t informational_r_rules[] = { /* payload type min max encr suff */ {PLV2_FRAGMENT, 0, 1, TRUE, TRUE}, {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV2_CONFIGURATION, 0, 1, TRUE, FALSE}, {PLV2_DELETE, 0, MAX_DELETE_PAYLOADS, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, }; /** * payload order for INFORMATIONAL responder */ static payload_order_t informational_r_order[] = { /* payload type notify type */ {PLV2_NOTIFY, UPDATE_SA_ADDRESSES}, {PLV2_NOTIFY, NAT_DETECTION_SOURCE_IP}, {PLV2_NOTIFY, NAT_DETECTION_DESTINATION_IP}, {PLV2_NOTIFY, COOKIE2}, {PLV2_NOTIFY, 0}, {PLV2_DELETE, 0}, {PLV2_CONFIGURATION, 0}, {PLV2_FRAGMENT, 0}, }; /** * Message rule for CREATE_CHILD_SA from initiator. */ static payload_rule_t create_child_sa_i_rules[] = { /* payload type min max encr suff */ {PLV2_FRAGMENT, 0, 1, TRUE, TRUE}, {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV2_SECURITY_ASSOCIATION, 1, 1, TRUE, FALSE}, {PLV2_NONCE, 1, 1, TRUE, FALSE}, {PLV2_KEY_EXCHANGE, 0, 1, TRUE, FALSE}, {PLV2_TS_INITIATOR, 0, 1, TRUE, FALSE}, {PLV2_TS_RESPONDER, 0, 1, TRUE, FALSE}, {PLV2_CONFIGURATION, 0, 1, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, }; /** * payload order for CREATE_CHILD_SA from initiator. */ static payload_order_t create_child_sa_i_order[] = { /* payload type notify type */ {PLV2_NOTIFY, REKEY_SA}, {PLV2_NOTIFY, IPCOMP_SUPPORTED}, {PLV2_NOTIFY, USE_TRANSPORT_MODE}, {PLV2_NOTIFY, ESP_TFC_PADDING_NOT_SUPPORTED}, {PLV2_NOTIFY, NON_FIRST_FRAGMENTS_ALSO}, {PLV2_SECURITY_ASSOCIATION, 0}, {PLV2_NONCE, 0}, {PLV2_KEY_EXCHANGE, 0}, {PLV2_TS_INITIATOR, 0}, {PLV2_TS_RESPONDER, 0}, {PLV2_NOTIFY, 0}, {PLV2_FRAGMENT, 0}, }; /** * Message rule for CREATE_CHILD_SA from responder. */ static payload_rule_t create_child_sa_r_rules[] = { /* payload type min max encr suff */ {PLV2_FRAGMENT, 0, 1, TRUE, TRUE}, {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, TRUE}, {PLV2_SECURITY_ASSOCIATION, 1, 1, TRUE, FALSE}, {PLV2_NONCE, 1, 1, TRUE, FALSE}, {PLV2_KEY_EXCHANGE, 0, 1, TRUE, FALSE}, {PLV2_TS_INITIATOR, 0, 1, TRUE, FALSE}, {PLV2_TS_RESPONDER, 0, 1, TRUE, FALSE}, {PLV2_CONFIGURATION, 0, 1, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, }; /** * payload order for CREATE_CHILD_SA from responder. */ static payload_order_t create_child_sa_r_order[] = { /* payload type notify type */ {PLV2_NOTIFY, IPCOMP_SUPPORTED}, {PLV2_NOTIFY, USE_TRANSPORT_MODE}, {PLV2_NOTIFY, ESP_TFC_PADDING_NOT_SUPPORTED}, {PLV2_NOTIFY, NON_FIRST_FRAGMENTS_ALSO}, {PLV2_SECURITY_ASSOCIATION, 0}, {PLV2_NONCE, 0}, {PLV2_KEY_EXCHANGE, 0}, {PLV2_TS_INITIATOR, 0}, {PLV2_TS_RESPONDER, 0}, {PLV2_NOTIFY, ADDITIONAL_TS_POSSIBLE}, {PLV2_NOTIFY, 0}, {PLV2_FRAGMENT, 0}, }; #ifdef ME /** * Message rule for ME_CONNECT from initiator. */ static payload_rule_t me_connect_i_rules[] = { /* payload type min max encr suff */ {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, TRUE}, {PLV2_ID_PEER, 1, 1, TRUE, FALSE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE} }; /** * payload order for ME_CONNECT from initiator. */ static payload_order_t me_connect_i_order[] = { /* payload type notify type */ {PLV2_NOTIFY, 0}, {PLV2_ID_PEER, 0}, {PLV2_VENDOR_ID, 0}, }; /** * Message rule for ME_CONNECT from responder. */ static payload_rule_t me_connect_r_rules[] = { /* payload type min max encr suff */ {PLV2_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, TRUE}, {PLV2_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE} }; /** * payload order for ME_CONNECT from responder. */ static payload_order_t me_connect_r_order[] = { /* payload type notify type */ {PLV2_NOTIFY, 0}, {PLV2_VENDOR_ID, 0}, }; #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 */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV1_SECURITY_ASSOCIATION, 0, 1, FALSE, FALSE}, {PLV1_KEY_EXCHANGE, 0, 1, FALSE, FALSE}, {PLV1_NONCE, 0, 1, FALSE, FALSE}, {PLV1_VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {PLV1_CERTREQ, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D_DRAFT_00_03, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_ID, 0, 1, TRUE, FALSE}, {PLV1_CERTIFICATE, 0, MAX_CERT_PAYLOADS, TRUE, FALSE}, {PLV1_SIGNATURE, 0, 1, TRUE, FALSE}, {PLV1_HASH, 0, 1, TRUE, FALSE}, {PLV1_FRAGMENT, 0, 1, FALSE, TRUE}, }; /** * payload order for ID_PROT from initiator. */ static payload_order_t id_prot_i_order[] = { /* payload type notify type */ {PLV1_SECURITY_ASSOCIATION, 0}, {PLV1_KEY_EXCHANGE, 0}, {PLV1_NONCE, 0}, {PLV1_ID, 0}, {PLV1_CERTIFICATE, 0}, {PLV1_SIGNATURE, 0}, {PLV1_HASH, 0}, {PLV1_CERTREQ, 0}, {PLV1_NOTIFY, 0}, {PLV1_VENDOR_ID, 0}, {PLV1_NAT_D, 0}, {PLV1_NAT_D_DRAFT_00_03, 0}, {PLV1_FRAGMENT, 0}, }; /** * Message rule for ID_PROT from responder. */ static payload_rule_t id_prot_r_rules[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV1_SECURITY_ASSOCIATION, 0, 1, FALSE, FALSE}, {PLV1_KEY_EXCHANGE, 0, 1, FALSE, FALSE}, {PLV1_NONCE, 0, 1, FALSE, FALSE}, {PLV1_VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {PLV1_CERTREQ, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D_DRAFT_00_03, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_ID, 0, 1, TRUE, FALSE}, {PLV1_CERTIFICATE, 0, MAX_CERT_PAYLOADS, TRUE, FALSE}, {PLV1_SIGNATURE, 0, 1, TRUE, FALSE}, {PLV1_HASH, 0, 1, TRUE, FALSE}, {PLV1_FRAGMENT, 0, 1, FALSE, TRUE}, }; /** * payload order for ID_PROT from responder. */ static payload_order_t id_prot_r_order[] = { /* payload type notify type */ {PLV1_SECURITY_ASSOCIATION, 0}, {PLV1_KEY_EXCHANGE, 0}, {PLV1_NONCE, 0}, {PLV1_ID, 0}, {PLV1_CERTIFICATE, 0}, {PLV1_SIGNATURE, 0}, {PLV1_HASH, 0}, {PLV1_CERTREQ, 0}, {PLV1_NOTIFY, 0}, {PLV1_VENDOR_ID, 0}, {PLV1_NAT_D, 0}, {PLV1_NAT_D_DRAFT_00_03, 0}, {PLV1_FRAGMENT, 0}, }; /** * Message rule for AGGRESSIVE from initiator. */ static payload_rule_t aggressive_i_rules[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV1_SECURITY_ASSOCIATION, 0, 1, FALSE, FALSE}, {PLV1_KEY_EXCHANGE, 0, 1, FALSE, FALSE}, {PLV1_NONCE, 0, 1, FALSE, FALSE}, {PLV1_VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {PLV1_CERTREQ, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D_DRAFT_00_03, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_ID, 0, 1, FALSE, FALSE}, {PLV1_CERTIFICATE, 0, MAX_CERT_PAYLOADS, TRUE, FALSE}, {PLV1_SIGNATURE, 0, 1, TRUE, FALSE}, {PLV1_HASH, 0, 1, TRUE, FALSE}, {PLV1_FRAGMENT, 0, 1, FALSE, TRUE}, }; /** * payload order for AGGRESSIVE from initiator. */ static payload_order_t aggressive_i_order[] = { /* payload type notify type */ {PLV1_SECURITY_ASSOCIATION, 0}, {PLV1_KEY_EXCHANGE, 0}, {PLV1_NONCE, 0}, {PLV1_ID, 0}, {PLV1_CERTIFICATE, 0}, {PLV1_CERTREQ, 0}, {PLV1_NOTIFY, 0}, {PLV1_VENDOR_ID, 0}, {PLV1_HASH, 0}, {PLV1_NAT_D, 0}, {PLV1_NAT_D_DRAFT_00_03, 0}, {PLV1_SIGNATURE, 0}, {PLV1_FRAGMENT, 0}, }; /** * Message rule for AGGRESSIVE from responder. */ static payload_rule_t aggressive_r_rules[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV1_SECURITY_ASSOCIATION, 0, 1, FALSE, FALSE}, {PLV1_KEY_EXCHANGE, 0, 1, FALSE, FALSE}, {PLV1_NONCE, 0, 1, FALSE, FALSE}, {PLV1_VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, {PLV1_CERTREQ, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_NAT_D_DRAFT_00_03, 0, MAX_NAT_D_PAYLOADS, FALSE, FALSE}, {PLV1_ID, 0, 1, FALSE, FALSE}, {PLV1_CERTIFICATE, 0, MAX_CERT_PAYLOADS, FALSE, FALSE}, {PLV1_SIGNATURE, 0, 1, FALSE, FALSE}, {PLV1_HASH, 0, 1, FALSE, FALSE}, {PLV1_FRAGMENT, 0, 1, FALSE, TRUE}, }; /** * payload order for AGGRESSIVE from responder. */ static payload_order_t aggressive_r_order[] = { /* payload type notify type */ {PLV1_SECURITY_ASSOCIATION, 0}, {PLV1_KEY_EXCHANGE, 0}, {PLV1_NONCE, 0}, {PLV1_ID, 0}, {PLV1_CERTIFICATE, 0}, {PLV1_CERTREQ, 0}, {PLV1_NOTIFY, 0}, {PLV1_VENDOR_ID, 0}, {PLV1_NAT_D, 0}, {PLV1_NAT_D_DRAFT_00_03, 0}, {PLV1_SIGNATURE, 0}, {PLV1_HASH, 0}, {PLV1_FRAGMENT, 0}, }; /** * Message rule for INFORMATIONAL_V1 from initiator. */ static payload_rule_t informational_i_rules_v1[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV1_DELETE, 0, MAX_DELETE_PAYLOADS, TRUE, FALSE}, {PLV1_VENDOR_ID, 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 */ {PLV1_NOTIFY, 0}, {PLV1_DELETE, 0}, {PLV1_VENDOR_ID, 0}, }; /** * Message rule for INFORMATIONAL_V1 from responder. */ static payload_rule_t informational_r_rules_v1[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, FALSE, FALSE}, {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV1_DELETE, 0, MAX_DELETE_PAYLOADS, TRUE, FALSE}, {PLV1_VENDOR_ID, 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 */ {PLV1_NOTIFY, 0}, {PLV1_DELETE, 0}, {PLV1_VENDOR_ID, 0}, }; /** * Message rule for QUICK_MODE from initiator. */ static payload_rule_t quick_mode_i_rules[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV1_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, {PLV1_HASH, 0, 1, TRUE, FALSE}, {PLV1_SECURITY_ASSOCIATION, 0, 2, TRUE, FALSE}, {PLV1_NONCE, 0, 1, TRUE, FALSE}, {PLV1_KEY_EXCHANGE, 0, 1, TRUE, FALSE}, {PLV1_ID, 0, 2, TRUE, FALSE}, {PLV1_NAT_OA, 0, 2, TRUE, FALSE}, {PLV1_NAT_OA_DRAFT_00_03, 0, 2, TRUE, FALSE}, {PLV1_FRAGMENT, 0, 1, FALSE, TRUE}, }; /** * payload order for QUICK_MODE from initiator. */ static payload_order_t quick_mode_i_order[] = { /* payload type notify type */ {PLV1_NOTIFY, 0}, {PLV1_VENDOR_ID, 0}, {PLV1_HASH, 0}, {PLV1_SECURITY_ASSOCIATION, 0}, {PLV1_NONCE, 0}, {PLV1_KEY_EXCHANGE, 0}, {PLV1_ID, 0}, {PLV1_NAT_OA, 0}, {PLV1_NAT_OA_DRAFT_00_03, 0}, {PLV1_FRAGMENT, 0}, }; /** * Message rule for QUICK_MODE from responder. */ static payload_rule_t quick_mode_r_rules[] = { /* payload type min max encr suff */ {PLV1_NOTIFY, 0, MAX_NOTIFY_PAYLOADS, TRUE, FALSE}, {PLV1_VENDOR_ID, 0, MAX_VID_PAYLOADS, TRUE, FALSE}, {PLV1_HASH, 0, 1, TRUE, FALSE}, {PLV1_SECURITY_ASSOCIATION, 0, 2, TRUE, FALSE}, {PLV1_NONCE, 0, 1, TRUE, FALSE}, {PLV1_KEY_EXCHANGE, 0, 1, TRUE, FALSE}, {PLV1_ID, 0, 2, TRUE, FALSE}, {PLV1_NAT_OA, 0, 2, TRUE, FALSE}, {PLV1_NAT_OA_DRAFT_00_03, 0, 2, TRUE, FALSE}, {PLV1_FRAGMENT, 0, 1, FALSE, TRUE}, }; /** * payload order for QUICK_MODE from responder. */ static payload_order_t quick_mode_r_order[] = { /* payload type notify type */ {PLV1_NOTIFY, 0}, {PLV1_VENDOR_ID, 0}, {PLV1_HASH, 0}, {PLV1_SECURITY_ASSOCIATION, 0}, {PLV1_NONCE, 0}, {PLV1_KEY_EXCHANGE, 0}, {PLV1_ID, 0}, {PLV1_NAT_OA, 0}, {PLV1_NAT_OA_DRAFT_00_03, 0}, {PLV1_FRAGMENT, 0}, }; /** * Message rule for TRANSACTION. */ static payload_rule_t transaction_payload_rules_v1[] = { /* payload type min max encr suff */ {PLV1_HASH, 0, 1, TRUE, FALSE}, {PLV1_CONFIGURATION, 1, 1, FALSE, FALSE}, }; /** * Payload order for TRANSACTION. */ static payload_order_t transaction_payload_order_v1[] = { /* payload type notify type */ {PLV1_HASH, 0}, {PLV1_CONFIGURATION, 0}, }; #endif /* USE_IKEV1 */ /** * Message rules, defines allowed payloads. */ static message_rule_t message_rules[] = { {IKE_SA_INIT, TRUE, FALSE, countof(ike_sa_init_i_rules), ike_sa_init_i_rules, countof(ike_sa_init_i_order), ike_sa_init_i_order, }, {IKE_SA_INIT, FALSE, FALSE, countof(ike_sa_init_r_rules), ike_sa_init_r_rules, countof(ike_sa_init_r_order), ike_sa_init_r_order, }, {IKE_AUTH, TRUE, TRUE, countof(ike_auth_i_rules), ike_auth_i_rules, countof(ike_auth_i_order), ike_auth_i_order, }, {IKE_AUTH, FALSE, TRUE, countof(ike_auth_r_rules), ike_auth_r_rules, countof(ike_auth_r_order), ike_auth_r_order, }, {INFORMATIONAL, TRUE, TRUE, countof(informational_i_rules), informational_i_rules, countof(informational_i_order), informational_i_order, }, {INFORMATIONAL, FALSE, TRUE, countof(informational_r_rules), informational_r_rules, countof(informational_r_order), informational_r_order, }, {CREATE_CHILD_SA, TRUE, TRUE, countof(create_child_sa_i_rules), create_child_sa_i_rules, countof(create_child_sa_i_order), create_child_sa_i_order, }, {CREATE_CHILD_SA, FALSE, TRUE, countof(create_child_sa_r_rules), create_child_sa_r_rules, countof(create_child_sa_r_order), create_child_sa_r_order, }, #ifdef ME {ME_CONNECT, TRUE, TRUE, countof(me_connect_i_rules), me_connect_i_rules, countof(me_connect_i_order), me_connect_i_order, }, {ME_CONNECT, FALSE, TRUE, countof(me_connect_r_rules), me_connect_r_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 */ }; /** * Data for fragment reassembly. */ typedef struct { /** * For IKEv1 the number of the last fragment (in case we receive them out * of order), since the first one starts with 1 this defines the number of * fragments we expect. * For IKEv2 we store the total number of fragment we received last. */ uint16_t last; /** * Length of all currently received fragments. */ size_t len; /** * Maximum length of a fragmented packet. */ size_t max_packet; } fragment_data_t; typedef struct private_message_t private_message_t; /** * Private data of an message_t object. */ struct private_message_t { /** * Public part of a message_t object. */ message_t public; /** * Minor version of message. */ uint8_t major_version; /** * Major version of message. */ uint8_t minor_version; /** * First Payload in message. */ payload_type_t first_payload; /** * Assigned exchange type. */ exchange_type_t exchange_type; /** * TRUE if message is a request, FALSE if a reply. */ bool is_request; /** * The message is encrypted (IKEv1) */ bool is_encrypted; /** * Higher version supported? */ bool version_flag; /** * Reserved bits in IKE header */ bool reserved[2]; /** * Sorting of message disabled? */ bool sort_disabled; /** * Message ID of this message. */ uint32_t message_id; /** * ID of assigned IKE_SA. */ ike_sa_id_t *ike_sa_id; /** * Assigned UDP packet, stores incoming packet or last generated one. */ packet_t *packet; /** * Array of generated fragments (if any), as packet_t*. * If defragmenting (i.e. frag != NULL) this contains fragment_t* */ array_t *fragments; /** * Linked List where payload data are stored in. */ linked_list_t *payloads; /** * Assigned parser to parse Header and Body of this message. */ parser_t *parser; /** * The message rule for this message instance */ message_rule_t *rule; /** * Data used to reassemble a fragmented message */ fragment_data_t *frag; }; /** * Maximum number of fragments we will handle */ #define MAX_FRAGMENTS 255 /** * A single fragment within a fragmented message */ typedef struct { /** fragment number */ uint8_t num; /** fragment data */ chunk_t data; } fragment_t; static void fragment_destroy(fragment_t *this) { chunk_free(&this->data); free(this); } static void reset_defrag(private_message_t *this) { array_destroy_function(this->fragments, (void*)fragment_destroy, NULL); this->fragments = NULL; this->frag->last = 0; this->frag->len = 0; } /** * Get the message rule that applies to this message */ static message_rule_t* get_message_rule(private_message_t *this) { int i; for (i = 0; i < countof(message_rules); i++) { if ((this->exchange_type == message_rules[i].exchange_type) && (this->is_request == message_rules[i].is_request)) { return &message_rules[i]; } } return NULL; } /** * Look up a payload rule */ static payload_rule_t* get_payload_rule(private_message_t *this, payload_type_t type) { int i; for (i = 0; i < this->rule->rule_count;i++) { if (this->rule->rules[i].type == type) { return &this->rule->rules[i]; } } return NULL; } METHOD(message_t, set_ike_sa_id, void, private_message_t *this,ike_sa_id_t *ike_sa_id) { DESTROY_IF(this->ike_sa_id); this->ike_sa_id = ike_sa_id->clone(ike_sa_id); } METHOD(message_t, get_ike_sa_id, ike_sa_id_t*, private_message_t *this) { return this->ike_sa_id; } METHOD(message_t, set_message_id, void, private_message_t *this,uint32_t message_id) { this->message_id = message_id; } METHOD(message_t, get_message_id, uint32_t, private_message_t *this) { return this->message_id; } METHOD(message_t, get_initiator_spi, uint64_t, private_message_t *this) { return (this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); } METHOD(message_t, get_responder_spi, uint64_t, private_message_t *this) { return (this->ike_sa_id->get_responder_spi(this->ike_sa_id)); } METHOD(message_t, set_major_version, void, private_message_t *this, uint8_t major_version) { this->major_version = major_version; } METHOD(message_t, get_major_version, uint8_t, private_message_t *this) { return this->major_version; } METHOD(message_t, set_minor_version, void, private_message_t *this,uint8_t minor_version) { this->minor_version = minor_version; } METHOD(message_t, get_minor_version, uint8_t, private_message_t *this) { return this->minor_version; } METHOD(message_t, set_exchange_type, void, private_message_t *this, exchange_type_t exchange_type) { this->exchange_type = exchange_type; } METHOD(message_t, get_exchange_type, exchange_type_t, private_message_t *this) { return this->exchange_type; } METHOD(message_t, get_first_payload_type, payload_type_t, private_message_t *this) { return this->first_payload; } METHOD(message_t, set_request, void, private_message_t *this, bool request) { this->is_request = request; } METHOD(message_t, get_request, bool, private_message_t *this) { return this->is_request; } METHOD(message_t, set_version_flag, void, private_message_t *this) { this->version_flag = TRUE; } METHOD(message_t, get_reserved_header_bit, bool, private_message_t *this, u_int nr) { if (nr < countof(this->reserved)) { return this->reserved[nr]; } return FALSE; } METHOD(message_t, set_reserved_header_bit, void, private_message_t *this, u_int nr) { if (nr < countof(this->reserved)) { this->reserved[nr] = TRUE; } } METHOD(message_t, is_encoded, bool, private_message_t *this) { return this->packet->get_data(this->packet).ptr != NULL; } METHOD(message_t, is_fragmented, bool, private_message_t *this) { return array_count(this->fragments) > 0; } METHOD(message_t, add_payload, void, private_message_t *this, payload_t *payload) { payload_t *last_payload; if (this->payloads->get_count(this->payloads) > 0) { this->payloads->get_last(this->payloads, (void **)&last_payload); last_payload->set_next_type(last_payload, payload->get_type(payload)); } else { this->first_payload = payload->get_type(payload); } payload->set_next_type(payload, PL_NONE); this->payloads->insert_last(this->payloads, payload); DBG2(DBG_ENC ,"added payload of type %N to message", payload_type_names, payload->get_type(payload)); } METHOD(message_t, add_notify, void, private_message_t *this, bool flush, notify_type_t type, chunk_t data) { notify_payload_t *notify; payload_t *payload; if (flush) { while (this->payloads->remove_last(this->payloads, (void**)&payload) == SUCCESS) { payload->destroy(payload); } } if (this->major_version == IKEV2_MAJOR_VERSION) { notify = notify_payload_create(PLV2_NOTIFY); } else { notify = notify_payload_create(PLV1_NOTIFY); } notify->set_notify_type(notify, type); notify->set_notification_data(notify, data); add_payload(this, (payload_t*)notify); } METHOD(message_t, set_source, void, private_message_t *this, host_t *host) { this->packet->set_source(this->packet, host); } METHOD(message_t, set_destination, void, private_message_t *this, host_t *host) { this->packet->set_destination(this->packet, host); } METHOD(message_t, get_source, host_t*, private_message_t *this) { return this->packet->get_source(this->packet); } METHOD(message_t, get_destination, host_t*, private_message_t *this) { return this->packet->get_destination(this->packet); } METHOD(message_t, create_payload_enumerator, enumerator_t*, private_message_t *this) { return this->payloads->create_enumerator(this->payloads); } METHOD(message_t, remove_payload_at, void, private_message_t *this, enumerator_t *enumerator) { this->payloads->remove_at(this->payloads, enumerator); } METHOD(message_t, get_payload, payload_t*, private_message_t *this, payload_type_t type) { payload_t *current, *found = NULL; enumerator_t *enumerator; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, ¤t)) { if (current->get_type(current) == type) { found = current; break; } } enumerator->destroy(enumerator); return found; } METHOD(message_t, get_notify, notify_payload_t*, private_message_t *this, notify_type_t type) { enumerator_t *enumerator; notify_payload_t *notify = NULL; payload_t *payload; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &payload)) { if (payload->get_type(payload) == PLV2_NOTIFY || payload->get_type(payload) == PLV1_NOTIFY) { notify = (notify_payload_t*)payload; if (notify->get_notify_type(notify) == type) { break; } notify = NULL; } } enumerator->destroy(enumerator); return notify; } /** * get a string representation of the message */ static char* get_string(private_message_t *this, char *buf, int len) { enumerator_t *enumerator; payload_t *payload; int written; char *pos = buf; memset(buf, 0, len); len--; written = snprintf(pos, len, "%N %s %u [", exchange_type_names, this->exchange_type, this->is_request ? "request" : "response", this->message_id); if (written >= len || written < 0) { return ""; } pos += written; len -= written; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &payload)) { written = snprintf(pos, len, " %N", payload_type_short_names, payload->get_type(payload)); if (written >= len || written < 0) { return buf; } pos += written; len -= written; if (payload->get_type(payload) == PLV2_NOTIFY || payload->get_type(payload) == PLV1_NOTIFY) { notify_payload_t *notify; notify_type_t type; chunk_t data; notify = (notify_payload_t*)payload; type = notify->get_notify_type(notify); data = notify->get_notification_data(notify); if (type == MS_NOTIFY_STATUS && data.len == 4) { written = snprintf(pos, len, "(%N(%d))", notify_type_short_names, type, untoh32(data.ptr)); } else { written = snprintf(pos, len, "(%N)", notify_type_short_names, type); } if (written >= len || written < 0) { return buf; } pos += written; len -= written; } if (payload->get_type(payload) == PLV2_EAP) { eap_payload_t *eap = (eap_payload_t*)payload; uint32_t vendor; eap_type_t type; char method[64] = ""; type = eap->get_type(eap, &vendor); if (type) { if (vendor) { snprintf(method, sizeof(method), "/%d-%d", type, vendor); } else { snprintf(method, sizeof(method), "/%N", eap_type_short_names, type); } } written = snprintf(pos, len, "/%N%s", eap_code_short_names, eap->get_code(eap), method); if (written >= len || written < 0) { return buf; } pos += written; len -= written; } if (payload->get_type(payload) == PLV2_CONFIGURATION || payload->get_type(payload) == PLV1_CONFIGURATION) { cp_payload_t *cp = (cp_payload_t*)payload; enumerator_t *attributes; configuration_attribute_t *attribute; bool first = TRUE; char *pfx; switch (cp->get_type(cp)) { case CFG_REQUEST: pfx = "RQ("; break; case CFG_REPLY: pfx = "RP("; break; case CFG_SET: pfx = "S("; break; case CFG_ACK: pfx = "A("; break; default: pfx = "("; break; } attributes = cp->create_attribute_enumerator(cp); while (attributes->enumerate(attributes, &attribute)) { written = snprintf(pos, len, "%s%N", first ? pfx : " ", configuration_attribute_type_short_names, attribute->get_type(attribute)); if (written >= len || written < 0) { return buf; } pos += written; len -= written; first = FALSE; } attributes->destroy(attributes); if (!first) { written = snprintf(pos, len, ")"); if (written >= len || written < 0) { return buf; } pos += written; len -= written; } } if (payload->get_type(payload) == PLV1_FRAGMENT) { fragment_payload_t *frag; frag = (fragment_payload_t*)payload; if (frag->is_last(frag)) { written = snprintf(pos, len, "(%u/%u)", frag->get_number(frag), frag->get_number(frag)); } else { written = snprintf(pos, len, "(%u)", frag->get_number(frag)); } if (written >= len || written < 0) { return buf; } pos += written; len -= written; } if (payload->get_type(payload) == PLV2_FRAGMENT) { encrypted_fragment_payload_t *frag; frag = (encrypted_fragment_payload_t*)payload; written = snprintf(pos, len, "(%u/%u)", frag->get_fragment_number(frag), frag->get_total_fragments(frag)); if (written >= len || written < 0) { return buf; } pos += written; len -= written; } if (payload->get_type(payload) == PL_UNKNOWN) { unknown_payload_t *unknown; unknown = (unknown_payload_t*)payload; written = snprintf(pos, len, "(%d)", unknown->get_type(unknown)); if (written >= len || written < 0) { return buf; } pos += written; len -= written; } } enumerator->destroy(enumerator); /* remove last space */ snprintf(pos, len, " ]"); return buf; } METHOD(message_t, disable_sort, void, private_message_t *this) { this->sort_disabled = TRUE; } /** * reorder payloads depending on reordering rules */ static void order_payloads(private_message_t *this) { linked_list_t *list; payload_t *payload; int i; DBG2(DBG_ENC, "order payloads in message"); /* move to temp list */ list = linked_list_create(); while (this->payloads->remove_last(this->payloads, (void**)&payload) == SUCCESS) { list->insert_first(list, payload); } /* for each rule, ... */ for (i = 0; i < this->rule->order_count; i++) { enumerator_t *enumerator; notify_payload_t *notify; payload_order_t order; order = this->rule->order[i]; /* ... find all payload ... */ enumerator = list->create_enumerator(list); while (enumerator->enumerate(enumerator, &payload)) { /* ... with that type ... */ if (payload->get_type(payload) == order.type) { notify = (notify_payload_t*)payload; /**... and check notify for type. */ if (order.type != PLV2_NOTIFY || order.notify == 0 || order.notify == notify->get_notify_type(notify)) { list->remove_at(list, enumerator); add_payload(this, payload); } } } enumerator->destroy(enumerator); } /* append all payloads without a rule to the end */ while (list->remove_last(list, (void**)&payload) == SUCCESS) { /* do not complain about payloads in private use space */ if (payload->get_type(payload) < 128) { DBG1(DBG_ENC, "payload %N has no ordering rule in %N %s", payload_type_names, payload->get_type(payload), exchange_type_names, this->rule->exchange_type, this->rule->is_request ? "request" : "response"); } add_payload(this, payload); } list->destroy(list); } /** * Wrap payloads in an encrypted payload */ static encrypted_payload_t* wrap_payloads(private_message_t *this) { encrypted_payload_t *encrypted = NULL; linked_list_t *payloads; payload_t *current; /* move all payloads to a temporary list */ payloads = linked_list_create(); while (this->payloads->remove_first(this->payloads, (void**)¤t) == SUCCESS) { if (current->get_type(current) == PLV2_FRAGMENT) { /* treat encrypted fragment payload as encrypted payload */ encrypted = (encrypted_payload_t*)current; } else { payloads->insert_last(payloads, current); } } if (encrypted) { /* simply adopt all the unencrypted payloads */ this->payloads->destroy(this->payloads); this->payloads = payloads; return encrypted; } if (this->is_encrypted) { encrypted = encrypted_payload_create(PLV1_ENCRYPTED); } else { encrypted = encrypted_payload_create(PLV2_ENCRYPTED); } while (payloads->remove_first(payloads, (void**)¤t) == SUCCESS) { payload_rule_t *rule; payload_type_t type; bool encrypt = TRUE; type = current->get_type(current); rule = get_payload_rule(this, type); if (rule) { encrypt = rule->encrypted; } if (encrypt || this->is_encrypted) { /* encryption is forced for IKEv1 */ DBG2(DBG_ENC, "insert payload %N into encrypted payload", payload_type_names, type); encrypted->add_payload(encrypted, current); } else { DBG2(DBG_ENC, "insert payload %N unencrypted", payload_type_names, type); add_payload(this, current); } } payloads->destroy(payloads); return encrypted; } /** * Creates the IKE header for this message */ static ike_header_t *create_header(private_message_t *this) { ike_header_t *ike_header; bool *reserved; int i; 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); 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, this->ike_sa_id->get_responder_spi(this->ike_sa_id)); for (i = 0; i < countof(this->reserved); i++) { reserved = payload_get_field(&ike_header->payload_interface, RESERVED_BIT, i); if (reserved) { *reserved = this->reserved[i]; } } return ike_header; } /** * Generates the message, if needed, wraps the payloads in an encrypted payload. * * The generator and the possible encrypted payload are returned. The latter * is not yet encrypted (but the transform is set). It is also not added to * the payload list (so unless there are unencrypted payloads that list will * be empty afterwards). */ static status_t generate_message(private_message_t *this, keymat_t *keymat, generator_t **out_generator, encrypted_payload_t **encrypted) { keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; generator_t *generator; payload_type_t next_type; enumerator_t *enumerator; aead_t *aead = NULL; chunk_t hash = chunk_empty; char str[BUF_LEN]; ike_header_t *ike_header; payload_t *payload, *next; bool encrypting = FALSE; if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) { DBG1(DBG_ENC, "exchange type is not defined"); return INVALID_STATE; } if (this->packet->get_source(this->packet) == NULL || this->packet->get_destination(this->packet) == NULL) { DBG1(DBG_ENC, "source/destination not defined"); return INVALID_STATE; } this->rule = get_message_rule(this); if (!this->rule) { DBG1(DBG_ENC, "no message rules specified for this message type"); return NOT_SUPPORTED; } if (!this->sort_disabled) { 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(PLV1_HASH); hash_payload->set_hash(hash_payload, hash); this->payloads->insert_first(this->payloads, hash_payload); if (this->exchange_type == INFORMATIONAL_V1) { this->is_encrypted = encrypting = TRUE; } chunk_free(&hash); } } if (this->major_version == IKEV2_MAJOR_VERSION) { encrypting = this->rule->encrypted; } else if (!encrypting) { /* 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 = encrypting = TRUE; break; } } enumerator->destroy(enumerator); } DBG1(DBG_ENC, "generating %s", get_string(this, str, sizeof(str))); if (keymat) { aead = keymat->get_aead(keymat, FALSE); } if (encrypting) { if (aead) { *encrypted = wrap_payloads(this); (*encrypted)->set_transform(*encrypted, aead); } else if (this->exchange_type == INFORMATIONAL || this->exchange_type == INFORMATIONAL_V1) { /* allow sending unencrypted INFORMATIONALs */ encrypting = FALSE; } else { DBG1(DBG_ENC, "unable to encrypt payloads without AEAD transform"); return FAILED; } } if (!encrypting) { DBG2(DBG_ENC, "not encrypting payloads"); this->is_encrypted = FALSE; } /* generate all payloads with proper next type */ *out_generator = generator = generator_create(); ike_header = create_header(this); payload = (payload_t*)ike_header; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &next)) { payload->set_next_type(payload, next->get_type(next)); generator->generate_payload(generator, payload); payload = next; } enumerator->destroy(enumerator); next_type = PL_NONE; if (this->is_encrypted) { /* for encrypted IKEv1 messages */ next_type = (*encrypted)->payload_interface.get_next_type( (payload_t*)*encrypted); } else if (*encrypted) { /* use proper IKEv2 encrypted (fragment) payload type */ next_type = (*encrypted)->payload_interface.get_type( (payload_t*)*encrypted); } payload->set_next_type(payload, next_type); generator->generate_payload(generator, payload); ike_header->destroy(ike_header); return SUCCESS; } /** * Encrypts and adds the encrypted payload (if any) to the payload list and * finalizes the message generation. Destroys the given generator. */ static status_t finalize_message(private_message_t *this, keymat_t *keymat, generator_t *generator, encrypted_payload_t *encrypted) { keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; chunk_t chunk; uint32_t *lenpos; if (encrypted) { 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); encrypted->destroy(encrypted); return FAILED; } } else { /* build associated data (without header of encrypted payload) */ chunk = generator->get_chunk(generator, &lenpos); /* fill in length, including encrypted payload */ htoun32(lenpos, chunk.len + encrypted->get_length(encrypted)); } this->payloads->insert_last(this->payloads, encrypted); if (encrypted->encrypt(encrypted, this->message_id, chunk) != SUCCESS) { generator->destroy(generator); return INVALID_STATE; } generator->generate_payload(generator, &encrypted->payload_interface); } chunk = generator->get_chunk(generator, &lenpos); htoun32(lenpos, chunk.len); this->packet->set_data(this->packet, chunk_clone(chunk)); if (this->is_encrypted && this->exchange_type != INFORMATIONAL_V1) { /* update the IV for the next IKEv1 message */ chunk_t last_block; aead_t *aead; size_t bs; aead = keymat->get_aead(keymat, FALSE); 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); return SUCCESS; } METHOD(message_t, generate, status_t, private_message_t *this, keymat_t *keymat, packet_t **packet) { generator_t *generator = NULL; encrypted_payload_t *encrypted = NULL; status_t status; status = generate_message(this, keymat, &generator, &encrypted); if (status != SUCCESS) { DESTROY_IF(generator); return status; } status = finalize_message(this, keymat, generator, encrypted); if (status != SUCCESS) { return status; } if (packet) { *packet = this->packet->clone(this->packet); } return SUCCESS; } /** * Creates a (basic) clone of the given message */ static message_t *clone_message(private_message_t *this) { message_t *message; host_t *src, *dst; src = this->packet->get_source(this->packet); dst = this->packet->get_destination(this->packet); message = message_create(this->major_version, this->minor_version); message->set_ike_sa_id(message, this->ike_sa_id); message->set_message_id(message, this->message_id); message->set_request(message, this->is_request); message->set_source(message, src->clone(src)); message->set_destination(message, dst->clone(dst)); message->set_exchange_type(message, this->exchange_type); memcpy(((private_message_t*)message)->reserved, this->reserved, sizeof(this->reserved)); return message; } /** * Create a single fragment with the given data */ static message_t *create_fragment(private_message_t *this, payload_type_t next, uint16_t num, uint16_t count, chunk_t data) { enumerator_t *enumerator; payload_t *fragment, *payload; message_t *message; peer_cfg_t *peer_cfg; ike_sa_t *ike_sa; message = clone_message(this); if (this->major_version == IKEV1_MAJOR_VERSION) { /* other implementations seem to just use 0 as message ID, so here we go */ message->set_message_id(message, 0); /* always use the initial message type for fragments, even for quick mode * or transaction messages. */ ike_sa = charon->bus->get_sa(charon->bus); if (ike_sa && (peer_cfg = ike_sa->get_peer_cfg(ike_sa)) && peer_cfg->use_aggressive(peer_cfg)) { message->set_exchange_type(message, AGGRESSIVE); } else { message->set_exchange_type(message, ID_PROT); } fragment = (payload_t*)fragment_payload_create_from_data( num, num == count, data); } else { fragment = (payload_t*)encrypted_fragment_payload_create_from_data( num, count, data); if (num == 1) { /* only in the first fragment is this set to the type of the first * payload in the encrypted payload */ fragment->set_next_type(fragment, next); /* move unencrypted payloads to the first fragment */ enumerator = this->payloads->create_enumerator(this->payloads); while (enumerator->enumerate(enumerator, &payload)) { if (payload->get_type(payload) != PLV2_ENCRYPTED) { this->payloads->remove_at(this->payloads, enumerator); message->add_payload(message, payload); } } enumerator->destroy(enumerator); } } message->add_payload(message, (payload_t*)fragment); return message; } /** * Destroy all fragments */ static void clear_fragments(private_message_t *this) { array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); this->fragments = NULL; } /** * Reduce the fragment length but ensure it stays > 0 */ #define REDUCE_FRAG_LEN(fl, amount) ({ \ fl = max(1, (ssize_t)fl - (amount)); \ }) METHOD(message_t, fragment, status_t, private_message_t *this, keymat_t *keymat, size_t frag_len, enumerator_t **fragments) { encrypted_payload_t *encrypted = NULL; generator_t *generator = NULL; message_t *fragment; packet_t *packet; payload_type_t next = PL_NONE; uint16_t num, count; host_t *src, *dst; chunk_t data; status_t status; uint32_t *lenpos; size_t len; src = this->packet->get_source(this->packet); dst = this->packet->get_destination(this->packet); if (!frag_len) { frag_len = (src->get_family(src) == AF_INET) ? 576 : 1280; } /* frag_len is the complete IP datagram length, account for overhead (we * assume no IP options/extension headers are used) */ REDUCE_FRAG_LEN(frag_len, (src->get_family(src) == AF_INET) ? 20 : 40); /* 8 (UDP header) */ REDUCE_FRAG_LEN(frag_len, 8); if (dst->get_port(dst) != IKEV2_UDP_PORT && src->get_port(src) != IKEV2_UDP_PORT) { /* reduce length due to non-ESP marker */ REDUCE_FRAG_LEN(frag_len, 4); } if (is_encoded(this)) { if (this->major_version == IKEV2_MAJOR_VERSION) { encrypted = (encrypted_payload_t*)get_payload(this, PLV2_ENCRYPTED); } data = this->packet->get_data(this->packet); len = data.len; } else { status = generate_message(this, keymat, &generator, &encrypted); if (status != SUCCESS) { DESTROY_IF(generator); return status; } data = generator->get_chunk(generator, &lenpos); len = data.len + (encrypted ? encrypted->get_length(encrypted) : 0); } /* check if we actually need to fragment the message and if we have an * encrypted payload for IKEv2 */ if (len <= frag_len || (this->major_version == IKEV2_MAJOR_VERSION && !encrypted)) { if (generator) { status = finalize_message(this, keymat, generator, encrypted); if (status != SUCCESS) { return status; } } *fragments = enumerator_create_single(this->packet, NULL); return SUCCESS; } /* frag_len denoted the maximum IKE message size so far, later on it will * denote the maximum content size of a fragment payload, therefore, * account for IKE header */ REDUCE_FRAG_LEN(frag_len, 28); if (this->major_version == IKEV1_MAJOR_VERSION) { if (generator) { status = finalize_message(this, keymat, generator, encrypted); if (status != SUCCESS) { return status; } data = this->packet->get_data(this->packet); generator = NULL; } /* overhead for the fragmentation payload header */ REDUCE_FRAG_LEN(frag_len, 8); } else { aead_t *aead; if (generator) { generator->destroy(generator); generator = generator_create(); } else { /* do not log again if it was generated previously */ generator = generator_create_no_dbg(); } next = encrypted->payload_interface.get_next_type((payload_t*)encrypted); encrypted->generate_payloads(encrypted, generator); data = generator->get_chunk(generator, &lenpos); if (!is_encoded(this)) { encrypted->destroy(encrypted); } aead = keymat->get_aead(keymat, FALSE); /* overhead for the encrypted fragment payload */ REDUCE_FRAG_LEN(frag_len, aead->get_iv_size(aead)); REDUCE_FRAG_LEN(frag_len, aead->get_icv_size(aead)); /* header */ REDUCE_FRAG_LEN(frag_len, 8); /* padding and padding length */ frag_len = round_down(frag_len, aead->get_block_size(aead)); REDUCE_FRAG_LEN(frag_len, 1); /* TODO-FRAG: if there are unencrypted payloads, should we account for * their length in the first fragment? we still would have to add * an encrypted fragment payload (albeit empty), even so we couldn't * prevent IP fragmentation in every case */ } count = data.len / frag_len + (data.len % frag_len ? 1 : 0); this->fragments = array_create(0, count); DBG1(DBG_ENC, "splitting IKE message (%zu bytes) into %hu fragments", len, count); for (num = 1; num <= count; num++) { len = min(data.len, frag_len); fragment = create_fragment(this, next, num, count, chunk_create(data.ptr, len)); status = fragment->generate(fragment, keymat, &packet); fragment->destroy(fragment); if (status != SUCCESS) { DBG1(DBG_ENC, "failed to generate IKE fragment"); clear_fragments(this); DESTROY_IF(generator); return FAILED; } array_insert(this->fragments, ARRAY_TAIL, packet); data = chunk_skip(data, len); } *fragments = array_create_enumerator(this->fragments); DESTROY_IF(generator); return SUCCESS; } METHOD(message_t, get_packet, packet_t*, private_message_t *this) { return this->packet->clone(this->packet); } METHOD(message_t, get_packet_data, chunk_t, private_message_t *this) { return this->packet->get_data(this->packet); } METHOD(message_t, get_fragments, enumerator_t*, private_message_t *this) { return array_create_enumerator(this->fragments); } METHOD(message_t, parse_header, status_t, private_message_t *this) { ike_header_t *ike_header; status_t status; bool *reserved; int i; DBG2(DBG_ENC, "parsing header of message"); if (!this->parser) { /* reassembled IKEv2 message, header is inherited from fragments */ return SUCCESS; } this->parser->reset_context(this->parser); status = this->parser->parse_payload(this->parser, PL_HEADER, (payload_t**)&ike_header); if (status != SUCCESS) { DBG1(DBG_ENC, "header could not be parsed"); return status; } status = ike_header->payload_interface.verify( &ike_header->payload_interface); if (status != SUCCESS) { DBG1(DBG_ENC, "header verification failed"); ike_header->destroy(ike_header); return status; } DESTROY_IF(this->ike_sa_id); 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->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); if (this->first_payload == PLV1_FRAGMENT && this->is_encrypted) { /* racoon sets the encrypted bit when sending a fragment, but these * messages are really not encrypted */ this->is_encrypted = FALSE; } for (i = 0; i < countof(this->reserved); i++) { reserved = payload_get_field(&ike_header->payload_interface, RESERVED_BIT, i); if (reserved) { this->reserved[i] = *reserved; } } ike_header->destroy(ike_header); this->parser->set_major_version(this->parser, this->major_version); 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; } /** * Check if a payload is for a mediation extension connectivity check */ static bool is_connectivity_check(private_message_t *this, payload_t *payload) { #ifdef ME if (this->exchange_type == INFORMATIONAL && payload->get_type(payload) == PLV2_NOTIFY) { notify_payload_t *notify = (notify_payload_t*)payload; switch (notify->get_notify_type(notify)) { case ME_CONNECTID: case ME_ENDPOINT: case ME_CONNECTAUTH: return TRUE; default: break; } } #endif /* !ME */ return FALSE; } /** * 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 encrypted * payload which is then handled just like a regular payload */ encrypted_payload_t *encryption; status = this->parser->parse_payload(this->parser, PLV1_ENCRYPTED, (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 != PL_NONE) { 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; } if (payload->get_type(payload) == PL_UNKNOWN) { DBG2(DBG_ENC, "%N payload unknown or not allowed", payload_type_names, type); } else { DBG2(DBG_ENC, "%N payload verified, adding to payload list", payload_type_names, type); } this->payloads->insert_last(this->payloads, payload); /* an encrypted (fragment) payload MUST be the last one, so STOP here. * decryption is done later */ if (type == PLV2_ENCRYPTED || type == PLV2_FRAGMENT) { DBG2(DBG_ENC, "%N payload found, stop parsing", payload_type_names, type); break; } type = payload->get_next_type(payload); } return SUCCESS; } /** * Decrypt an encrypted payload and extract all contained payloads. */ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat, payload_t *previous, encrypted_payload_t *encryption) { payload_t *encrypted; payload_type_t type; chunk_t chunk; aead_t *aead; size_t bs; status_t status = SUCCESS; if (!keymat) { DBG1(DBG_ENC, "found encrypted payload, but no keymat"); return INVALID_ARG; } aead = keymat->get_aead(keymat, TRUE); if (!aead) { DBG1(DBG_ENC, "found encrypted payload, but no transform set"); return INVALID_ARG; } if (!this->parser) { /* reassembled IKEv2 messages are already decrypted, we still call * decrypt() to parse the contained payloads */ status = encryption->decrypt(encryption, chunk_empty); } else { 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) || chunk.len < bs) { DBG1(DBG_ENC, "invalid payload length"); return VERIFY_ERROR; } 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) { return status; } while ((encrypted = encryption->remove_payload(encryption))) { type = encrypted->get_type(encrypted); if (previous) { previous->set_next_type(previous, type); } else { this->first_payload = type; } DBG2(DBG_ENC, "insert decrypted payload of type %N at end of list", payload_type_names, type); this->payloads->insert_last(this->payloads, encrypted); previous = encrypted; } return SUCCESS; } /** * Decrypt an encrypted fragment payload. */ static status_t decrypt_fragment(private_message_t *this, keymat_t *keymat, encrypted_fragment_payload_t *fragment) { encrypted_payload_t *encrypted = (encrypted_payload_t*)fragment; chunk_t chunk; aead_t *aead; size_t bs; if (!keymat) { DBG1(DBG_ENC, "found encrypted fragment payload, but no keymat"); return INVALID_ARG; } aead = keymat->get_aead(keymat, TRUE); if (!aead) { DBG1(DBG_ENC, "found encrypted fragment payload, but no transform set"); return INVALID_ARG; } bs = aead->get_block_size(aead); encrypted->set_transform(encrypted, aead); chunk = this->packet->get_data(this->packet); if (chunk.len < encrypted->get_length(encrypted) || chunk.len < bs) { DBG1(DBG_ENC, "invalid payload length"); return VERIFY_ERROR; } chunk.len -= encrypted->get_length(encrypted); return encrypted->decrypt(encrypted, chunk); } /** * Do we accept unencrypted ID/HASH payloads in Main Mode, as seen from * some SonicWall boxes? */ static bool accept_unencrypted_mm(private_message_t *this, payload_type_t type) { if (this->exchange_type == ID_PROT) { if (type == PLV1_ID || type == PLV1_HASH) { return lib->settings->get_bool(lib->settings, "%s.accept_unencrypted_mainmode_messages", FALSE, lib->ns); } } return FALSE; } /** * Decrypt payload from the encrypted payload */ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) { payload_t *payload, *previous = NULL; enumerator_t *enumerator; payload_rule_t *rule; payload_type_t type; status_t status = SUCCESS; char *was_encrypted = NULL; enumerator = this->payloads->create_enumerator(this->payloads); while (enumerator->enumerate(enumerator, &payload)) { type = payload->get_type(payload); DBG2(DBG_ENC, "process payload of type %N", payload_type_names, type); if (type == PLV2_ENCRYPTED || type == PLV1_ENCRYPTED || type == PLV2_FRAGMENT) { if (was_encrypted) { DBG1(DBG_ENC, "%s can't contain other payloads of type %N", was_encrypted, payload_type_names, type); status = VERIFY_ERROR; break; } } if (type == PLV2_ENCRYPTED || type == PLV1_ENCRYPTED) { encrypted_payload_t *encryption; DBG2(DBG_ENC, "found an encrypted payload"); encryption = (encrypted_payload_t*)payload; this->payloads->remove_at(this->payloads, enumerator); if (enumerator->enumerate(enumerator, NULL)) { DBG1(DBG_ENC, "encrypted payload is not last payload"); encryption->destroy(encryption); status = VERIFY_ERROR; break; } status = decrypt_and_extract(this, keymat, previous, encryption); encryption->destroy(encryption); if (status != SUCCESS) { break; } was_encrypted = "encrypted payload"; } else if (type == PLV2_FRAGMENT) { encrypted_fragment_payload_t *fragment; DBG2(DBG_ENC, "found an encrypted fragment payload"); fragment = (encrypted_fragment_payload_t*)payload; if (enumerator->enumerate(enumerator, NULL)) { DBG1(DBG_ENC, "encrypted fragment payload is not last payload"); status = VERIFY_ERROR; break; } status = decrypt_fragment(this, keymat, fragment); if (status != SUCCESS) { break; } was_encrypted = "encrypted fragment payload"; } if (type != PL_UNKNOWN && !was_encrypted && !is_connectivity_check(this, payload) && this->exchange_type != AGGRESSIVE) { rule = get_payload_rule(this, type); if ((!rule || rule->encrypted) && !accept_unencrypted_mm(this, type)) { DBG1(DBG_ENC, "payload type %N was not encrypted", payload_type_names, type); status = FAILED; break; } } previous = payload; } enumerator->destroy(enumerator); return status; } /** * Verify a message and all payload according to message/payload rules */ static status_t verify(private_message_t *this) { bool complete = FALSE; int i; DBG2(DBG_ENC, "verifying message structure"); /* check for payloads with wrong count */ for (i = 0; i < this->rule->rule_count; i++) { enumerator_t *enumerator; payload_t *payload; payload_rule_t *rule; int found = 0; rule = &this->rule->rules[i]; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &payload)) { payload_type_t type; type = payload->get_type(payload); if (type == rule->type) { found++; DBG2(DBG_ENC, "found payload of type %N", payload_type_names, type); if (found > rule->max_occurrence) { DBG1(DBG_ENC, "payload of type %N more than %d times (%d) " "occurred in current message", payload_type_names, type, rule->max_occurrence, found); enumerator->destroy(enumerator); return VERIFY_ERROR; } } } enumerator->destroy(enumerator); if (!complete && found < rule->min_occurrence) { DBG1(DBG_ENC, "payload of type %N not occurred %d times (%d)", payload_type_names, rule->type, rule->min_occurrence, found); return VERIFY_ERROR; } if (found && rule->sufficient) { complete = TRUE; } } return SUCCESS; } METHOD(message_t, parse_body, status_t, private_message_t *this, keymat_t *keymat) { status_t status = SUCCESS; char str[BUF_LEN]; DBG2(DBG_ENC, "parsing body of message, first payload is %N", payload_type_names, this->first_payload); 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 NOT_SUPPORTED; } /* reassembled IKEv2 messages are already parsed (except for the payloads * contained in the encrypted payload, which are handled below) */ if (this->parser) { status = parse_payloads(this); if (status != SUCCESS) { /* error is already logged */ return status; } } status = decrypt_payloads(this, keymat); if (status != SUCCESS) { DBG1(DBG_ENC, "could not decrypt payloads"); return status; } status = verify(this); if (status != SUCCESS) { return status; } 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 != PLV1_HASH) { 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, PLV1_HASH); other_hash = hash_payload->get_hash(hash_payload); DBG3(DBG_ENC, "HASH received %B\nHASH expected %B", &other_hash, &hash); if (!chunk_equals_const(hash, other_hash)) { DBG1(DBG_ENC, "received HASH payload does not match"); chunk_free(&hash); return FAILED; } chunk_free(&hash); } if (this->is_encrypted && this->exchange_type != INFORMATIONAL_V1) { /* message verified, confirm IV */ if (!keymat_v1->confirm_iv(keymat_v1, this->message_id)) { return FAILED; } } } return SUCCESS; } /** * Store the fragment data for the fragment with the given fragment number. */ static status_t add_fragment(private_message_t *this, uint16_t num, chunk_t data) { fragment_t *fragment; int i, insert_at = -1; for (i = 0; i < array_count(this->fragments); i++) { array_get(this->fragments, i, &fragment); if (fragment->num == num) { /* ignore a duplicate fragment */ DBG1(DBG_ENC, "received duplicate fragment #%hu", num); return NEED_MORE; } if (fragment->num > num) { insert_at = i; break; } } this->frag->len += data.len; if (this->frag->len > this->frag->max_packet) { DBG1(DBG_ENC, "fragmented IKE message is too large"); reset_defrag(this); return FAILED; } INIT(fragment, .num = num, .data = chunk_clone(data), ); array_insert(this->fragments, insert_at, fragment); return SUCCESS; } /** * Merge the cached fragment data and resets the defragmentation state. * Also updates the IP addresses to those of the last received fragment. */ static chunk_t merge_fragments(private_message_t *this, message_t *last) { fragment_t *fragment; bio_writer_t *writer; host_t *src, *dst; chunk_t data; int i; writer = bio_writer_create(this->frag->len); for (i = 0; i < array_count(this->fragments); i++) { array_get(this->fragments, i, &fragment); writer->write_data(writer, fragment->data); } data = writer->extract_buf(writer); writer->destroy(writer); /* set addresses to those of the last fragment we received */ src = last->get_source(last); dst = last->get_destination(last); this->packet->set_source(this->packet, src->clone(src)); this->packet->set_destination(this->packet, dst->clone(dst)); reset_defrag(this); free(this->frag); this->frag = NULL; return data; } METHOD(message_t, add_fragment_v1, status_t, private_message_t *this, message_t *message) { fragment_payload_t *payload; chunk_t data; uint8_t num; status_t status; if (!this->frag) { return INVALID_STATE; } payload = (fragment_payload_t*)message->get_payload(message, PLV1_FRAGMENT); if (!payload) { return INVALID_ARG; } if (!this->fragments || this->message_id != payload->get_id(payload)) { reset_defrag(this); this->message_id = payload->get_id(payload); /* we don't know the total number of fragments, assume something */ this->fragments = array_create(0, 4); } num = payload->get_number(payload); data = payload->get_data(payload); if (!this->frag->last && payload->is_last(payload)) { this->frag->last = num; } status = add_fragment(this, num, data); if (status != SUCCESS) { return status; } if (array_count(this->fragments) != this->frag->last) { /* there are some fragments missing */ DBG1(DBG_ENC, "received fragment #%hhu, waiting for complete IKE " "message", num); return NEED_MORE; } data = merge_fragments(this, message); this->packet->set_data(this->packet, data); DBG1(DBG_ENC, "received fragment #%hhu, reassembled fragmented IKE " "message (%zu bytes)", num, data.len); this->parser = parser_create(data); if (parse_header(this) != SUCCESS) { DBG1(DBG_IKE, "failed to parse header of reassembled IKE message"); return FAILED; } return SUCCESS; } METHOD(message_t, add_fragment_v2, status_t, private_message_t *this, message_t *message) { encrypted_fragment_payload_t *encrypted_fragment; encrypted_payload_t *encrypted; payload_t *payload; aead_t *aead; enumerator_t *enumerator; chunk_t data; uint16_t total, num; size_t len; status_t status; if (!this->frag) { return INVALID_STATE; } payload = message->get_payload(message, PLV2_FRAGMENT); if (!payload || this->message_id != message->get_message_id(message)) { return INVALID_ARG; } encrypted_fragment = (encrypted_fragment_payload_t*)payload; total = encrypted_fragment->get_total_fragments(encrypted_fragment); if (total > MAX_FRAGMENTS) { DBG1(DBG_IKE, "maximum fragment count exceeded"); reset_defrag(this); return FAILED; } if (!this->fragments || total > this->frag->last) { reset_defrag(this); this->frag->last = total; this->fragments = array_create(0, total); } num = encrypted_fragment->get_fragment_number(encrypted_fragment); data = encrypted_fragment->get_content(encrypted_fragment); status = add_fragment(this, num, data); if (status != SUCCESS) { return status; } if (num == 1) { /* the first fragment denotes the payload type of the first payload in * the original encrypted payload, cache that */ this->first_payload = payload->get_next_type(payload); /* move all unencrypted payloads contained in the first fragment */ enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { if (payload->get_type(payload) != PLV2_FRAGMENT) { message->remove_payload_at(message, enumerator); this->payloads->insert_last(this->payloads, payload); } } enumerator->destroy(enumerator); } if (array_count(this->fragments) != total) { /* there are some fragments missing */ DBG1(DBG_ENC, "received fragment #%hu of %hu, waiting for complete IKE " "message", num, total); return NEED_MORE; } encrypted = (encrypted_payload_t*)encrypted_fragment; aead = encrypted->get_transform(encrypted); data = merge_fragments(this, message); encrypted = encrypted_payload_create_from_plain(this->first_payload, data); encrypted->set_transform(encrypted, aead); this->payloads->insert_last(this->payloads, encrypted); /* update next payload type (could be an unencrypted payload) */ this->payloads->get_first(this->payloads, (void**)&payload); this->first_payload = payload->get_type(payload); /* we report the length of the complete IKE message when splitting, do the * same here, so add the IKEv2 header len to the reassembled payload data */ len = 28; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &payload)) { len += payload->get_length(payload); } enumerator->destroy(enumerator); DBG1(DBG_ENC, "received fragment #%hu of %hu, reassembled fragmented IKE " "message (%zu bytes)", num, total, len); return SUCCESS; } METHOD(message_t, destroy, void, private_message_t *this) { DESTROY_IF(this->ike_sa_id); DESTROY_IF(this->parser); this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); this->packet->destroy(this->packet); if (this->frag) { reset_defrag(this); free(this->frag); } else { array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); } free(this); } /* * Described in header. */ message_t *message_create_from_packet(packet_t *packet) { private_message_t *this; INIT(this, .public = { .set_major_version = _set_major_version, .get_major_version = _get_major_version, .set_minor_version = _set_minor_version, .get_minor_version = _get_minor_version, .set_message_id = _set_message_id, .get_message_id = _get_message_id, .get_initiator_spi = _get_initiator_spi, .get_responder_spi = _get_responder_spi, .set_ike_sa_id = _set_ike_sa_id, .get_ike_sa_id = _get_ike_sa_id, .set_exchange_type = _set_exchange_type, .get_exchange_type = _get_exchange_type, .get_first_payload_type = _get_first_payload_type, .set_request = _set_request, .get_request = _get_request, .set_version_flag = _set_version_flag, .get_reserved_header_bit = _get_reserved_header_bit, .set_reserved_header_bit = _set_reserved_header_bit, .add_payload = _add_payload, .add_notify = _add_notify, .disable_sort = _disable_sort, .generate = _generate, .is_encoded = _is_encoded, .is_fragmented = _is_fragmented, .fragment = _fragment, .add_fragment = _add_fragment_v2, .set_source = _set_source, .get_source = _get_source, .set_destination = _set_destination, .get_destination = _get_destination, .create_payload_enumerator = _create_payload_enumerator, .remove_payload_at = _remove_payload_at, .get_payload = _get_payload, .get_notify = _get_notify, .parse_header = _parse_header, .parse_body = _parse_body, .get_packet = _get_packet, .get_packet_data = _get_packet_data, .get_fragments = _get_fragments, .destroy = _destroy, }, .exchange_type = EXCHANGE_TYPE_UNDEFINED, .is_request = TRUE, .first_payload = PL_NONE, .packet = packet, .payloads = linked_list_create(), .parser = parser_create(packet->get_data(packet)), ); return &this->public; } /* * Described in header. */ message_t *message_create(int major, int minor) { message_t *this = message_create_from_packet(packet_create()); this->set_major_version(this, major); this->set_minor_version(this, minor); return this; } /* * Described in header. */ message_t *message_create_defrag(message_t *fragment) { private_message_t *this; if (!fragment->get_payload(fragment, PLV1_FRAGMENT) && !fragment->get_payload(fragment, PLV2_FRAGMENT)) { return NULL; } this = (private_message_t*)clone_message((private_message_t*)fragment); /* we don't need a parser for IKEv2, the one for IKEv1 is created after * reassembling the original message */ this->parser->destroy(this->parser); this->parser = NULL; if (fragment->get_major_version(fragment) == IKEV1_MAJOR_VERSION) { /* we store the fragment ID in the message ID field, which should be * zero for fragments, but make sure */ this->message_id = 0; this->public.add_fragment = _add_fragment_v1; } INIT(this->frag, .max_packet = lib->settings->get_int(lib->settings, "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), ); return &this->public; }