The only messages that are generally sent encrypted but could be sent unencrypted are INFORMATIONALs (currently only used for IKEv1 and ME connectivity checks). This should prevent issues if the keymat_t behaves incorrectly and does not return an aead_t when it actually should.
3072 lines
80 KiB
C
3072 lines
80 KiB
C
/*
|
|
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "message.h"
|
|
|
|
#include <library.h>
|
|
#include <bio/bio_writer.h>
|
|
#include <collections/array.h>
|
|
#include <daemon.h>
|
|
#include <sa/ikev1/keymat_v1.h>
|
|
#include <encoding/generator.h>
|
|
#include <encoding/parser.h>
|
|
#include <encoding/payloads/encodings.h>
|
|
#include <encoding/payloads/payload.h>
|
|
#include <encoding/payloads/hash_payload.h>
|
|
#include <encoding/payloads/encrypted_payload.h>
|
|
#include <encoding/payloads/encrypted_fragment_payload.h>
|
|
#include <encoding/payloads/unknown_payload.h>
|
|
#include <encoding/payloads/cp_payload.h>
|
|
#include <encoding/payloads/fragment_payload.h>
|
|
|
|
/**
|
|
* 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_occurence;
|
|
/* Max occurrence of this payload. */
|
|
size_t max_occurence;
|
|
/* 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 enrypted 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_occurence)
|
|
{
|
|
DBG1(DBG_ENC, "payload of type %N more than %d times (%d) "
|
|
"occurred in current message", payload_type_names,
|
|
type, rule->max_occurence, found);
|
|
enumerator->destroy(enumerator);
|
|
return VERIFY_ERROR;
|
|
}
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (!complete && found < rule->min_occurence)
|
|
{
|
|
DBG1(DBG_ENC, "payload of type %N not occurred %d times (%d)",
|
|
payload_type_names, rule->type, rule->min_occurence, 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;
|
|
}
|