/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 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 #include #include #include /* **************************************************************************** * 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 **************************************************************************** */