3394 lines
117 KiB
C
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
|
|
****************************************************************************
|
|
*/
|