591 lines
20 KiB
C
591 lines
20 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_exchange_informational.c,v $ $Revision: 1.20.10.7 $
|
|
* $Source: /home/interpeak/CVSRoot/ipike/src/ipike_exchange_informational.c,v $
|
|
* $Author: svc-cmnet $ $Date: 2013-10-30 23:37:40 $
|
|
* $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
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipike_h.h"
|
|
#include "ipike_exchange_informational.h"
|
|
#include "ipike_crypto.h"
|
|
#include "ipike_payload_delete.h"
|
|
#include "ipike_payload_hash.h"
|
|
#include "ipike_payload_notification.h"
|
|
#include "ipike_util.h"
|
|
|
|
#include <openssl/hmac.h>
|
|
#include <openssl/rand.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_syslog.h>
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
* Checks if the DPD counter is in the window _base + IPIKE_DPD_WINDOW_SIZE
|
|
*/
|
|
#define IPIKE_IS_IN_DPD_WINDOW(_base, _counter) \
|
|
(((((_base) + IPIKE_DPD_WINDOW_SIZE) >= (_counter)) && \
|
|
((_base) < (_counter) )) || \
|
|
((((_base) + IPIKE_DPD_WINDOW_SIZE) < (_base)) && \
|
|
(((_counter) < (IPIKE_DPD_WINDOW_SIZE - (0xffffffffu-(_base)))) || \
|
|
((_base) < (_counter)))))
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_on_new_exchange
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_informational_on_new_exchange(Ipike_isakmp_exchange* ie)
|
|
{
|
|
Ipike_error err;
|
|
Ipike_transform_ike *transform_ike;
|
|
|
|
/* IKE v1 */
|
|
ie->version = 1 << 4;
|
|
|
|
ipcom_microtime(&ie->start);
|
|
|
|
if (ie->ike_sa->state == IPIKE_SA_ACTIVE)
|
|
{
|
|
/* Messages must be protected */
|
|
ie->isakmp_flags = IPIKE_ISAKMP_ENCRYPTION;
|
|
|
|
transform_ike = ipike_transform_ike_from_exch(ie);
|
|
if (transform_ike==IP_NULL)
|
|
return IPIKE_FAILED;
|
|
if (IPIKE_SUCCESS != (err = ipike_crypto_compute_phase2_iv(
|
|
transform_ike->hash_algorithm,
|
|
&ie->ike_sa->base_iv, ip_htonl(ie->message_id), &ie->iv)))
|
|
return err;
|
|
}
|
|
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_on_new_exchange
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_on_new_exchange_initiator(
|
|
Ipike_isakmp_exchange* ie,
|
|
Ipike_sadb_entry* ike_sa,
|
|
const void* payload,
|
|
const void* not_used)
|
|
{
|
|
IPCOM_UNUSED_ARG(not_used);
|
|
|
|
ie->ike_sa = ike_sa;
|
|
ipike_sadb_lock(ie->ike_sa);
|
|
|
|
ie->phase2.notify_or_delete = (Ipike_payload_header*) payload;
|
|
|
|
ie->is_phase1 = IP_FALSE;
|
|
ie->exchange_initiator = IP_TRUE;
|
|
|
|
return ipike_informational_on_new_exchange(ie);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_on_new_exchange
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_on_new_exchange_responder(
|
|
Ipike_isakmp_exchange* ie,
|
|
Ipike_isakmp_header* first_isakmp,
|
|
union Ip_sockaddr_union* local_addr,
|
|
union Ip_sockaddr_union* peer_addr)
|
|
{
|
|
(void) local_addr;
|
|
(void) peer_addr;
|
|
|
|
if (ipike_sadb_get( first_isakmp->initiator_cookie,
|
|
first_isakmp->responder_cookie,
|
|
IPIKE_SA_ACTIVE | IPIKE_SA_CONSTRUCTING,
|
|
&ie->ike_sa) != IPIKE_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(WARNING, "ipike_informational_on_new_exchange: IKE SA not found");
|
|
IPCOM_LOG1(WARNING, "initiator_cookie: %s",
|
|
ipike_cookie_to_string(first_isakmp->initiator_cookie));
|
|
IPCOM_LOG1(WARNING, "responder_cookie: %s",
|
|
ipike_cookie_to_string(first_isakmp->responder_cookie));
|
|
|
|
IPIKE_EVENT_LOG(IKE_PACKET_DISCARD, INVALID_SPI_ERROR,
|
|
peer_addr, local_addr, 0, IP_NULL);
|
|
|
|
return IPIKE_SA_NOT_FOUND;
|
|
}
|
|
|
|
return ipike_informational_on_new_exchange(ie);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_on_delete_exchange
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC void
|
|
ipike_informational_on_delete_exchange(Ipike_isakmp_exchange* ie)
|
|
{
|
|
if (ie->phase2.notify_or_delete)
|
|
IPIKE_FREEP(ie->phase2.notify_or_delete);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_get_payload_order
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC const Ip_u16 *
|
|
ipike_informational_get_payload_order(Ipike_isakmp_exchange *ie)
|
|
{
|
|
/* The hash payload must be included if a IKE SA
|
|
is established with the recipient.
|
|
The ISAKMP message must contain one notification payload
|
|
or one delete payload */
|
|
static Ip_u16 informational_exchange[] = {
|
|
IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_HASH,
|
|
IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_NOTIFICATION,
|
|
IPIKE_PAYLOAD_OPT | IPIKE_PAYLOAD_DELETE,
|
|
IPIKE_PAYLOAD_NONE,
|
|
};
|
|
|
|
(void) ie;
|
|
return informational_exchange;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_process_payloads
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_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;
|
|
|
|
/* Process payloads */
|
|
this_payload = isakmp->next_payload;
|
|
payload = ipike_first_payload(isakmp, &ctx);
|
|
while (payload != IP_NULL)
|
|
{
|
|
switch (this_payload)
|
|
{
|
|
case IPIKE_PAYLOAD_HASH:
|
|
if(!(err = ipike_hash_extract((Ipike_hash_payload*) payload, &ie->hash)))
|
|
err = ipike_crypto_verify_hash(ie, isakmp);
|
|
break;
|
|
case IPIKE_PAYLOAD_NOTIFICATION:
|
|
err = ipike_notification_process_message(ie, (Ipike_notification_payload*) payload);
|
|
break;
|
|
case IPIKE_PAYLOAD_DELETE:
|
|
err = ipike_payload_delete_process_message(ie->ike_sa, (Ipike_delete_payload*) payload);
|
|
break;
|
|
default:
|
|
IPCOM_LOG2(ERR,
|
|
"ipike_informational_process_payloads: '%s' (%d) is an unsupported payload type",
|
|
ipike_payload_type_to_string(this_payload), this_payload);
|
|
err = IPIKE_ISAKMP_UNSUPPORTED_PAYLOAD;
|
|
break;
|
|
}
|
|
if (err != IPIKE_SUCCESS)
|
|
break;
|
|
this_payload = payload->next_payload;
|
|
payload = ipike_next_payload(&ctx);
|
|
}
|
|
|
|
ipike_free_payload_enumerator(&ctx);
|
|
if (err != IPIKE_SUCCESS)
|
|
{
|
|
IPIKE_EVENT_LOG(IKE_PACKET_DISCARD, ISAKMP_PACKET_ERROR,
|
|
&ie->msg.peer_addr, &ie->msg.local_addr,
|
|
0, IP_NULL);
|
|
return err;
|
|
}
|
|
|
|
/* We do not need this exchange anymore */
|
|
return IPIKE_EXCH_FINNISHED;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_add_reply_payloads
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_add_reply_payloads(
|
|
Ipike_isakmp_exchange* ie,
|
|
Ipike_payload_header*** reply_payload)
|
|
{
|
|
Ipike_error err;
|
|
Ipike_transform_ike* transform;
|
|
|
|
if (ie->ike_sa->state != IPIKE_SA_CONSTRUCTING)
|
|
{
|
|
/* We have a IKE SA, so the payloads should be encrypted and
|
|
a hash payload is needed.
|
|
The hash value will be calculated later, just add it so
|
|
enough memory will be allocated */
|
|
Ip_u8 hash_buffer[HMAC_MAX_MD_CBLOCK];
|
|
Ipike_value dummy_hash;
|
|
Ipike_hash_payload* hash_payload = IP_NULL;
|
|
|
|
transform = ipike_transform_ike_from_exch(ie);
|
|
if (transform==IP_NULL)
|
|
return IPIKE_FAILED;
|
|
dummy_hash.value = hash_buffer;
|
|
dummy_hash.len = ipike_attribute_get_digest_size(transform->hash_algorithm);
|
|
if (IPIKE_SUCCESS != (err = ipike_hash_create(&dummy_hash, &hash_payload)))
|
|
goto error;
|
|
if (IPIKE_SUCCESS != (err = ipike_array_add((void***) reply_payload, hash_payload)))
|
|
{
|
|
ipcom_free(hash_payload);
|
|
goto error;
|
|
}
|
|
}
|
|
if (IPIKE_SUCCESS != (err = ipike_array_add((void***) reply_payload, ie->phase2.notify_or_delete)))
|
|
goto error;
|
|
/* To avoid multiple releases of the memory for the delete/notify payload */
|
|
ie->phase2.notify_or_delete = IP_NULL;
|
|
error:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_cleartext_outgoing_message
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_cleartext_outgoing_message(
|
|
Ipike_isakmp_exchange* ie,
|
|
Ipike_isakmp_header** outgoing_isakmp)
|
|
{
|
|
Ipike_error err;
|
|
Ipike_isakmp_header *reply = *outgoing_isakmp;
|
|
|
|
if (ie->ike_sa->state != IPIKE_SA_CONSTRUCTING)
|
|
{
|
|
Ipike_value hash = { IP_NULL, 0};
|
|
Ipike_payload_header* hash_payload;
|
|
|
|
if (reply->next_payload != IPIKE_PAYLOAD_HASH)
|
|
{
|
|
IPCOM_LOG1(ERR,
|
|
"ipike_informational_cleartext_outgoing_message: malformed quick mode message, "
|
|
"the first payload must be a hash payload (not %s)",
|
|
ipike_payload_type_to_string(reply->next_payload));
|
|
return IPIKE_ISAKMP_INVALID_PAYLOAD;
|
|
}
|
|
if (IPIKE_SUCCESS != (err = ipike_crypto_compute_hash_1(ie->ike_sa, reply, &hash)))
|
|
return err;
|
|
|
|
hash_payload = (Ipike_payload_header *) ((Ip_u8*) reply + sizeof(Ipike_isakmp_header));
|
|
ip_assert(ip_ntohs(hash_payload->length) == IPIKE_HASH_HEADER_LENGTH + hash.len);
|
|
ipcom_memcpy((Ip_u8*) hash_payload + IPIKE_HASH_HEADER_LENGTH, hash.value, hash.len);
|
|
ipcom_free(hash.value);
|
|
}
|
|
else
|
|
{
|
|
if (reply->next_payload != IPIKE_PAYLOAD_NOTIFICATION &&
|
|
reply->next_payload != IPIKE_PAYLOAD_DELETE)
|
|
{
|
|
IPCOM_LOG1(ERR,
|
|
"ipike_informational_cleartext_outgoing_message: malformed quick mode message, "
|
|
"the second payload must be a notification or delete payload (not %s)",
|
|
ipike_payload_type_to_string(reply->next_payload));
|
|
return IPIKE_ISAKMP_INVALID_PAYLOAD;
|
|
}
|
|
}
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_exchange_done
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_exchange_done(Ipike_isakmp_exchange* ie)
|
|
{
|
|
ipcom_microtime(&ie->stop);
|
|
|
|
IPCOM_LOG0(INFO, "informational exchange done");
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_report_error
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ipike_error
|
|
ipike_informational_report_error(
|
|
Ipike_sadb_entry* ike_sa,
|
|
Ipike_ipsec_sa_param* ipsec_sa,
|
|
Ipike_error error_code)
|
|
{
|
|
Ipike_error err;
|
|
Ip_u16 msg_type;
|
|
Ipike_value spi = { IP_NULL, 0 };
|
|
Ip_u8 isakmp_spi[IPIKE_ISAKMP_COOKIE_SIZE * 2];
|
|
Ip_u8 proto_id = IPIKE_PROTO_ISAKMP;
|
|
Ipike_notification_payload* notification;
|
|
|
|
if (IPIKE_SUCCESS != (err = ipike_notification_error_code_to_message_type(error_code, &msg_type)))
|
|
return err;
|
|
|
|
if (ike_sa->state == IPIKE_SA_CONSTRUCTING)
|
|
{
|
|
spi.value = isakmp_spi;
|
|
spi.len = sizeof(isakmp_spi);
|
|
ipcom_memcpy(isakmp_spi, ike_sa->initiator_cookie, IPIKE_ISAKMP_COOKIE_SIZE);
|
|
ipcom_memcpy(isakmp_spi + IPIKE_ISAKMP_COOKIE_SIZE, ike_sa->responder_cookie,
|
|
IPIKE_ISAKMP_COOKIE_SIZE);
|
|
}
|
|
else if (ipsec_sa)
|
|
{
|
|
/* we are in phase 2 (quick mode) IPSec DOI */
|
|
Ipike_sa* inbound;
|
|
|
|
/* Even if ESP+AH accepted only report first transform SPI in
|
|
error message. ignore the second ! */
|
|
inbound = &ipsec_sa->inbound_sa;
|
|
if (inbound->proposals && inbound->selected_proposal >= 0)
|
|
{
|
|
proto_id = inbound->proposals[inbound->selected_proposal]->proto_id;
|
|
spi = inbound->proposals[inbound->selected_proposal]->spi;
|
|
}
|
|
}
|
|
|
|
notification = ipike_notification_new(IPIKE_DOI_IPSEC,
|
|
proto_id,
|
|
msg_type,
|
|
spi.value,
|
|
(Ip_u8) spi.len,
|
|
IP_NULL,
|
|
0);
|
|
if (IP_NULL == notification)
|
|
{
|
|
IP_PANIC();
|
|
return IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return ipike_isakmp_initiate_informational_exchange(ike_sa, ¬ification->header);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_liveness
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC Ip_bool
|
|
ipike_informational_liveness(Ipike_sadb_entry* ike_sa, Ip_u32 counter)
|
|
{
|
|
Ipike_error err = IPIKE_SUCCESS;
|
|
Ipike_notification_payload* notification = IP_NULL;
|
|
Ip_u16 type = 0;
|
|
Ip_u8 spi_data[IPIKE_ISAKMP_COOKIE_SIZE * 2];
|
|
|
|
if (ike_sa->liveness.enabled)
|
|
{
|
|
if (counter == 0)
|
|
{
|
|
if (ike_sa->liveness.local_counter == 0)
|
|
{
|
|
(void)RAND_bytes((Ip_u8*)&ike_sa->liveness.local_counter, sizeof(ike_sa->liveness.local_counter));
|
|
ike_sa->liveness.local_counter &= 0x00ffffff;
|
|
}
|
|
|
|
/* Increase seqeuence number for each DPD R-U-THERE message */
|
|
ike_sa->liveness.local_counter++;
|
|
type = IPIKE_NOTI_R_U_THERE;
|
|
counter = ike_sa->liveness.local_counter;
|
|
}
|
|
else
|
|
{
|
|
/* Counter always incremented with one */
|
|
if (ike_sa->liveness.peer_counter == 0 ||
|
|
IPIKE_IS_IN_DPD_WINDOW(ike_sa->liveness.peer_counter, counter))
|
|
{
|
|
type = IPIKE_NOTI_R_U_THERE_ACK;
|
|
ike_sa->liveness.peer_counter = counter;
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipike_informational_liveness() :: Received"\
|
|
" DPD sequence number '%d' is not acceptable",
|
|
counter);
|
|
}
|
|
}
|
|
/* Initiate exchange if not rejected */
|
|
if (type != 0)
|
|
{
|
|
counter = ip_htonl(counter);
|
|
|
|
ipcom_memcpy(spi_data, ike_sa->initiator_cookie, IPIKE_ISAKMP_COOKIE_SIZE);
|
|
ipcom_memcpy(spi_data + IPIKE_ISAKMP_COOKIE_SIZE, ike_sa->responder_cookie, IPIKE_ISAKMP_COOKIE_SIZE);
|
|
|
|
notification = ipike_notification_new(IPIKE_DOI_IPSEC,
|
|
IPIKE_PROTO_ISAKMP,
|
|
type,
|
|
spi_data,
|
|
sizeof(spi_data),
|
|
(Ip_u8*)&counter,
|
|
sizeof(counter));
|
|
if (IP_NULL == notification)
|
|
{
|
|
IP_PANIC();
|
|
err = IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
err = ipike_isakmp_initiate_informational_exchange(ike_sa,
|
|
¬ification->header);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (err == IPIKE_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_report_sa_delete
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC void
|
|
ipike_informational_report_sa_delete(
|
|
Ipike_sadb_entry* ike_sa,
|
|
Ip_u8 proto_id,
|
|
Ipike_value* spi)
|
|
{
|
|
Ipike_delete_payload *del;
|
|
|
|
del = ipike_payload_delete_new(IPIKE_DOI_IPSEC, proto_id, (Ip_u8) spi->len, spi->value);
|
|
if (IP_NULL == del)
|
|
{
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
(void) ipike_isakmp_initiate_informational_exchange(ike_sa, &del->header);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_informational_report_initial_contact
|
|
*===========================================================================
|
|
*/
|
|
IP_PUBLIC void
|
|
ipike_informational_report_initial_contact(Ipike_sadb_entry* ike_sa)
|
|
{
|
|
Ipike_notification_payload* notify;
|
|
Ip_u8 spi_data[IPIKE_ISAKMP_COOKIE_SIZE * 2];
|
|
|
|
ipcom_memcpy(spi_data, ike_sa->initiator_cookie, IPIKE_ISAKMP_COOKIE_SIZE);
|
|
ipcom_memcpy(spi_data + IPIKE_ISAKMP_COOKIE_SIZE, ike_sa->responder_cookie, IPIKE_ISAKMP_COOKIE_SIZE);
|
|
|
|
notify = ipike_notification_new(IPIKE_DOI_IPSEC,
|
|
IPIKE_PROTO_ISAKMP,
|
|
IPIKE_NOTI_INITIAL_CONTACT,
|
|
spi_data,
|
|
sizeof(spi_data),
|
|
IP_NULL,
|
|
0);
|
|
if (IP_NULL == notify)
|
|
{
|
|
IP_PANIC();
|
|
return;
|
|
}
|
|
|
|
(void) ipike_isakmp_initiate_informational_exchange(ike_sa, ¬ify->header);
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|