Files
m241Neelima/components/ip_net2-6.9/ipike/src/ipike2_exchange_sa_init.c
T
2026-07-30 11:19:44 +05:30

3394 lines
117 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipike2_exchange_sa_init.c,v $ $Revision: 1.88.10.20 $
* $Source: /home/interpeak/CVSRoot/ipike/src/ipike2_exchange_sa_init.c,v $
* $Author: wdeng $ $Date: 2021-06-16 08:38:22 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipike_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#ifdef IPIKE_USE_VERSION2
#include "ipike_h.h"
#include "ipike2_exchange_sa_init.h"
#include "ipike2_exchange_child_sa.h"
#include "ipike2_exchange_info.h"
#include "ipike_crypto.h"
#include "ipike_attributes.h"
#include "ipike_payload.h"
#include "ipike2_payload_key_exchange.h"
#include "ipike2_payload_notify.h"
#include "ipike2_payload_sa.h"
#include "ipike_payload_nonce.h"
#include "ipike_sadb.h"
#include "ipike_util.h"
#include "ipike_ipsec.h"
#include "ipike_ipsec_attributes.h"
#include "ipike_configuration.h"
#include "ipike_policy.h"
#include "ipike_http_fetcher.h"
#include <openssl/x509.h>
#include <ipcom_clib.h>
#include <ipcom_sock2.h>
#include <ipcom_syslog.h>
#include <ipcom_sock2.h>
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
#define IPIKE_EAP_MAX_BUF 1400
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
*===========================================================================
* ipike_exchange_sa_init_http_get
*===========================================================================
* Description: Fetches the resource pointed out by the URI using HTTP.
* Parameters: ie - The ISAKMP exchange associated with this message.
* uri - URI pointing out the resource.
* callback - User callback which is called when the HTTP data
* has been received.
* Returns: IPIKE_SUCCESS or IPIKE_FAILURE
*
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_http_get(
Ipike_isakmp_exchange *ie,
const char *uri,
Ipike_http_fetcher_callback callback
);
/*
*===========================================================================
* ipike_exchange_sa_init_http_get_cert_cb
*===========================================================================
* Description: Callback function which is called when a certificate has
* been fetched using HTTP.
* Parameters: status - IPIKE_SUCCESS or IPIKE_FAILURE.
* data - HTTP content data.
* data_len - HTTP content data length.
* id - Callback context, a unique id that identifies
* an IKE exchange.
* Returns:
*
*/
IP_STATIC void
ipike_exchange_sa_init_http_get_cert_cb(
Ipike_error status,
const char *data,
Ip_size_t data_len,
void *id
);
/*
*===========================================================================
* ipike_exchange_sa_init_http_get_crl_cb
*===========================================================================
* Description: Callback function which is called when a CRL has been fetched
* using HTTP.
* Parameters: status - IPIKE_SUCCESS or IPIKE_FAILURE.
* data - HTTP content data.
* data_len - HTTP content data length.
* id - Callback context, a unique id that identifies
* an IKE exchange.
* Returns:
*
*/
IP_STATIC void
ipike_exchange_sa_init_http_get_crl_cb(
Ipike_error status,
const char *data,
Ip_size_t data_len,
void *id
);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipike_exchange_sa_init_print_keys
*===========================================================================
*/
IP_GLOBAL void
ipike_exchange_sa_init_print_keys(Ipike_keys *keys)
{
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key d: ",
keys->SK_d.value, keys->SK_d.len);
if (keys->SK_ai.len > 0 || keys->SK_ar.len > 0)
{
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key ai: ",
keys->SK_ai.value, keys->SK_ai.len);
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key ar: ",
keys->SK_ar.value, keys->SK_ar.len);
}
if (keys->SK_ei.len > 0 || keys->SK_er.len > 0)
{
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key ei: ",
keys->SK_ei.value, keys->SK_ei.len);
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key er: ",
keys->SK_er.value, keys->SK_er.len);
}
if (keys->SK_pi.len > 0 || keys->SK_pr.len > 0)
{
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key pi: ",
keys->SK_pi.value, keys->SK_pi.len);
(void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Key pr: ",
keys->SK_pr.value, keys->SK_pr.len);
}
}
/*
*===========================================================================
* ipike_exchange_sa_init_has_ike_sa_error
*===========================================================================
*/
IP_STATIC Ip_bool
ipike_exchange_sa_init_has_ike_sa_error(Ipike_isakmp_exchange *ie)
{
ip_assert(ie);
if ( ie->ike_sa->error != IPIKE_SUCCESS )
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* ipike_exchange_sa_init_has_child_sa_error
*===========================================================================
*/
IP_STATIC Ip_bool
ipike_exchange_sa_init_has_child_sa_error(Ipike_isakmp_exchange *ie)
{
ip_assert(ie);
if ( ie->ipsec_sa && ie->ipsec_sa->error != IPIKE_SUCCESS )
{
switch (ie->ipsec_sa->error)
{
case IPIKE_ISAKMP_NO_PROPOSAL_CHOSEN:
case IPIKE_ISAKMP_TS_UNACCEPTABLE:
case IPIKE_ISAKMP_INTERNAL_ADDRESS_FAILURE:
case IPIKE_ISAKMP_FAILED_CP_REQUIRED:
case IPIKE_ISAKMP_SINGLE_PAIR_REQUIRED:
return IP_TRUE;
default:
return IP_FALSE;
}
}
return IP_FALSE;
}
/*
*===========================================================================
* ipike_exchange_sa_init_has_eap
*===========================================================================
*/
IP_STATIC Ip_bool
ipike_exchange_sa_init_has_eap(Ipike_isakmp_exchange* ie)
{
#ifdef IPIKE_USE_EAP
/* Return true if EAP will be used as authentication method. This is true
* if we have an EAP payload or if third message and AUTH payload is
* missing. */
if (ie->message_number >= 3)
{
if (ie->ike_sa->is_initiator)
{
if ((ie->ike_sa->selected.auth_method == IPIKE2_AUTH_EAP) &&
((ie->ike_sa->v2.eap_ctx == IP_NULL) ||
(!ie->ike_sa->v2.eap_success)))
return IP_TRUE;
}
else
{
if ((ie->ike_sa->v2.eap.len > 0) ||
((ie->message_number == 3) &&
(ie->ike_sa->v2.auth.data.len == 0)))
return IP_TRUE;
}
}
#else
IPCOM_UNUSED_ARG(ie);
#endif
return IP_FALSE;
}
#ifdef IPIKE_USE_EAP
/*
*===========================================================================
* ipike_exchange_sa_init_new_eap
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_new_eap(Ipike_isakmp_exchange* ie)
{
const char* identity;
struct Ipeap_ctx_st** ctx;
Ip_bool initiator;
identity = ie->ike_conf->authentication.identity;
ctx = &ie->ike_sa->v2.eap_ctx;
initiator = ie->ike_sa->is_initiator;
if (initiator)
{
*ctx = ipeap_ctx_new(IPEAP_MODE_SUPPLICANT);
if (*ctx == IP_NULL)
{
IP_PANIC();
return IPIKE_OUT_OF_MEMORY;
}
else if ((identity == IP_NULL) || (identity[0] == 0))
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_new_eap() :: parameter"\
" 'identity' missing, check configuration");
return IPIKE_CONF_VALUE_NOT_FOUND;
}
if (ipeap_ctx_set_identity(*ctx, identity) != IPCOM_SUCCESS)
{
ipeap_ctx_free(*ctx);
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_new_eap() :: Bad 'identity'"\
" parameter, check configuration");
return IPIKE_CONF_INVALID_VALUE;
}
}
else
{
#ifdef IPIKE_USE_RADIUS
/* Open radius connection */
if ((ie->ike_sa->v2.radius_ctx = ipike_radius_create(
ie->ike_conf->ike_id,
&ie->ike_conf->radius.server_address,
&ie->ike_conf->radius.local_address,
ie->ike_conf->radius.shared_secret)) == IP_NULL)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_new_eap() :: failed to"\
" connect to radius server, check configuration");
return IPIKE_ISAKMP_AUTHENTICATION_FAILED;
}
#else
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_new_eap() :: radius not supported");
return IPIKE_ISAKMP_AUTHENTICATION_FAILED;
#endif /* IPIKE_USE_RADIUS */
}
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_sa_init_eap_get_key
*===========================================================================
*/
IP_STATIC void
ipike_exchange_sa_init_eap_get_key(
Ipike_isakmp_exchange* ie,
Ip_u8* key,
Ip_u32* key_len)
{
if (ie->ike_sa->is_initiator)
{
struct Ipeap_ctx_st* ctx = IP_NULL;
ctx = ie->ike_sa->v2.eap_ctx;
if (ipeap_get_key(ctx, key, (int) *key_len) != IPCOM_SUCCESS)
*key_len = 0;
}
else
{
#ifdef IPIKE_USE_RADIUS
ipike_radius_extract_key(ie->ike_sa->v2.radius_ctx, key, key_len);
#else
*key_len = 0;
#endif
}
(void)ipike_print_memory(IPCOM_LOG_DEBUG2,
"ipike_exchange_sa_init_eap_get_key() :: EAP key:\n",
key, *key_len);
}
/*
*===========================================================================
* ipike_exchange_sa_init_eap_process
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_eap_process(Ipike_isakmp_exchange* ie)
{
Ipike_error err = IPIKE_SUCCESS;
Ipike_value* msg = IP_NULL;
struct Ipeap_ctx_st* ctx = IP_NULL;
Ipeap_pkt pkt_in = {0};
Ipeap_pkt pkt_out = {0};
/* Create new context if not available, will either have a
* radius (responder) or eap (initiator) context. */
if (ie->ike_sa->v2.eap_ctx == IP_NULL &&
#ifdef IPIKE_USE_RADIUS
ie->ike_sa->v2.radius_ctx == IP_NULL &&
#endif
(IPIKE_SUCCESS != (err = ipike_exchange_sa_init_new_eap(ie))))
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_eap_process() ::"\
" failed to create new EAP context");
return err;
}
ctx = ie->ike_sa->v2.eap_ctx;
msg = &ie->ike_sa->v2.eap;
IPCOM_LOG1(DEBUG2,
"ipike_exchange_sa_init_eap_process() ::"\
" message %d bytes", msg->len);
/* Convert message to packet */
pkt_in.start = 0;
pkt_in.end = (int) msg->len;
pkt_in.data = msg->value;
pkt_in.max_len = pkt_in.end - pkt_in.start;
/* Allocate out buffer */
pkt_out.max_len = IPIKE_EAP_MAX_BUF;
pkt_out.start = 0;
pkt_out.end = 0;
pkt_out.data = ipcom_malloc(IPIKE_EAP_MAX_BUF);
if (pkt_out.data == IP_NULL)
{
IP_PANIC();
return IPIKE_OUT_OF_MEMORY;
}
/* Process packet. Responder send to radius server and initiator to EAP processing. */
if (!ie->ike_sa->is_initiator)
{
#ifdef IPIKE_USE_RADIUS
Ip_u8 id[74] = {0};
Ip_u32 id_len = sizeof(id);
if (!ipike_isakmp_get_unique_id_for_exchange(ie, id, &id_len))
err = IPEAP_FAILURE;
else
err = ipike_radius_process_pkt(ie->ike_sa->v2.radius_ctx,
id,
id_len,
&pkt_in,
&pkt_out);
#else
err = IPEAP_FAILURE;
#endif /* IPIKE_USE_RADIUS */
}
else
{
err = ipeap_process_pkt(ctx, &pkt_in, &pkt_out);
}
ipcom_free(msg->value);
msg->value = IP_NULL;
msg->len = 0;
if (err == IPEAP_PENDING_SEND_OUT_PKT)
{
msg->len = (Ip_size_t) (pkt_out.end - pkt_out.start);
msg->value = ipcom_malloc(msg->len);
if (msg->value != IP_NULL)
ipcom_memcpy(msg->value, pkt_out.data+pkt_out.start, msg->len);
}
ipcom_free(pkt_out.data);
if (err == IPEAP_SUCCESS)
{
ie->ike_sa->v2.eap_success = IP_TRUE;
IPCOM_LOG0(DEBUG2,
"ipike_exchange_sa_init_eap_process() ::"\
" EAP success");
}
else if ((err != IPEAP_PENDING_SEND_OUT_PKT || msg->value == IP_NULL) &&
err != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_eap_process() ::"\
" no EAP out message");
return IPIKE_ISAKMP_AUTHENTICATION_FAILED;
}
else if (err == IPCOM_SUCCESS)
{
/* No response yet, wait for radius server */
ie->wait_response = IP_TRUE;
}
/* We will not allow more than IPIKE_IKEV2_MAX_EXCH_IN_NEG extra exchanges */
if (ie->message_number_extra == 2 * IPIKE_IKEV2_MAX_EXCH_IN_NEG)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_eap_process() ::"\
" no more exchanges allowed");
return IPIKE_ISAKMP_AUTHENTICATION_FAILED;
}
/* Need another exchange pair */
ie->message_number_extra += 2;
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_sa_init_auth_eap
*===========================================================================
*/
IP_INLINE Ip_bool
ipike_exchange_sa_init_auth_eap(Ipike_isakmp_exchange *ie)
{
if (
#ifdef IPIKE_USE_RADIUS
(ie->ike_sa->v2.eap_ctx || ie->ike_sa->v2.radius_ctx) &&
#else
ie->ike_sa->v2.eap_ctx &&
#endif
0 == ie->ike_sa->v2.eap.len)
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* ipike_exchange_sa_init_generate_auth_eap
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_generate_auth_eap(Ipike_isakmp_exchange* ie)
{
Ipike_error err = IPIKE_SUCCESS;
Ip_u8 key[64] = {0}; /* msk is at least 64 bytes */
Ip_u32 key_len = 0;
/* Wait with generate until authenticated and no eap payload. The
* authentication is pre shared key with the keys from the finished
* EAP exchange. */
if (!ie->wait_response && ipike_exchange_sa_init_auth_eap(ie) )
{
key_len = sizeof(key);
ipike_exchange_sa_init_eap_get_key(ie, key, &key_len);
if (ie->ike_sa->is_initiator)
err = ipike_payload_authentication_generate_eap(key,
key_len,
ie->ike_sa->selected.prf,
&ie->ike_sa->v2.id_i,
&ie->ike_sa->v2.keys.SK_pi,
&ie->ike_sa->nr,
&ie->ike_sa->v2.auth_msg_i,
&ie->ike_sa->v2.auth);
else
err = ipike_payload_authentication_generate_eap(key,
key_len,
ie->ike_sa->selected.prf,
&ie->ike_sa->v2.id_r,
&ie->ike_sa->v2.keys.SK_pr,
&ie->ike_sa->ni,
&ie->ike_sa->v2.auth_msg_r,
&ie->ike_sa->v2.auth);
if (err != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_eap_generate_auth() ::"\
" generate failed");
return err;
}
}
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_sa_init_verify_auth_eap
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_verify_auth_eap(Ipike_isakmp_exchange* ie)
{
Ipike_error err = IPIKE_SUCCESS;
Ip_u8 key[64] = {0}; /* msk is at least 64 bytes */
Ip_u32 key_len = 0;
/* Verify as soon as AUTH payload is present. Require EAP payload if
* AUTH payload is missing, as long as EAP payloads are present will the
* negotiating not be finished. */
if (ie->message_number > 4)
{
if (ie->ike_sa->v2.auth.data.len > 0)
{
key_len = sizeof(key);
ipike_exchange_sa_init_eap_get_key(ie, key, &key_len);
if (!ie->ike_sa->is_initiator)
err = ipike_payload_authentication_verify_eap(key,
key_len,
ie->ike_sa->selected.prf,
&ie->ike_sa->v2.id_i,
&ie->ike_sa->v2.keys.SK_pi,
&ie->ike_sa->nr,
&ie->ike_sa->v2.auth_msg_i,
&ie->ike_sa->v2.auth);
else
err = ipike_payload_authentication_verify_eap(key,
key_len,
ie->ike_sa->selected.prf,
&ie->ike_sa->v2.id_r,
&ie->ike_sa->v2.keys.SK_pr,
&ie->ike_sa->ni,
&ie->ike_sa->v2.auth_msg_r,
&ie->ike_sa->v2.auth);
if (err != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_eap_verify_auth() ::"\
" verify failed");
return err;
}
}
/* Require AUTH payload if last exchange pair */
else if (ie->message_number == (4 + ie->message_number_extra))
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_eap_verify_auth() ::"\
" no EAP and no AUTH payload");
return IPIKE_ISAKMP_AUTHENTICATION_FAILED;
}
}
return IPIKE_SUCCESS;
}
#endif /* IPIKE_USE_EAP */
/*
*===========================================================================
* ipike_exchange_sa_init_send_ipsec_info
*===========================================================================
*/
IP_STATIC Ip_bool
ipike_exchange_sa_init_send_ipsec_info(Ipike_isakmp_exchange* ie)
{
Ip_bool is = IP_FALSE;
/* Initiator should send at message number 3, but responder should wait
* until authenticated and sending second AUTH payload. The normal case
* is message number 4, but for EAP this will be later */
#ifdef IPIKE_USE_EAP
if ( (ie->ike_sa->is_initiator && (ie->message_number == 3)) ||
(!ie->ike_sa->is_initiator &&
(ie->message_number == 4 + ie->message_number_extra)))
is = IP_TRUE;
#else
if ( ie->message_number == 3 || ie->message_number == 4 )
is = IP_TRUE;
#endif
/* Don't send any IPsec information if we have an child sa error,
* e.g. due NO_PROPOSAL_CHOSEN etc. */
if ( is && ipike_exchange_sa_init_has_child_sa_error(ie) )
is = IP_FALSE;
return is;
}
/*
*===========================================================================
* ipike_exchange_sa_init_generate_auth
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_generate_auth(Ipike_isakmp_exchange* ie)
{
Ipike_error err = IPIKE_SUCCESS;
/* If we are waiting for a response, dont generate auth yet */
if ( ie->wait_response )
return err;
ipike_payload_authentication_free(&ie->ike_sa->v2.auth);
/* Get authentication method */
err = ipike_attribute_auth_method_from_string_v2(
ie->ike_conf->authentication.method,
&ie->ike_sa->selected.auth_method);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_generate_auth() :: "\
"failed to get authentication method, check configuration");
return err;
}
#ifdef IPIKE_USE_EAP
/* Generate auth payload for eap if available, will return success if
* not available */
err = ipike_exchange_sa_init_generate_auth_eap(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_generate_auth() ::"\
" failed to generate authentication payload for EAP");
return err;
}
#endif
/* Generate authentication payload before message 3 and 4. For EAP should
* initiator not send a auth payload. A special case when we continue a non eap
* negotiation (e.g. after http get) and we require an auth payload to be generated */
if ( (ie->ike_sa->selected.auth_method != IPIKE2_AUTH_EAP &&
ie->message_number == 2) || ie->message_number == 3 ||
(ie->message_number == 4 && !ipike_exchange_sa_init_has_eap(ie) ))
{
/* Create authentication message (that will be sent) */
if (ie->ike_sa->is_initiator)
err = ipike_payload_authentication_generate(ie->ike_sa->selected.auth_method,
ie->ike_sa->selected.prf,
&ie->ike_conf->authentication,
&ie->ike_sa->v2.id_i,
&ie->ike_sa->v2.keys.SK_pi,
&ie->ike_sa->nr,
&ie->ike_sa->v2.auth_msg_i,
&ie->ike_sa->v2.auth);
else
err = ipike_payload_authentication_generate(ie->ike_sa->selected.auth_method,
ie->ike_sa->selected.prf,
&ie->ike_conf->authentication,
&ie->ike_sa->v2.id_r,
&ie->ike_sa->v2.keys.SK_pr,
&ie->ike_sa->ni,
&ie->ike_sa->v2.auth_msg_r,
&ie->ike_sa->v2.auth);
}
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_generate_auth() ::"\
" failed to generate authentication data");
return err;
}
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_verify_auth
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_verify_auth(Ipike_isakmp_exchange* ie)
{
Ipike_error err = IPIKE_SUCCESS;
Ipike2_id* id = IP_NULL;
Ipike_value* key = IP_NULL;
Ipike_value* nonce = IP_NULL;
Ipike_value* msg = IP_NULL;
/* Verify identity for message 3 and 4. */
if (ie->message_number == 3 || ie->message_number == 4)
{
if (!ie->ike_sa->is_initiator)
{
id = &ie->ike_sa->v2.id_i;
key = &ie->ike_sa->v2.keys.SK_pi;
nonce = &ie->ike_sa->nr;
msg = &ie->ike_sa->v2.auth_msg_i;
}
else
{
id = &ie->ike_sa->v2.id_r;
key = &ie->ike_sa->v2.keys.SK_pr;
nonce = &ie->ike_sa->ni;
msg = &ie->ike_sa->v2.auth_msg_r;
}
/* Accept/reject the id payload */
err = ipike_policy_accept_id(ie->ike_conf->remote_id,
id->info.id_type,
&id->data,
ie->ike_sa->cert.peer);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_verify_auth() ::"\
" bad 'remote_identifier'");
return err;
}
}
/* The authentication check is always done for message 4 (responder) but
* not for message 3 (inititator) if EAP is used. The EPA verify is the
* extra auth payloads sent in two last messages. */
if ((err == IPIKE_SUCCESS) &&
#ifdef IPIKE_USE_EAP
((ie->message_number == 3 && ie->ike_sa->v2.auth.data.len > 0) ||
#else
(ie->message_number == 3 ||
#endif
ie->message_number == 4))
{
/* Take certificate from configuration if not specified by remote peer. */
if ( ie->ike_sa->cert.peer == IP_NULL &&
ie->ike_conf->authentication.remote_certificate != IP_NULL )
ie->ike_sa->cert.peer = ipike_crypto_read_cert(
ie->ike_conf->authentication.remote_certificate);
err = ipike_payload_authentication_verify(ie->ike_sa->selected.prf,
&ie->ike_conf->authentication,
id,
key,
nonce,
msg,
&ie->ike_sa->cert,
&ie->ike_sa->v2.auth);
}
#ifdef IPIKE_USE_EAP
/* Let the function decide when to verify, will be during the EAP exchanges */
if (ie->message_number > 4)
{
err = ipike_exchange_sa_init_verify_auth_eap(ie);
}
#endif /* # IPIKE_USE_EAP */
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_select_param
*===========================================================================
*/
IP_GLOBAL Ipike_error
ipike_exchange_sa_init_select_param(
Ipike_isakmp_exchange * ie,
struct Ipike_sadb_entry_struct * ike_sa)
{
Ipike_error err;
ip_assert(ike_sa != IP_NULL);
ip_assert(ie->ike_conf != IP_NULL);
if (ie->message_number == 1 || ie->message_number == 2)
{
/* Select SA parameters (if not selected already) */
if (ike_sa->v2.sa.selected_proposal < 0)
{
err = ipike2_policy_select_sa_param(IP_TRUE,
ie->ike_conf->proposals,
&ike_sa->v2.sa,
&ike_sa->selected);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipike_exchange_sa_init_select_param() ::"\
" failed to select SA parameters");
return err;
}
}
/* ENCR, INTEG, PRF and DH must be specified for IKE SA */
if (ike_sa->v2.sa.selected_encr == IPIKE_TRANSFORM_UNKNOWN
|| ike_sa->v2.sa.selected_integ == IPIKE_TRANSFORM_UNKNOWN
|| ike_sa->v2.sa.selected_prf == IPIKE_TRANSFORM_UNKNOWN
|| ike_sa->v2.sa.selected_dh == IPIKE_TRANSFORM_UNKNOWN)
{
IPCOM_LOG0(CRIT, "ipike_exchange_sa_init_select_param() ::"\
" must specify ENCR, INTEG, PRF and DH for IKE SA");
return IPIKE_ISAKMP_NO_PROPOSAL_CHOSEN;
}
/* Initiator dh group must be accepted */
if (ike_sa->v2.dh_group != ike_sa->v2.sa.selected_dh)
{
ike_sa->v2.dh_group = ike_sa->v2.sa.selected_dh;
IPCOM_LOG0(DEBUG, "ipike_exchange_sa_init_select_param() :: "\
"bad diffie-hellman group");
return IPIKE_ISAKMP_KE_PAYLOAD;
}
}
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_sa_init_update_child
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_update_child(Ipike_isakmp_exchange *ie)
{
Ipike_error err = IPIKE_SUCCESS;
/* INIT Exchange */
if ( ie->message_number == 1 || ie->message_number == 2 )
{
err = ipike_exchange_child_sa_on_new(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(DEBUG,
"ipike_exchange_sa_init_update_child() ::"\
" failed to create");
IPIKE_EVENT_LOG(IKE_PHASE_II_FAIL, IKE_INTERNAL_ERROR,
&ie->ike_sa->peer_addr, &ie->ike_sa->local_addr,
0, IP_NULL);
return err;
}
}
/* AUTH Exchange */
if ( ie->message_number == 3 ||
ie->message_number == (4 + ie->message_number_extra) )
{
/* Select SA parameters */
err = ipike_exchange_child_sa_select_param(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(DEBUG,
"ipike_exchange_sa_init_update_child() ::"\
" failed to select sa param");
IPIKE_EVENT_LOG(IKE_PHASE_II_FAIL, PROPOSAL_MISMATCH,
&ie->ike_sa->peer_addr, &ie->ike_sa->local_addr,
0, IP_NULL);
return err;
}
/* pfs is not used for first child sa */
ie->ipsec_sa->v2.sa.selected_dh = IPIKE_TRANSFORM_UNKNOWN;
/* Copy nonce */
(void)ipike_cpy_value(&ie->ipsec_sa->n, &ie->ike_sa->n);
/* Set peer spi values */
err = ipike_exchange_child_sa_set_peer_spi(ie->ipsec_sa);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_update_child() :: "\
"failed to set peer spi");
IPIKE_EVENT_LOG(IKE_PHASE_II_FAIL, IKE_INTERNAL_ERROR,
&ie->ike_sa->peer_addr, &ie->ike_sa->local_addr,
0, IP_NULL);
return err;
}
}
/* Generate initiator and responder traffic selector */
if ( ie->message_number == 2 )
{
Ipike_config_flow *flow;
/* Use trigger flow if not 'flow' granularity is set */
if ( ie->option2 && ie->ipsec_conf->granularity &&
ipcom_strcmp( ie->ipsec_conf->granularity, "flow") != 0)
flow = (Ipike_config_flow *) ie->option2;
else
flow = &ie->ipsec_conf->flow;
/* Use trigger flow information if available */
err = ipike_payload_traffic_selector_initialize(flow,
ie->ike_sa->is_initiator,
&ie->ipsec_sa->v2.ts_i,
&ie->ipsec_sa->v2.ts_r);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_update_child() :: "\
" failed to generate traffic selector");
IPIKE_EVENT_LOG(IKE_PHASE_II_FAIL, IKE_INTERNAL_ERROR,
&ie->ike_sa->peer_addr, &ie->ike_sa->local_addr,
0, IP_NULL);
return err;
}
}
/* Merge initiator and responder traffic selector */
if ( ie->message_number == 3 || ie->message_number == 4 )
{
err = ipike_exchange_merge_traffic_selectors(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_update_child() ::"\
" Failed to merge traffic selectors");
IPIKE_EVENT_LOG(IKE_PHASE_II_FAIL, FLOW_MISMATCH,
&ie->ike_sa->peer_addr, &ie->ike_sa->local_addr,
0, IP_NULL);
return err;
}
}
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_sa_exchange_sa_init_update_responder_config
*===========================================================================
*/
IP_STATIC void
ipike_exchange_sa_init_update_responder_config(Ipike_isakmp_exchange *ie)
{
Ipike2_id *id = IP_NULL;
Ipike_config_ike_sa *ike_conf = IP_NULL;
Ipike_sadb_entry *ike_sa = IP_NULL;
ike_sa = ie->ike_sa;
id = &ike_sa->v2.id_i;
ike_conf = ipike_config_get_ike_sa_from_address_and_remote_id(ie,
&ie->msg.local_addr,
&ie->msg.peer_addr,
id->info.id_type,
&id->data,
ike_sa->cert.peer);
if ( IP_NULL != ike_conf && ike_conf != ie->ike_conf )
{
/* We found a different configuration that can be used */
ie->ike_conf = ike_conf;
/* Update IKE SA parameters */
if ( IP_NULL != ike_sa->id )
ipcom_free(ike_sa->id);
ike_sa->id = ipcom_strdup(ike_conf->ike_id);
/* OCSP is used added in INIT exchange, so it has already been set */
#ifdef IPIKE_USE_OCSP
/* Paranoid check! */
if (IP_NULL != ie->ike_sa->cert.resp_local)
OCSP_RESPONSE_free(ie->ike_sa->cert.resp_local);
if (IP_NULL != ie->ike_conf->authentication.ocsp_response)
{
ie->ike_sa->cert.resp_local = ipike_crypto_read_ocsp_response(
ie->ike_conf->authentication.ocsp_response);
}
#endif
IPCOM_LOG0(NOTICE,
"ipike_exchange_sa_init_update_responder_config() ::"\
" Updated IKE SA config selection based on 'remote_identifier'");
}
/* Fall through even if no matching configuration was found since and
* let the negotiation fail in the authentication check */
}
/*
*===========================================================================
* ipike_exchange_sa_init_auth_process
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_auth_process(Ipike_isakmp_exchange *ie)
{
Ipike_error err = IPIKE_SUCCESS;
if ( (ie->message_number == 3 || ie->message_number == 4) &&
( ie->ike_sa->v2.auth.method == IPIKE2_AUTH_RSA_SIGNATURE ||
ie->ike_sa->v2.auth.method == IPIKE2_AUTH_DSS_SIGNATURE) )
{
/* Are we using "Hash and URL" and are supposed to fetch certificate
* via HTTP */
if ( !ie->ike_sa->cert.peer &&
ipike.use_hash_and_url && ie->ike_sa->cert.peer_url != IP_NULL )
{
err = ipike_exchange_sa_init_http_get(ie,
ie->ike_sa->cert.peer_url,
ipike_exchange_sa_init_http_get_cert_cb);
if ( IPIKE_SUCCESS != err )
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_auth_process() :: "\
"failed to fetch crl via http");
return IPIKE_AUTHENTICATION_FAILED;
}
/* Fetching certificate via HTTP, break processing for now and
* continue when we have received the certificate */
return err;
}
/* Get CRL */
if (!ie->ike_sa->cert.crls && ie->ike_conf->authentication.crl_store)
{
/* Fetching CRLs using CRL using http */
if (ipcom_strncmp(ie->ike_conf->authentication.crl_store,
"http://", 7) == 0 )
{
IPCOM_LOG1(DEBUG2, "ipike_exchange_sa_init_auth_process() :: "\
"fetching crl via http \"%s\"",
ie->ike_conf->authentication.crl_store );
err = ipike_exchange_sa_init_http_get(
ie,
ie->ike_conf->authentication.crl_store,
ipike_exchange_sa_init_http_get_crl_cb);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_auth_process() ::"\
" failed to fetch crl via http");
return IPIKE_AUTHENTICATION_FAILED;
}
/* Fetching CRLs via HTTP, break processing for now and
* continue when we have received the certificate */
return err;
}
else
{
/* Read crl from local storage */
IPCOM_LOG0(DEBUG2, "ipike_exchange_sa_init_auth_process() ::"\
" reading crl from local storage");
ie->ike_sa->cert.crls = ipike_crypto_read_crls(
ie->ike_conf->authentication.crl_store);
}
}
}
#ifdef IPIKE_USE_EAP
/* Process any EAP messages */
if (ipike_exchange_sa_init_has_eap(ie))
{
err = ipike_exchange_sa_init_eap_process(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_auth_process() :: "\
"failed to process EAP message");
return err;
}
}
#endif /* IPIKE_USE_EAP */
/* Verify authentication message, need to verify before create since will
* reuse auth variable */
err = ipike_exchange_sa_init_verify_auth(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_auth_process() :: "\
"failed to verify authentication data");
return err;
}
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_update
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_update(
Ipike_isakmp_exchange* ie,
Ipike_isakmp_header* isakmp)
{
Ipike_sadb_entry * ike_sa = ie->ike_sa;
Ipike_error err;
/* Don't do anything in case restart */
if (ie->message_number == 0)
return IPIKE_SUCCESS;
/* As responder in AUTH exchange, try to find a better matched IKE SA
* configuration */
if ( ie->message_number == 3 )
ipike_exchange_sa_init_update_responder_config(ie);
/* Select SA parameters */
err = ipike_exchange_sa_init_select_param(ie, ike_sa);
if (IPIKE_SUCCESS != err)
{
IPIKE_EVENT_LOG(IKE_PHASE_I_FAIL, PROPOSAL_MISMATCH,
&ike_sa->peer_addr, &ike_sa->local_addr,
0, IP_NULL);
return err;
}
if (ie->message_number == 1 || ie->message_number == 2)
{
/* Set initiator spi */
err = ipike_set_data(&ike_sa->v2.spi_i,
ike_sa->initiator_cookie,
sizeof(ike_sa->initiator_cookie));
if ( IPIKE_SUCCESS != err )
goto errout;
/* Set responder spi */
if (ie->message_number == 1)
{
err = ipike_set_data(&ike_sa->v2.spi_r,
ike_sa->responder_cookie,
sizeof(ike_sa->responder_cookie));
}
else
{
err = ipike_set_data(&ike_sa->v2.spi_r,
isakmp->responder_cookie,
sizeof(isakmp->responder_cookie));
}
if (IPIKE_SUCCESS != err)
goto errout;
/* Concatenate initiator and responder nonces */
if ((IPIKE_SUCCESS != (err = ipike_cpy_value(&ike_sa->n, &ike_sa->ni))) ||
(IPIKE_SUCCESS != (err = ipike_append_value(&ike_sa->n, &ike_sa->nr))))
goto errout;
/* Responder need to generate a Diffie-Hellman key */
if (ie->message_number == 1)
{
err = ipike_payload_key_exchange_generate(
ike_sa->v2.sa.selected_dh,
&ie->dh,
&ike_sa->g_xr);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(CRIT, "ipike_exchange_sa_init_update() ::"\
" failed to generate diffie hellman key");
goto errout;
}
/* Generate diffie-hellman shared secret */
if (ie->message_number == 1)
err = ipike_crypto_compute_dh_key(ie->dh,
&ike_sa->g_xi,
&ike_sa->g_xy);
}
else
err = ipike_crypto_compute_dh_key(ie->dh,
&ike_sa->g_xr,
&ike_sa->g_xy);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_update() :: "\
"failed to generate DH shared secret");
goto errout;
}
/* Store the key reference for use by first child sa */
ike_sa->v2.dh = ie->dh;
/* Generate the IKE keys */
err = ipike_crypto_generate_keys_ike_sa(ike_sa, IP_NULL);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(DEBUG, "ipike_exchange_sa_init_update() ::"\
" failed to generate IKE keys");
goto errout;
}
ipike_exchange_sa_init_print_keys(&ike_sa->v2.keys);
#ifdef IPIKE_USE_OCSP
/* Paranoid check! */
if (IP_NULL != ike_sa->cert.resp_local)
OCSP_RESPONSE_free(ike_sa->cert.resp_local);
if (IP_NULL != ie->ike_conf->authentication.ocsp_response)
{
ike_sa->cert.resp_local = ipike_crypto_read_ocsp_response(
ie->ike_conf->authentication.ocsp_response);
}
#endif
#ifdef IPIKE_REQUEST_PEER_APPLICATION_VERSION
/* Request peer application and version */
err = ipike_payload_configuration_set(&ike_sa->cp_request,
IPIKE_CFG_ATTR_APPLICATION_VERSION,
IP_NULL,
0);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_update() ::"\
" can't request application version");
goto errout;
}
#endif
/* Trusted CA certs are needed to generate a proper certificate request
in message 2 or 3 (INIT Response or AUTH Request) */
if (IP_NULL != ike_sa->cert.trusted)
{
ipike_array_free((void**)ike_sa->cert.trusted,
(void(*)(void*))X509_free);
ike_sa->cert.trusted = IP_NULL;
}
if (IP_NULL != ie->ike_conf->authentication.trusted_ca_store)
{
ike_sa->cert.trusted = ipike_crypto_get_intermediate_ca_certs(
&ike_sa->cert,
ie->ike_conf->authentication.trusted_ca_store);
}
}
/* AUTH exchange read certificates from from configuration */
if (ie->message_number == 2 || ie->message_number == 3 )
{
/* Read eventual certificates for authentication */
if (IP_NULL != ike_sa->cert.local)
X509_free(ike_sa->cert.local);
if (IP_NULL != ie->ike_conf->authentication.certificate)
{
ike_sa->cert.local = ipike_crypto_read_cert(
ie->ike_conf->authentication.certificate);
}
if (IP_NULL != ike_sa->cert.ca)
{
ipike_array_free((void**)ike_sa->cert.ca,
(void(*)(void*))X509_free);
ike_sa->cert.ca = IP_NULL;
}
if (IP_NULL != ie->ike_conf->authentication.intermediate_ca_store)
{
ike_sa->cert.ca = ipike_crypto_get_intermediate_ca_certs(&ike_sa->cert,
ie->ike_conf->authentication.intermediate_ca_store);
}
}
/* May be configured to request address from responder */
if (ie->ipsec_conf != IP_NULL && ie->ipsec_conf->dynamic_address != IP_NULL)
{
if (ie->message_number == 2)
{
if ( ie->ike_sa->dhcp.has_info )
{
/* Request old address */
err = ipike_payload_configuration_request_address(&ie->ike_sa->cp_request,
ie->ipsec_conf->dynamic_address,
&ie->ike_sa->dhcp.address);
/* Clear old values, and lets hope we get the old address */
ipcom_memset(&ie->ike_sa->dhcp, 0, sizeof(ie->ike_sa->dhcp));
}
else
{
/* Request address */
err = ipike_payload_configuration_request_address(&ie->ike_sa->cp_request,
ie->ipsec_conf->dynamic_address,
IP_NULL);
}
if ( IPIKE_SUCCESS != err )
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_update() ::"\
" can't request address");
goto errout;
}
}
}
/* Do authentication processing */
err = ipike_exchange_sa_init_auth_process(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_update() ::"\
" failed in authentication processing");
IPIKE_EVENT_LOG(IKE_PHASE_I_FAIL, AUTHENTICATION_ERROR,
&ike_sa->peer_addr, &ike_sa->local_addr,
0, IP_NULL);
return err;
}
/* Create authentication data */
err = ipike_exchange_sa_init_generate_auth(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_update() ::"\
" failed to generate authentication data");
goto errout;
}
/* We could have non-critical errors on the IKE SA that affects the establishment
* of the IPsec SAs, e.g. NO_PROPOSAL_CHOSEN. This would establish the IKE SA
* but not the IPsec SAs. */
if ( !ipike_exchange_sa_init_has_child_sa_error(ie) )
{
/* Update first child sa */
err = ipike_exchange_sa_init_update_child(ie);
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(WARNING, "ipike_exchange_sa_init_update() ::"\
" failed to create first child");
/* Handle IPsec SA error here */
if ( ie->ipsec_sa != IP_NULL )
return ipike_exchange_handle_error(ie, ie->ipsec_sa, err);
else
return err;
}
}
return IPIKE_SUCCESS;
errout:
IPIKE_EVENT_LOG(IKE_PHASE_I_FAIL, IKE_INTERNAL_ERROR,
&ie->ike_sa->peer_addr, &ie->ike_sa->local_addr,
0, IP_NULL);
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_on_new
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_on_new(Ipike_isakmp_exchange *ie)
{
Ipike_error err;
Ipike2_id *local_id;
Ipike2_id *remote_id;
ip_assert(ie->ike_sa != IP_NULL);
/* Generate initiator and responder id */
if (ie->ike_sa->is_initiator)
{
local_id = &ie->ike_sa->v2.id_i;
remote_id = &ie->ike_sa->v2.id_r;
}
else
{
local_id = &ie->ike_sa->v2.id_r;
remote_id = &ie->ike_sa->v2.id_i;
}
err = ipike_payload_identification_generate(ie->ike_conf->local_id,
ie->ike_sa->is_initiator,
local_id);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_on_new() ::"\
" failed to generate local id");
return err;
}
if (ie->ike_conf->remote_id != IP_NULL)
{
err = ipike_payload_identification_generate(ie->ike_conf->remote_id,
!ie->ike_sa->is_initiator,
remote_id);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_on_new() ::"\
" failed to generate remote id");
return err;
}
}
if (ie->ike_sa->is_initiator)
ie->isakmp_flags = IPIKE_ISAKMP_INITIATOR;
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_process_continue
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_process_continue(Ipike_isakmp_exchange* ie)
{
Ipike_error err = IPIKE_SUCCESS;
ie->wait_response = IP_FALSE;
if (IPIKE_SUCCESS != (err = ipike_exchange_sa_init_generate_auth(ie)))
{
IPCOM_LOG0(DEBUG, "ipike_exchange_sa_init_process_continue() ::"\
" failed to generate auth payload");
}
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_add_mobike_payloads
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_add_mobike_payloads(
Ipike_isakmp_exchange* ie,
Ipike_payload_header*** reply_payloads)
{
Ipike_error err = IPIKE_SUCCESS;
Ip_u32 j = 0;
Ip_u32 count = 0;
Ipike_payload_header* reply_payload = IP_NULL;
/* Add mobike supported payload */
err = ipike_payload_notify_mobike_supported(
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads, reply_payload);
if (IPIKE_SUCCESS != err)
{
ipcom_free(reply_payload);
reply_payload = IP_NULL;
}
}
/* Add eventual additional addresses, take from listen */
j = 0;
while (IPIKE_SUCCESS == err &&
IP_NULL != ipike.config.general.listen[j])
{
if (ipike_addr_compare(ipike.config.general.listen[j],
&ie->ike_sa->local_addr) != 0)
{
err = ipike_payload_notify_additional_ip_address(
ipike.config.general.listen[j],
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads, reply_payload);
if (IPIKE_SUCCESS != err)
{
ipcom_free(reply_payload);
reply_payload = IP_NULL;
}
else
{
count++;
}
}
}
j++;
}
/* Add no additional paylod if any added */
if (IPIKE_SUCCESS == err && count > 0)
{
err = ipike_payload_notify_no_additional_addresses(
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads, reply_payload);
if (IPIKE_SUCCESS != err)
{
ipcom_free(reply_payload);
reply_payload = IP_NULL;
}
}
}
/* No NATs allow payload must be sent if MOBIKE is used without NAT Traversal */
if (IPIKE_SUCCESS == err &&
ipcom_sysvar_get_as_int("ipike.port_nat", 0) == 0)
{
err = ipike_payload_notify_no_nats_allowed(&ie->ike_sa->local_addr,
&ie->ike_sa->peer_addr,
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads, reply_payload);
if (IPIKE_SUCCESS != err)
{
ipcom_free(reply_payload);
reply_payload = IP_NULL;
}
}
}
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_http_get_cert_cb
*===========================================================================
*/
IP_STATIC void
ipike_exchange_sa_init_http_get_cert_cb(
Ipike_error status,
const char * data,
Ip_size_t data_len,
void * id)
{
Ipike_isakmp_exchange *ie;
Ipike_error err = IPIKE_AUTHENTICATION_FAILED;
Ip_bool cont = IP_TRUE;
ip_assert(id);
/* Get exchange state using the unique id */
ie = ipike_isakmp_get_exchange_from_unique_id(
id,
ipike_isakmp_get_unique_id_size());
if ( !ie )
{
/* No exchange found => abort */
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_http_get_cert_cb() ::"\
" no IKE exchange found");
/* Free allocated id */
ipcom_free( id );
/* No exchange found, stop processing */
return;
}
/* Was the HTTP request successful */
if ( IPIKE_SUCCESS != status )
{
IPCOM_LOG1(ERR, "ipike_exchange_sa_init_http_get_cert_cb() ::"\
" bad http response received (%d)",
status);
goto done_continue;
}
/* Decode DER encoded certificate */
ie->ike_sa->cert.peer = d2i_X509(IP_NULL,
(const unsigned char**)&data,
(int) data_len);
if ( !ie->ike_sa->cert.peer )
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_http_get_cert_cb() ::"\
" failed to decode certificate");
goto done_continue;
}
/* Need to clear wait_response since an new async call may be registred.
If that is the case we cannot continue the negotiation from this
negotiation, it is done when the new async call returns */
ie->wait_response = IP_FALSE;
/* We got certificate, try authentication processing again */
err = ipike_exchange_sa_init_auth_process(ie);
/* Check if another async call is registered (e.g CRL fetching), if so
* we can't continue this negotiation yet, otherwise reset wait_response
* flag to TRUE and continue */
if ( ie->wait_response )
cont = IP_FALSE;
else
ie->wait_response = IP_TRUE;
if ( IPIKE_SUCCESS != err )
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_http_get_cb() :: "\
"failed to verify authentication data");
}
done_continue:
/* Free allocated id */
ipcom_free( id );
/* Handle any errors, i.e. authentication failures */
if ( IPIKE_SUCCESS != err)
ipike_exchange_handle_error(ie, ie->ike_sa, err);
/* Continue the exchange and send the response */
if ( cont )
ipike_isakmp_process_message_continue(ie);
}
/*
*===========================================================================
* ipike_exchange_sa_http_get_crls_cb
*===========================================================================
*/
IP_STATIC void
ipike_exchange_sa_init_http_get_crl_cb(
Ipike_error status,
const char *data,
Ip_size_t data_len,
void *id)
{
Ipike_isakmp_exchange *ie;
X509_CRL *crl;
Ipike_error err = IPIKE_AUTHENTICATION_FAILED;
ip_assert(id);
/* Get exchange state using the unique id */
ie = ipike_isakmp_get_exchange_from_unique_id(
id,
ipike_isakmp_get_unique_id_size());
if ( !ie )
{
/* No exchange found => abort */
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_http_get_crl_cb() ::"\
" no IKE exchange found");
/* Free allocated id */
ipcom_free( id );
/* No exchange found, stop proccessing */
return;
}
/* Was the HTTP request successful */
if ( IPIKE_SUCCESS != status )
{
IPCOM_LOG1(ERR,
"ipike_exchange_sa_init_http_get_crl_cb() :: "\
"bad http response received (%d)",
status);
goto done_continue;
}
/* Decode CRL */
crl = ipike_crypto_decode_crl((Ip_u8 *)data, data_len);
if ( !crl )
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_http_get_crl_cb() ::"\
" failed to decode crl");
goto done_continue;
}
if (IPIKE_SUCCESS != ipike_array_add((void***)&ie->ike_sa->cert.crls, crl))
goto done_continue;
/* We got CRL try authentication processing again */
err = ipike_exchange_sa_init_auth_process(ie);
if ( IPIKE_SUCCESS != err )
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_http_get_crl_cb() ::"\
" failed to verify authentication data");
}
done_continue:
/* Free allocated id */
ipcom_free( id );
/* Handle any errors, i.e. authentication failures */
if ( IPIKE_SUCCESS != err)
ipike_exchange_handle_error(ie, ie->ike_sa, err);
/* Continue the exchange and send the response */
ipike_isakmp_process_message_continue(ie);
}
/*
*===========================================================================
* ipike_exchange_sa_init_http_get
*===========================================================================
*/
IP_STATIC Ipike_error
ipike_exchange_sa_init_http_get(
Ipike_isakmp_exchange *ie,
const char *uri,
Ipike_http_fetcher_callback callback)
{
Ip_u8 *id;
Ip_u32 id_len;
if ( !uri )
return IPIKE_FAILED;
id_len = ipike_isakmp_get_unique_id_size();
id = ipcom_malloc( id_len );
if ( !id )
{
IP_PANIC();
return IPIKE_OUT_OF_MEMORY;
}
ipcom_memset( id, 0, id_len);
/* Create an unique id for this exchange that is used for obtaining
correct exchange state in the callback */
if ( !ipike_isakmp_get_unique_id_for_exchange(ie, id, &id_len))
goto error_out;
/* Get data that is pointed out by the URI */
if ( IPIKE_SUCCESS != ipike_http_fetcher_get(uri, callback, id) )
{
IPCOM_LOG1(ERR,
"ipike_exchange_sa_init_http_get() ::"\
" failed to fetch \"%s\"", uri);
goto error_out;
}
/* Mark that this exchange is waiting for an external response */
ie->wait_response = IP_TRUE;
return IPIKE_SUCCESS;
error_out:
ipcom_free(id);
return IPIKE_FAILED;
}
/*
*===========================================================================
* ipike_exchange_sa_init_handle_simultaneous
*===========================================================================
*/
IP_STATIC Ip_bool
ipike_exchange_sa_init_handle_simultaneous(
Ipike_isakmp_exchange* ie)
{
Ipike_sadb_entry *existing_sa = IP_NULL;
Ipike_sadb_entry *ike_sa = IP_NULL;
Ipike_value * peer_id = IP_NULL;
Ip_bool collision = IP_FALSE;
ip_assert(ie != IP_NULL);
ike_sa = ie->ike_sa;
if ( ike_sa->is_initiator )
peer_id = &ike_sa->v2.id_r.data;
else
peer_id = &ike_sa->v2.id_i.data;
/* Check if we just installed an IKE SA with the remote peer */
(void) ipike_sadb_get_from_peer_id(peer_id, &existing_sa);
if ( existing_sa != IP_NULL )
{
/* Handle simultaneous (within N sec) IKE SA establishments */
if ( IPIKE_SADB_IS_SIMULTANEOUS(ike_sa, existing_sa) &&
ipike_sadb_nonces_cmp(ike_sa, existing_sa) < 0 )
{
/* Two IKE SA established at the same time */
/* Remove the one with the least nonce */
IPCOM_LOG0(NOTICE, "ipike_exchange_sa_init_handle_simultaneous()"\
":: another IKE SA has just been completed");
/* Exchange collision and the IKE SA for this exchange will not live */
collision = IP_TRUE;
}
/* Release reference taken in ipike_sadb_get_from_addresses */
ipike_sadb_release(existing_sa);
}
return collision;
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipike_exchange_sa_init_on_new_initiator
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_sa_init_on_new_initiator(
Ipike_isakmp_exchange* ie,
Ipike_sadb_entry* ike_sa,
const void* not_used1,
const void* trigger_flow)
{
Ipike_error err;
Ip_u8 initiator_cookie[IPIKE_ISAKMP_COOKIE_SIZE];
Ip_u8 responder_cookie[IPIKE_ISAKMP_COOKIE_SIZE];
union Ip_sockaddr_union peer_addr;
union Ip_sockaddr_union local_addr;
Ipike_config_flow *flow = IP_NULL;
IPCOM_UNUSED_ARG(not_used1);
/* IKE version 2 */
ie->version = 2 << 4;
if (trigger_flow != IP_NULL)
{
/* Make a copy of the flow information, we will need to pass this
* further to the IPsec SA establishment */
flow = ipike_config_flow_duplicate(trigger_flow);
if (flow == IP_NULL)
{
IP_PANIC();
return IPIKE_OUT_OF_MEMORY;
}
/* Store the flow in the place holder for option2*/
ie->option2 = flow;
}
ipcom_microtime(&ie->start);
if (ipcom_strcmp(ie->ike_conf->ike_id, ie->ipsec_conf->ike_id))
{
IPCOM_LOG0(DEBUG,
"ipike_exchange_sa_init_on_new_initiator() ::"\
" IKE and IPSec SA configuration doesn't match");
/* Should never happen */
return IPIKE_FAILED;
}
ipcom_memset((void*)responder_cookie, 0, IPIKE_ISAKMP_COOKIE_SIZE);
err = ipike_crypto_generate_cookie(initiator_cookie);
if (IPIKE_SUCCESS != err)
return err;
/* Do we have an IKE SA, i.e. this is a reauthentication */
if (ike_sa != IP_NULL)
{
/* Use the same addresses and ports as the old IKE SA */
err = ipike_addr_copy(&local_addr, &ike_sa->local_addr);
if (IPIKE_SUCCESS != err)
return err;
err = ipike_addr_copy(&peer_addr, &ike_sa->peer_addr);
if (IPIKE_SUCCESS != err)
return err;
}
else
{
/* Use local address from configuration */
err = ipike_addr_copy(&local_addr, &ie->ike_conf->local_address);
if (IPIKE_SUCCESS != err)
return err;
/* Check if we have a valid host address in the configuration */
if ( ipike_config_netobj_is_host(&ie->ike_conf->remote_address) )
{
/* Use remote address from configuration */
err = ipike_addr_copy(&peer_addr, &ie->ike_conf->remote_address.addr);
if (IPIKE_SUCCESS != err)
return err;
}
/* Was this negotiation triggered by a flow */
else if (flow != IP_NULL)
{
/* Use remote address from the trigger flow */
err = ipike_addr_copy(&peer_addr, &flow->dst[0]->addr);
if (IPIKE_SUCCESS != err)
return err;
}
else
{
/* No idea of the remote address */
return IPIKE_CONF_INVALID_VALUE;
}
/* Reset destination port to default IKE port (500) */
ipike_addr_set_port(&peer_addr, ipike.port);
}
if (ipike_addr_is_any(&local_addr))
{
/* Try to figure out our local address based on the peer address */
err = ipike_dispatcher_socket_local_addr_get(&local_addr,
&peer_addr);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(NOTICE,
"ipike_exchange_sa_init_on_new_initiator() ::"\
" failed to get local address");
/* Ignore error, the stack will find out the address
* eventually, however NAT-T will fail because of
* mismatching hashes */
}
}
err = ipike_sadb_add(initiator_cookie,
responder_cookie,
&local_addr,
&peer_addr,
&ie->ike_sa);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(DEBUG,
"ipike_exchange_sa_init_on_new_initiator() ::"\
" failed to add sadb");
return err;
}
ie->is_phase1 = IP_TRUE;
ie->exchange_initiator = IP_TRUE;
ie->ike_sa->is_initiator = IP_TRUE;
ie->ike_sa->id = ipcom_strdup(ie->ike_conf->ike_id);
ie->ike_sa->version = ie->version;
/* If this is a re-authentication, copy old configuration mode data to the
* new IKE SA so it's possible to request the same address */
if ( ike_sa != IP_NULL && ike_sa->dhcp.has_info )
ie->ike_sa->dhcp = ike_sa->dhcp;
err = ipike_payload_sa_initialize(IP_TRUE,
ie->ike_conf->proposals,
&ie->ike_sa->v2.sa);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_on_new_initiator() :: failed");
goto errout;
}
/* Generate Diffie-Hellman key */
err = ipike_exchange_generate_dh_key(ie, IP_NULL, 0);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_on_new_initiator() :: "\
"failed to generate diffie hellman key");
goto errout;
}
/* Generate nonce */
err = ipike_payload_nonce_generate(&ie->ike_sa->ni);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_on_new_initiator() ::"\
" failed to generate nonce");
goto errout;
}
/* Generic initialize */
err = ipike_exchange_sa_init_on_new(ie);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_on_new_initiator() ::"\
" failed to initialize generic");
goto errout;
}
/* Success */
return IPIKE_SUCCESS;
/* Failure */
errout:
(void)ipike_sadb_set_state(ie, ie->ike_sa, IPIKE_SA_DYING);
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_on_new_responder
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_sa_init_on_new_responder(
Ipike_isakmp_exchange* ie,
Ipike_isakmp_header* first_isakmp,
union Ip_sockaddr_union* local_addr,
union Ip_sockaddr_union* peer_addr)
{
Ipike_error err;
Ip_u8 cookie[IPIKE_ISAKMP_COOKIE_SIZE];
Ip_u8 retry;
ipcom_microtime(&ie->start);
ie->exchange_initiator = IP_FALSE;
/* Generate responder cookie until the initiator/responder cookie pair
* is unique */
retry = 0;
do {
if (retry++ > 100)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_on_new_responder() ::"\
" too many retries to generate SPI");
return IPIKE_FAILED;
}
err = ipike_crypto_generate_cookie(cookie);
if (err != IPIKE_SUCCESS)
return err;
} while (ipike_sadb_exist(first_isakmp->initiator_cookie, cookie));
/* Add IKE SA */
err = ipike_sadb_add(first_isakmp->initiator_cookie,
cookie,
local_addr,
peer_addr,
&ie->ike_sa);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(CRIT,
"ipike_exchange_sa_init_on_new_responder() ::"\
" failed to add IKE SA");
return err;
}
/* Generate responder nonce */
err = ipike_payload_nonce_generate(&ie->ike_sa->nr);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(CRIT,
"ipike_exchange_sa_init_on_new_exchange_responder() ::"\
" failed to generate nonce");
return err;
}
/* Generic initialize */
err = ipike_exchange_sa_init_on_new(ie);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_on_new_initiator() ::"\
" failed to initialize generic");
return err;
}
/* Success */
ie->is_phase1 = IP_TRUE;
ie->isakmp_flags |= IPIKE_ISAKMP_RESPONSE;
ie->ike_sa->version = ie->version;
ie->ike_sa->is_initiator = IP_FALSE;
ie->ike_sa->id = ipcom_strdup(ie->ike_conf->ike_id);
ipcom_memcpy(ie->ike_sa->responder_cookie, cookie, sizeof(cookie));
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_sa_init_on_delete
*===========================================================================
*/
IP_PUBLIC void
ipike_exchange_sa_init_on_delete(Ipike_isakmp_exchange* ie)
{
if (ie->ike_sa == IP_NULL)
return;
/* Remove incomplete and failed negotiations */
if ( ie->message_number < ipike_isakmp_exchange_message_count(ie) ||
ipike_exchange_sa_init_has_ike_sa_error(ie) )
{
IPCOM_LOG0(WARNING,
"ipike_exchange_sa_init_on_delete() :: the exchange"\
" was not finished, removing (incomplete) IKE SA");
(void) ipike_sadb_set_state(ie, ie->ike_sa, IPIKE_SA_DYING);
}
if (ie->option2)
ipike_config_flow_destroy((Ipike_config_flow *)ie->option2);
/* First IPSec SA may have allocated something */
ipike_exchange_child_sa_on_delete(ie);
}
/*
*===========================================================================
* ipike_exchange_sa_init_get_payload_order
*===========================================================================
*/
IP_PUBLIC const Ip_u16 *
ipike_exchange_sa_init_get_payload_order(Ipike_isakmp_exchange* ie)
{
static Ip_u16 sa_init_1[] = {
IPIKE2_PAYLOAD_COOKIE,
IPIKE2_PAYLOAD_SECURITY_ASSOCIATION,
IPIKE2_PAYLOAD_CONFIGURATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_NOTIFY | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_KEY_EXCHANGE,
IPIKE2_PAYLOAD_NONCE,
IPIKE2_PAYLOAD_VENDOR_ID | IPIKE_PAYLOAD_OPT,
IPIKE_PAYLOAD_NONE
};
static Ip_u16 sa_init_2[] = {
IPIKE2_PAYLOAD_COOKIE,
IPIKE2_PAYLOAD_SECURITY_ASSOCIATION,
IPIKE2_PAYLOAD_CONFIGURATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_NOTIFY | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_KEY_EXCHANGE,
IPIKE2_PAYLOAD_NONCE,
IPIKE2_PAYLOAD_CERTIFICATE_REQUEST | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_VENDOR_ID | IPIKE_PAYLOAD_OPT,
IPIKE_PAYLOAD_NONE
};
static Ip_u16 sa_init_3[] = {
IPIKE2_PAYLOAD_NOTIFY | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD, /* for errors */
IPIKE2_PAYLOAD_IDENTIFICATION_I,
IPIKE2_PAYLOAD_IDENTIFICATION_R,
IPIKE2_PAYLOAD_CONFIGURATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_CERTIFICATE | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_CERTIFICATE_REQUEST | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_IDENTIFICATION_R | IPIKE_PAYLOAD_OPT,
IPIKE2_PAYLOAD_AUTHENTICATION,
IPIKE2_PAYLOAD_SECURITY_ASSOCIATION,
IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_I,
IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_R,
IPIKE2_PAYLOAD_VENDOR_ID | IPIKE_PAYLOAD_OPT,
IPIKE_PAYLOAD_NONE
};
static Ip_u16 sa_init_4[] = {
IPIKE2_PAYLOAD_CONFIGURATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_NOTIFY | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD, /* for errors */
IPIKE2_PAYLOAD_IDENTIFICATION_R,
IPIKE2_PAYLOAD_CERTIFICATE | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_AUTHENTICATION,
IPIKE2_PAYLOAD_SECURITY_ASSOCIATION,
IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_I,
IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_R,
IPIKE2_PAYLOAD_EAP | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_VENDOR_ID | IPIKE_PAYLOAD_OPT,
IPIKE_PAYLOAD_NONE
};
static Ip_u16 sa_init_eap_1[] = {
IPIKE2_PAYLOAD_NOTIFY | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD, /* for errors */
IPIKE2_PAYLOAD_AUTHENTICATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_EAP | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_VENDOR_ID | IPIKE_PAYLOAD_OPT,
IPIKE_PAYLOAD_NONE
};
static Ip_u16 sa_init_eap_2[] = {
IPIKE2_PAYLOAD_CONFIGURATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_NOTIFY | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD, /* for errors */
IPIKE2_PAYLOAD_SECURITY_ASSOCIATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_I | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_R | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_AUTHENTICATION | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_EAP | IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_ADD,
IPIKE2_PAYLOAD_VENDOR_ID | IPIKE_PAYLOAD_OPT,
IPIKE_PAYLOAD_NONE
};
const Ip_u16* sa_init[] = {
sa_init_1,
sa_init_2,
sa_init_3,
sa_init_4,
sa_init_eap_1,
sa_init_eap_2
};
if ((ie->message_number >= 1) && (ie->message_number <= 4))
return sa_init[ie->message_number - 1];
return sa_init[4 + ((ie->message_number - 1) % 2)];
}
/*
*===========================================================================
* ipike_exchange_sa_init_process_payloads
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_sa_init_process_payloads(
Ipike_isakmp_exchange* ie,
Ipike_isakmp_header* isakmp)
{
Ipike_error err = IPIKE_SUCCESS;
Ipike_payload_header* payload;
Ip_u8 this_payload;
Ipike_payload_enum ctx;
if (((isakmp->flags & IPIKE_ISAKMP_RESPONSE) &&
((ie->message_number % 2) != 0)) ||
(!(isakmp->flags & IPIKE_ISAKMP_RESPONSE) &&
((ie->message_number % 2) == 0)))
{
IPCOM_LOG0(ERR, "ipike_exchange_sa_init_process_payloads() :: "\
"exchange collision, abort and hope for retry later");
return IPIKE_FAILED;
}
ie->message_id = ip_ntohl(isakmp->message_id);
/* Store message to later verify authentication data */
if (ie->message_number == 1)
{
err = ipike_cpy_data(&ie->ike_sa->v2.auth_msg_i,
isakmp,
ip_htonl(isakmp->length));
}
else if (ie->message_number == 2)
{
err = ipike_cpy_data(&ie->ike_sa->v2.auth_msg_r,
isakmp,
ip_htonl(isakmp->length));
}
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_process_payloads() :: copy failed");
return err;
}
/* Process payloads */
this_payload = isakmp->next_payload;
payload = ipike_first_payload(isakmp, &ctx);
/* If require cookie is enabled the first message from initiator must
* contain a cookie notify with the responder cookie. If not ignore all
* payloads an send a cookie notify as response */
if (ipike.require_cookie && ie->message_number == 1)
{
/* The response will contain the cookie notify */
if (IPIKE2_PAYLOAD_NOTIFY != this_payload ||
(IPIKE_SUCCESS != ipike_payload_notify_process_cookie(ie,
(Ipike2_notify_payload*)payload)))
{
/* Only one try to get the cookie, drop if second attempt */
if (ie->cookie.len == 0)
{
ipike_free_payload_enumerator(&ctx);
return IPIKE_SUCCESS;
}
else
{
/* Set correct responder cookie, so SA will be deleted */
ipcom_memcpy(ie->ike_sa->responder_cookie,
ie->cookie.value,
ie->cookie.len);
ipike_free_payload_enumerator(&ctx);
return IPIKE_INVALID_COOKIE;
}
}
}
while (err == IPIKE_SUCCESS && this_payload != IPIKE_PAYLOAD_NONE &&
payload != IP_NULL)
{
switch (this_payload)
{
case IPIKE2_PAYLOAD_CONFIGURATION:
err = ipike_payload_configuration_process(
ie,
(Ipike2_cp_payload*)payload);
break;
case IPIKE2_PAYLOAD_NOTIFY:
err = ipike_payload_notify_process(
ie,
(Ipike2_notify_payload*)payload);
break;
case IPIKE2_PAYLOAD_SECURITY_ASSOCIATION:
if (ie->message_number <= 2)
err = ipike_payload_sa_extract((Ipike2_sa_payload*)payload,
&ie->ike_sa->v2.sa);
else if (ie->ipsec_sa != IP_NULL)
err = ipike_payload_sa_extract((Ipike2_sa_payload*)payload,
&ie->ipsec_sa->v2.sa);
break;
case IPIKE2_PAYLOAD_KEY_EXCHANGE:
if (ie->ike_sa->is_initiator)
err = ipike_payload_key_exchange_extract(
(Ipike2_ke_payload*)payload,
&ie->ike_sa->v2.dh_group,
&ie->ike_sa->g_xr);
else
err = ipike_payload_key_exchange_extract(
(Ipike2_ke_payload*)payload,
&ie->ike_sa->v2.dh_group,
&ie->ike_sa->g_xi);
break;
case IPIKE2_PAYLOAD_NONCE:
/* We will receive the nonce from the other side */
if (ie->ike_sa->is_initiator)
err = ipike_payload_nonce_extract((Ipike_nonce_payload*)payload,
&ie->ike_sa->nr);
else
err = ipike_payload_nonce_extract((Ipike_nonce_payload*)payload,
&ie->ike_sa->ni);
break;
case IPIKE2_PAYLOAD_IDENTIFICATION_I:
err = ipike_payload_identification_extract((Ipike2_id_payload*)payload,
&ie->ike_sa->v2.id_i);
ie->ike_sa->v2.id_i.initiator = IP_TRUE;
break;
case IPIKE2_PAYLOAD_IDENTIFICATION_R:
err = ipike_payload_identification_extract((Ipike2_id_payload*)payload,
&ie->ike_sa->v2.id_r);
ie->ike_sa->v2.id_r.initiator = IP_FALSE;
break;
case IPIKE2_PAYLOAD_CERTIFICATE:
err = ipike_payload_certificate_extract((Ipike_cert_payload*)payload,
&ie->ike_sa->cert);
break;
case IPIKE2_PAYLOAD_CERTIFICATE_REQUEST:
ie->certificate_requested = IP_TRUE;
err = ipike_payload_certificate_req_extract_v2(
(Ipike_cert_req_payload*)payload,
&ie->ocsp_resp_needed);
break;
case IPIKE2_PAYLOAD_AUTHENTICATION:
err = ipike_payload_authentication_extract(
(Ipike2_auth_payload*)payload,
&ie->ike_sa->v2.auth);
break;
case IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_I:
if (ie->ipsec_sa != IP_NULL)
err = ipike_payload_traffic_selector_extract(
IP_TRUE,
(Ipike2_ts_payload*)payload,
&ie->ipsec_sa->v2.ts_i);
break;
case IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_R:
if (ie->ipsec_sa != IP_NULL)
err = ipike_payload_traffic_selector_extract(
IP_FALSE,
(Ipike2_ts_payload*)payload,
&ie->ipsec_sa->v2.ts_r);
break;
case IPIKE2_PAYLOAD_EAP:
err = ipike_payload_eap_extract((Ipike2_eap_payload*)payload,
&ie->ike_sa->v2.eap);
break;
default:
err = ipike_payload_handle_unsupported_payload(
&ie->ike_sa->notify_data,
payload, this_payload);
break;
}
this_payload = payload->next_payload;
payload = ipike_next_payload(&ctx);
}
ipike_free_payload_enumerator(&ctx);
/* Update */
if (err == IPIKE_SUCCESS)
{
err = ipike_exchange_sa_init_update(ie, isakmp);
}
if (IPIKE_SUCCESS != err)
{
/* Can we handle the error? Errors that are handled will be reported
* back in the response */
err = ipike_exchange_handle_error(ie, ie->ike_sa, err);
}
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_add_reply_payloads
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_sa_init_add_reply_payloads(
Ipike_isakmp_exchange* ie,
Ipike_payload_header*** reply_payloads)
{
Ipike_error err = IPIKE_SUCCESS;
const Ip_u16* payload_ids;
int i, j;
Ipike_payload_header* reply_payload;
Ip_u8 payload_type;
const char *url = IP_NULL;
/* We need to process again in case waiting for response, since the
* response will contain information needed. */
if (ie->wait_response)
if (IPIKE_SUCCESS != (err = ipike_exchange_sa_init_process_continue(ie)))
return err;
ie->isakmp_flags = 0;
/* Response payload should be marked as responses */
if ((ie->message_number % 2) == 0)
ie->isakmp_flags |= IPIKE_ISAKMP_RESPONSE;
/* Also the original initiator should be marked */
if (ie->ike_sa->is_initiator)
ie->isakmp_flags |= IPIKE_ISAKMP_INITIATOR;
/* All payloads after the initial two messages should be encrypted */
if (ie->message_number > 2)
ie->isakmp_flags |= IPIKE_ISAKMP_ENCRYPTION;
/* Generate iv if needed */
if (ie->isakmp_flags & IPIKE_ISAKMP_ENCRYPTION)
err = ipike_crypto_generate_iv(ie->ike_sa, &ie->iv);
/* Message id is increased by one for each request/response pair */
if (!(ie->isakmp_flags & IPIKE_ISAKMP_RESPONSE))
ie->message_id = ie->ike_sa->v2.message_id++;
/* Should we handle a error response */
if ( ipike_exchange_sa_init_has_ike_sa_error(ie) ||
ipike_exchange_sa_init_has_child_sa_error(ie) )
{
/* Notify error */
err = ipike_payload_notify_add_error_reply(ie, reply_payloads);
if ( err != IPIKE_SUCCESS )
return err;
/* Abort if it's an IKE SA error */
if ( ipike_exchange_sa_init_has_ike_sa_error(ie) )
{
/* Mark this exchange for destruction, it will be deleted
* after the response has been sent */
ie->send_and_destroy = IP_TRUE;
return IPIKE_SUCCESS;
}
}
payload_ids = ipike_exchange_sa_init_get_payload_order(ie);
ip_assert(payload_ids != IP_NULL);
for (i = 0; payload_ids[i] != IPIKE_PAYLOAD_NONE; i++)
{
if (IP_BIT_ISSET(payload_ids[i], IPIKE_PAYLOAD_OPT) &&
IP_BIT_ISSET(payload_ids[i], IPIKE_PAYLOAD_ADD) == IP_FALSE)
/* Only add the optional payload that had the IPIKE_PAYLOAD_ADD bit set */
continue;
payload_type = (Ip_u8) payload_ids[i];
reply_payload = IP_NULL;
switch (payload_type)
{
case IPIKE2_PAYLOAD_CONFIGURATION:
if ( ipike_exchange_sa_init_send_ipsec_info(ie) )
{
err = ipike_payload_configuration_create(
&ie->ike_sa->cp_response,
(Ipike2_cp_payload**)&reply_payload);
if (IPIKE_SUCCESS == err && IP_NULL != reply_payload)
err = ipike_array_add((void***) reply_payloads,
reply_payload);
}
if (IPIKE_SUCCESS == err)
err = ipike_payload_configuration_create(
&ie->ike_sa->cp_request,
(Ipike2_cp_payload**)&reply_payload);
break;
case IPIKE2_PAYLOAD_COOKIE:
/* Send cookie notify if responder haven't got any cookie */
if (ipike.require_cookie && ie->message_number == 2 &&
!ie->has_cookie)
{
err = ipike_payload_notify_cookie(
ie,
&ie->cookie,
(Ipike2_notify_payload**)&reply_payload);
/* This should be the only payload, and initator should restart
* we also need to zero responder cookie */
ipcom_memset((void*)ie->ike_sa->responder_cookie,
0,
sizeof(ie->ike_sa->responder_cookie));
ie->message_number -= 2;
if (IPIKE_SUCCESS == err)
err = ipike_array_add((void***) reply_payloads, reply_payload);
return err;
}
/* Send responder cookie back to responder */
if ( ie->message_number == 1 && ie->cookie.len > 0 )
{
if (IPIKE_SUCCESS == err)
err = ipike_payload_notify_cookie(
ie,
&ie->cookie,
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
err = ipike_array_add((void***) reply_payloads,
reply_payload);
reply_payload = IP_NULL;
}
break;
case IPIKE2_PAYLOAD_NOTIFY:
/* Add initial contact notify if wanted */
if ( ie->message_number > 2 && ie->ike_conf->initial_contact )
{
if (IPIKE_SUCCESS == err)
err = ipike_payload_notify_initial_contact(
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
err = ipike_array_add((void***) reply_payloads, reply_payload);
reply_payload = IP_NULL;
}
/* Send NAT detection in case NAT Traversal enabled */
if ( ie->message_number <= 2 && ipike.port_nat != 0 )
{
/* Always two payloads (local and peer) */
if (IPIKE_SUCCESS == err)
err = ipike_payload_notify_nat_detection(
ie,
IP_TRUE,
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
err = ipike_array_add((void***) reply_payloads,
reply_payload);
if (IPIKE_SUCCESS == err)
err = ipike_payload_notify_nat_detection(
ie,
IP_FALSE,
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
err = ipike_array_add((void***) reply_payloads,
reply_payload);
reply_payload = IP_NULL;
}
/* Add TFC notify */
if ( IPIKE_SUCCESS == err && ie->message_number > 2 &&
ie->ipsec_conf != IP_NULL &&
(ie->ipsec_conf->use_tfc == IP_NULL ||
ipcom_strstr(ie->ipsec_conf->use_tfc,"off")!= IP_NULL) )
{
err = ipike_payload_notify_dont_use_tfc(
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads,
reply_payload);
if (IPIKE_SUCCESS == err)
reply_payload = IP_NULL;
}
}
/* Send transport notify if we should try using transport mode */
if ( IPIKE_SUCCESS == err && ie->ipsec_sa != IP_NULL &&
ie->ipsec_sa->selected.encap_mode == IPIKE_IPSEC_ENCAPSULATION_MODE_TRANSPORT &&
(ie->ike_sa->is_initiator || ie->ike_sa->v2.transport_notify))
{
err = ipike_payload_notify_transport_mode(
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads,
reply_payload);
if (IPIKE_SUCCESS == err)
reply_payload = IP_NULL;
}
}
/* Send HTTP_CERT_LOOKUP_SUPPORTED if we have enabled it */
if ( IPIKE_SUCCESS == err && ipike.use_hash_and_url &&
(ie->message_number == 2 || ie->message_number == 3) )
{
err = ipike_payload_notify_http_cert_lookup_support(
(Ipike2_notify_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
err = ipike_array_add((void***) reply_payloads, reply_payload);
if (IPIKE_SUCCESS == err)
reply_payload = IP_NULL;
}
}
#ifdef IPIKE_USE_ROHC
/* Add ROHC notify */
if ( IPIKE_SUCCESS == err && ie->message_number >= 3 )
{
err = ipike_payload_notify_rohc((Ipike2_notify_payload**)&reply_payload,
ie);
}
#endif /* IPIKE_USE_ROHC */
break;
case IPIKE2_PAYLOAD_SECURITY_ASSOCIATION:
/* First two messages are for IKE SA, the following for IPSEC SA */
if ( ie->message_number < 3 )
{
err = ipike_payload_sa_create(
&ie->ike_sa->v2.sa,
(Ipike2_sa_payload**)&reply_payload);
}
else if ( IPIKE_SUCCESS == err &&
ipike_exchange_sa_init_send_ipsec_info(ie) )
{
/* Add mobike payloads, will be sent with SA payload for AUTH, so location may
* change depending on EAP */
if ( ie->message_number >= 3 && ipcom_sysvar_get_as_int("ipike.mobike", 0) != 0 )
{
err = ipike_exchange_sa_init_add_mobike_payloads(ie, reply_payloads);
}
if ( IPIKE_SUCCESS == err && ie->ipsec_sa != IP_NULL )
{
err = ipike_payload_sa_create(&ie->ipsec_sa->v2.sa, (Ipike2_sa_payload**)&reply_payload);
}
}
break;
case IPIKE2_PAYLOAD_KEY_EXCHANGE:
if ( ie->ike_sa->is_initiator )
err = ipike_payload_key_exchange_create(
ie->ike_sa->v2.dh_group,
&ie->ike_sa->g_xi,
(Ipike2_ke_payload**)&reply_payload);
else
err = ipike_payload_key_exchange_create(
ie->ike_sa->v2.dh_group,
&ie->ike_sa->g_xr,
(Ipike2_ke_payload**)&reply_payload);
break;
case IPIKE2_PAYLOAD_NONCE:
/* We will send our nonce to the other side */
if ( ie->ike_sa->is_initiator )
err = ipike_payload_nonce_create(
&ie->ike_sa->ni,
(Ipike_nonce_payload**)&reply_payload);
else
err = ipike_payload_nonce_create(
&ie->ike_sa->nr,
(Ipike_nonce_payload**)&reply_payload);
/* Same as IKE v1 except type */
reply_payload->next_payload = IPIKE2_PAYLOAD_NONCE;
break;
case IPIKE2_PAYLOAD_IDENTIFICATION_I:
if (ie->ike_sa->v2.id_i.data.len > 0)
err = ipike_payload_identification_create(
&ie->ike_sa->v2.id_i,
(Ipike2_id_payload**)&reply_payload);
break;
case IPIKE2_PAYLOAD_IDENTIFICATION_R:
if (ie->ike_sa->v2.id_r.data.len > 0)
err = ipike_payload_identification_create(
&ie->ike_sa->v2.id_r,
(Ipike2_id_payload**)&reply_payload);
break;
case IPIKE2_PAYLOAD_CERTIFICATE:
if ( !ipike_isakmp_should_send_certificate(ie) )
break;
if ( ie->ike_sa->v2.use_hash_and_url )
url = ie->ike_conf->authentication.certificate_uri;
if ((ie->ike_sa->selected.auth_method == IPIKE2_AUTH_RSA_SIGNATURE) ||
(ie->ike_sa->selected.auth_method == IPIKE2_AUTH_DSS_SIGNATURE))
err = ipike_payload_certificate_create(
ie->ike_sa->cert.local,
url,
(Ipike_cert_payload**)&reply_payload);
/* Same as IKE v1 except type */
if (IP_NULL != reply_payload)
{
reply_payload->next_payload = IPIKE2_PAYLOAD_CERTIFICATE;
/* We should also add our CA certificates */
if (IP_NULL != ie->ike_sa->cert.ca)
{
j = 0;
while (reply_payload != IP_NULL && err == IPIKE_SUCCESS
&& ie->ike_sa->cert.ca[j] != IP_NULL)
{
err = ipike_array_add((void***) reply_payloads,
reply_payload);
if (IPIKE_SUCCESS == err)
{
reply_payload = IP_NULL;
err = ipike_payload_certificate_create(
ie->ike_sa->cert.ca[j++],
url,
(Ipike_cert_payload**)&reply_payload);
if (reply_payload != IP_NULL)
reply_payload->next_payload = IPIKE2_PAYLOAD_CERTIFICATE;
}
}
}
#ifdef IPIKE_USE_OCSP
if ( IPIKE_SUCCESS==err &&
reply_payload != IP_NULL &&
ie->ocsp_resp_needed == IP_TRUE &&
ie->ike_sa->cert.resp_local != IP_NULL )
{
/* Add the last certificate/CA certificate */
err = ipike_array_add((void***) reply_payloads,
reply_payload);
if ( IPIKE_SUCCESS == err )
{
reply_payload = IP_NULL;
err = ipike_payload_certificate_ocsp_create(
ie->ike_sa->cert.resp_local,
(Ipike_cert_payload**)&reply_payload);
if (IPIKE_SUCCESS == err)
{
reply_payload->next_payload = IPIKE2_PAYLOAD_CERTIFICATE;
}
}
}
#endif
}
break;
case IPIKE2_PAYLOAD_CERTIFICATE_REQUEST:
if ( (ie->ike_sa->selected.auth_method == IPIKE2_AUTH_RSA_SIGNATURE ||
ie->ike_sa->selected.auth_method == IPIKE2_AUTH_DSS_SIGNATURE ) &&
ipike_isakmp_should_send_certificate_req(ie) )
{
IPCOM_LOG0(DEBUG2,
"ipike_exchange_sa_init_add_reply_payloads()"\
" :: creating CERTREQ content");
err = ipike_payload_certificate_req_create_v2(
(Ipike_cert_req_payload**)&reply_payload,
&ie->ike_sa->cert);
}
else
err = IPIKE_SUCCESS;
break;
case IPIKE2_PAYLOAD_AUTHENTICATION:
err = ipike_payload_authentication_create(
&ie->ike_sa->v2.auth,
(Ipike2_auth_payload**)&reply_payload);
break;
case IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_I:
if ( ipike_exchange_sa_init_send_ipsec_info(ie) )
{
if ( ie->ipsec_sa != IP_NULL )
err = ipike_payload_traffic_selector_create(
&ie->ipsec_sa->v2.ts_i,
(Ipike2_ts_payload**)&reply_payload);
}
break;
case IPIKE2_PAYLOAD_TRAFFIC_SELECTOR_R:
if ( ipike_exchange_sa_init_send_ipsec_info(ie) )
{
if ( ie->ipsec_sa != IP_NULL )
err = ipike_payload_traffic_selector_create(
&ie->ipsec_sa->v2.ts_r,
(Ipike2_ts_payload**)&reply_payload);
}
break;
case IPIKE2_PAYLOAD_EAP:
err = ipike_payload_eap_create(
&ie->ike_sa->v2.eap,
(Ipike2_eap_payload**)&reply_payload);
break;
default:
IPCOM_LOG1(ERR,
"ipike_exchange_sa_init_add_reply_payloads() :: payload"\
" creation failed since %d is a unknown payload type",
payload_type);
err = IPIKE_ISAKMP_UNSUPPORTED_PAYLOAD;
break;
}
if (err != IPIKE_SUCCESS)
break;
if (IP_NULL != reply_payload)
{
err = ipike_array_add((void***) reply_payloads, reply_payload);
if (IPIKE_SUCCESS != err)
{
ipcom_free(reply_payload);
break;
}
}
}
if (err != IPIKE_SUCCESS)
{
ipike_array_free((void**) *reply_payloads, ipcom_free);
*reply_payloads = IP_NULL;
}
return err;
}
/*
*===========================================================================
* ipike_exchange_sa_init_cleartext_outgoing_message
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_sa_init_cleartext_outgoing_message(
Ipike_isakmp_exchange* ie,
Ipike_isakmp_header** outgoing_isakmp)
{
Ipike_error err = IPIKE_SUCCESS;
Ipike_isakmp_header* isakmp = (*outgoing_isakmp);
/* Store message to later generate authentication data */
if (ie->message_number == 1)
err = ipike_cpy_data(&ie->ike_sa->v2.auth_msg_i,
isakmp,
ip_htonl(isakmp->length));
else if (ie->message_number == 2)
err = ipike_cpy_data(&ie->ike_sa->v2.auth_msg_r,
isakmp,
ip_htonl(isakmp->length));
if (IPIKE_SUCCESS != err)
{
IPCOM_LOG0(ERR,
"ipike_exchange_sa_init_cleartext_outgoing_message() ::"\
" copy failed");
return err;
}
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_sa_init_done
*===========================================================================
* Called when the complete 4-package handshake is done.
*/
IP_PUBLIC Ipike_error
ipike_exchange_sa_init_done(Ipike_isakmp_exchange *ie)
{
Ipike_error err;
Ip_bool collision = IP_FALSE;
/* Check for IKE SA errors */
if ( ipike_exchange_sa_init_has_ike_sa_error(ie) )
{
IPIKE_EVENT_LOG(IKE_PHASE_I_FAIL,
UNDEFINED,
&ie->ike_sa->local_addr,
&ie->ike_sa->peer_addr,
0,
IP_NULL);
/* IKE SA is in error state, returning IPIKE_EXCH_FINNISHED here will
* cause SA to be removed. */
IPCOM_LOG1(ERR, "ipike_exchange_sa_init_done() :: failed to "\
"establish ike_sa (%d)", ie->ike_sa->error);
return IPIKE_EXCH_FINNISHED;
}
/* Add parameters that are not negotiated */
if (ie->ike_conf->lifetime_seconds != IP_NULL)
ie->ike_sa->selected.life_seconds = *ie->ike_conf->lifetime_seconds[0];
if (ie->ike_conf->lifetime_kilobytes != IP_NULL)
ie->ike_sa->selected.life_kilobytes = *ie->ike_conf->lifetime_kilobytes[0];
/* Set liveness timeout */
ie->ike_sa->liveness.timeout = (Ip_u32) ie->ike_conf->liveness;
collision = ipike_exchange_sa_init_handle_simultaneous(ie);
if ( !collision )
{
/* Complete first IPSec SA */
if ( !ipike_exchange_sa_init_has_child_sa_error(ie) )
{
err = ipike_exchange_child_sa_done(ie);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(DEBUG,
"ipike_exchange_sa_init_done() ::"\
" failed to create child sa");
return err;
}
}
}
/* IKE SA is now active */
err = ipike_sadb_set_state(ie, ie->ike_sa, IPIKE_SA_ACTIVE);
if (IPIKE_SUCCESS != err)
return err;
ipcom_microtime(&ie->stop);
IPCOM_LOG3(INFO,
"IKE SA INIT done in %d ms (peer: %s, Message ID: %d)",
(ie->stop.tv_sec - ie->start.tv_sec) * 1000 +
(ie->stop.tv_usec - ie->start.tv_usec) / 1000,
ipike_addr_to_str(&ie->ike_sa->peer_addr),
ie->message_id);
/* Free sadb variables that are not needed anymore */
ipike_sadb_free_intermediate(ie->ike_sa);
/* Clear any non-critical errors */
ie->ike_sa->error = IPIKE_SUCCESS;
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_handle_error
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_handle_error(
Ipike_isakmp_exchange * ie,
Ipike_sadb_entry * sa,
Ipike_error err)
{
Ip_u16 id;
Ip_u8 version;
sa->error = err;
/* Only responder may send error codes */
if ( (ie->message_number % 2) != 1)
return err;
switch (err)
{
/* IKE Errors */
case IPIKE_ISAKMP_INVALID_MAJOR_VERSION:
version = ipike.version_max;
(void)ipike_set_data(&sa->notify_data, &version, 1);
err = IPIKE_SUCCESS;
break;
case IPIKE_ISAKMP_KE_PAYLOAD:
id = ip_htons(sa->v2.dh_group);
err = ipike_set_data(&sa->notify_data, &id, 2);
/* Was the invalid KE payload received in the IKE INIT exchange? */
if (ie->exchange_type == IPIKE2_ISAKMP_EXCH_IKE_SA_INIT)
{
Ip_u8 zero_cookie[IPIKE_ISAKMP_COOKIE_SIZE] = {0};
/* We will try to reinitiate the IKE negotiation with correct
* DH group, make sure that the responder SPI is cleared before
* that since all IKE INIT requests must have the responder SPI
* set to 0's. */
ipike_sadb_update_responder_cookie(sa, zero_cookie);
}
break;
default:
err = IPIKE_SUCCESS;
break;
}
return err;
}
/*
*===========================================================================
* ipike_exchange_generate_dh_key
*===========================================================================
*/
IP_PUBLIC Ipike_error
ipike_exchange_generate_dh_key(
Ipike_isakmp_exchange * ie,
Ipike_sadb_entry * sa,
Ip_u16 group)
{
Ipike_error err;
if (sa == IP_NULL)
{
/* IKE initial exchange */
if (ie->exchange_type == IPIKE2_ISAKMP_EXCH_IKE_SA_INIT)
sa = ie->ike_sa;
/* IKE Rekey */
else if (ie->ike_sa_new)
sa = ie->ike_sa_new;
/* IPsec rekey */
else
sa = ie->ipsec_sa;
}
ip_assert(sa != NULL);
/* Get dh group if not specified. The group will already be specified
* on the second attempt (responder didn't like the proposed group) */
if (group == 0)
group = ipike_proposal_get_next_transform(
(sa == ie->ike_sa_new) ?
&ie->ike_sa_new->v2.sa : &ie->ike_sa->v2.sa,
IPIKE_TRANSFORM_TYPE_DH,
0);
if (group == IPIKE_INVALID_VALUE)
{
IPCOM_LOG0(DEBUG,
"ipike_exchange_generate_dh_key() :: "\
"no diffie-hellman group specified");
return IPIKE_ISAKMP_NO_PROPOSAL_CHOSEN;
}
/* Generate Diffie-Hellman key */
sa->v2.dh_group = group;
err = ipike_payload_key_exchange_generate(sa->v2.dh_group,
&ie->dh,
&sa->g_xi);
if (err != IPIKE_SUCCESS)
{
IPCOM_LOG0(CRIT,
"ipike_exchange_generate_dh_key() ::"\
" failed to generate diffie hellman key");
return err;
}
/* Should always reselect the SA */
sa->v2.sa.selected_proposal = -1;
return IPIKE_SUCCESS;
}
/*
*===========================================================================
* ipike_exchange_merge_traffic_selectors
*===========================================================================
*/
IP_GLOBAL Ipike_error
ipike_exchange_merge_traffic_selectors(Ipike_isakmp_exchange *ie)
{
Ipike_error err = IPIKE_SUCCESS;
Ipike_config_flow *flow;
Ipike2_ts* ts_local;
Ipike2_ts* ts_remote;
Ipike2_ts ts;
Ipike_config_netobj tmp;
union Ip_sockaddr_union tmp_addr;
int i;
ipcom_memset(&ts, 0, sizeof(Ipike2_ts));
if (ie->exchange_initiator)
{
ts_local = &ie->ipsec_sa->v2.ts_i;
ts_remote = &ie->ipsec_sa->v2.ts_r;
}
else
{
ts_local = &ie->ipsec_sa->v2.ts_r;
ts_remote = &ie->ipsec_sa->v2.ts_i;
}
/* Special treatment for the transport mode and NAT */
if (ie->ike_sa->v2.transport_notify)
{
/* Are the remote side behind NAT */
if (ie->ike_sa->peer_behind_nat)
{
/* First TSr must be a specific address, i.e. not an range */
if (!ipike_addr_are_equal(&ts_remote->list[0]->addr_start,
&ts_remote->list[0]->addr_end))
return IPIKE_ISAKMP_TS_UNACCEPTABLE;
/* Store the old TS address */
(void)ipike_addr_copy(&ie->ike_sa->nat_oar, &ts_remote->list[0]->addr_start);
/* If the server is behind a NAT, substitute the IP address in the
TSr entries with the remote address of the IKE SA. */
ipcom_memcpy(ipike_addr_get_ptr(&ts_remote->list[0]->addr_start),
ipike_addr_get_ptr(&ie->ike_sa->peer_addr),
ipike_addr_get_len(&ie->ike_sa->peer_addr));
ipcom_memcpy(ipike_addr_get_ptr(&ts_remote->list[0]->addr_end),
ipike_addr_get_ptr(&ie->ike_sa->peer_addr),
ipike_addr_get_len(&ie->ike_sa->peer_addr));
}
/* Are we sitting behind NAT */
if (ie->ike_sa->local_behind_nat)
{
/* First TSi must be a specific address, i.e. not an range */
if (!ipike_addr_are_equal(&ts_local->list[0]->addr_start,
&ts_local->list[0]->addr_end))
return IPIKE_ISAKMP_TS_UNACCEPTABLE;
/* Store the old TS address */
(void)ipike_addr_copy(&ie->ike_sa->nat_oai,
&ts_local->list[0]->addr_start);
/* If the client is behind a NAT, substitute the IP address in the
TSi entries with the local address of the IKE SA. */
ipcom_memcpy(ipike_addr_get_ptr(&ts_local->list[0]->addr_start),
ipike_addr_get_ptr(&ie->ike_sa->local_addr),
ipike_addr_get_len(&ie->ike_sa->local_addr));
ipcom_memcpy(ipike_addr_get_ptr(&ts_local->list[0]->addr_end),
ipike_addr_get_ptr(&ie->ike_sa->local_addr),
ipike_addr_get_len(&ie->ike_sa->local_addr));
}
}
/* We should limit the initiator traffic selector, so we only include
* what is accepted by the responder. Fail if nothing accepted. */
flow = &ie->ipsec_conf->flow;
if (ie->ike_sa->dhcp.has_info && !ie->ike_sa->is_initiator)
{
/* Generate remote selectors */
ipike_config_netobj_from_addr(&tmp, &ie->ike_sa->dhcp.address);
/* Dynamic flow address, ignore config file */
err = ipike_payload_traffic_selector_element_add(&tmp, 0, &ts);
if ( err != IPIKE_SUCCESS)
goto errout;
/* Try to merge selectors */
err = ipike_payload_traffic_selector_merge(ts_remote, &ts);
ipike_payload_traffic_selector_free(&ts);
if ( err != IPIKE_SUCCESS)
goto errout;
}
else
{
/* Use remote traffic selector from config file and generate
* remote selectors */
for ( i = 0; flow->dst[i] != IP_NULL; i++)
{
err = ipike_payload_traffic_selector_element_add(flow->dst[i],
flow->protocol,
&ts);
if ( err != IPIKE_SUCCESS)
goto errout;
}
/* Narrow received traffic selector with our local selector "ts" */
err = ipike_payload_traffic_selector_merge(ts_remote, &ts);
ipike_payload_traffic_selector_free(&ts);
if ( err != IPIKE_SUCCESS)
goto errout;
}
/* Use any flow as local selector
* !!Remove? Use selector from flow in config instead
* Leaving this for backward compatibility for now */
if ( ie->ike_sa->dhcp.has_info && !ie->ike_sa->is_initiator &&
ipike.dynamic_ip_anyflow)
{
/* Any flow (0.0.0.0/0 or ::/0) */
ipike_addr_copy(&tmp_addr, &ie->ike_sa->dhcp.address);
ipcom_memset(ipike_addr_get_ptr(&tmp_addr),
0x00,
ipike_addr_get_len(&tmp_addr));
ipike_config_netobj_from_addr(&tmp, &tmp_addr);
err = ipike_payload_traffic_selector_element_add(&tmp, 0, &ts);
if ( err != IPIKE_SUCCESS )
goto errout;
err = ipike_payload_traffic_selector_merge(ts_local, &ts);
ipike_payload_traffic_selector_free(&ts);
if ( err != IPIKE_SUCCESS)
goto errout;
}
else
{
/* Use local traffic selector from config file */
/* Generate local selectors */
for ( i = 0; flow->src[i] != IP_NULL; i++)
{
err = ipike_payload_traffic_selector_element_add(flow->src[i],
flow->protocol,
&ts);
if ( err != IPIKE_SUCCESS)
goto errout;
}
err = ipike_payload_traffic_selector_merge(ts_local, &ts);
ipike_payload_traffic_selector_free(&ts);
}
errout:
return err;
}
#else
int ipike_exchange_sa_init_empty_file = 0;
#endif /* IPIKE_USE_VERSION2 */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/