/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_payload_sa.c,v $ $Revision: 1.32.10.15 $ * $Source: /home/interpeak/CVSRoot/ipike/src/ipike_payload_sa.c,v $ * $Author: yli12 $ $Date: 2016-03-25 09:56:29 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* IKE Security Association Payload NOTE! The current implementation does only support the IPSec DOI */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipike_h.h" #include "ipike_config.h" #include "ipike_payload_sa.h" #include "ipike_payload_proposal.h" #include "ipike_sadb.h" #include "ipike_util.h" #include "ipike_configuration.h" #include "ipike_ipsec_attributes.h" #include #include /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ /* **************************************************************************** * 6 TYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* *=========================================================================== * ipike_sa_header_size *=========================================================================== * Description: Returns the size of the SA payload header. * Parameters: sa - a pointer to a SA payload. * Returns: The size (in bytes) of the SA payload header. * */ IP_STATIC Ip_u16 ipike_sa_header_size(Ipike_sa_payload* sa); /* *=========================================================================== * ipike_sa_proposal_data *=========================================================================== * Description: Returns a pointer to the first proposal payload. * Parameters: sa - a pointer to a SA payload. * Returns: A pointer to the first proposal payload. * */ IP_STATIC Ipike_proposal_payload* ipike_sa_proposal_data(Ipike_sa_payload* sa); /* *=========================================================================== * ipike_sa_proposal_length *=========================================================================== * Description: Returns the length of the proposal payload(s). * Parameters: sa - a pointer to a SA payload. * Returns: The length (in bytes) of the proposal payload(s). * */ IP_STATIC Ip_u16 ipike_sa_proposal_length(Ipike_sa_payload* sa); /* *=========================================================================== * ipike_sa_process_message_ipsec *=========================================================================== * Description: Updates the security association and exchange paramters * with the selected encryption and hash alghorithm using * IPSec domain of interpretation. * Parameters: ie - paramters for the ongoing exchange. * payload - a SA payload. * sa - will be updated with the paramters from the SA payload * if the call was successful. * Returns: IPIKE_SUCCESS * IPIKE_OUT_OF_MEMORY * IPIKE_SA_NO_ACCEPTED_TRANSFORM, * IPIKE_SA_DOI_SITUATION_UNSUPPORTED, * */ IP_STATIC Ipike_error ipike_sa_process_message_ipsec(Ipike_isakmp_exchange* ie, Ipike_sa_payload* payload, Ipike_sa* sa); /* *=========================================================================== * ipike_sa_print_payload *=========================================================================== * Description: Prints the content of a security association payload to * the system log. * Parameters: sa - a SA payload. * print_proposal_payload - a function that knowns how to print * the proposal payload in the current phase and DOI. * Returns: * */ IP_STATIC void ipike_sa_print_payload( Ipike_sa_payload* sa, void (*print_proposal_payload)(Ipike_proposal_payload*), Ip_bool phase1); /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_sa_header_size *=========================================================================== */ IP_STATIC Ip_u16 ipike_sa_header_size(Ipike_sa_payload *sa) { switch (ip_ntohl(sa->doi)) { /* case IPIKE_DOI_ISAKMP: return IPIKE_SA_HEADER_SIZE_ISAKMP;*/ case IPIKE_DOI_IPSEC: return IPIKE_SA_HEADER_SIZE_IPSEC; } IPCOM_LOG1(ERR, "unsupported DOI (%d)", ip_ntohl(sa->doi)); return 0; } /* *=========================================================================== * ipike_sa_proposal_data *=========================================================================== */ IP_STATIC Ipike_proposal_payload * ipike_sa_proposal_data(Ipike_sa_payload *sa) { return (Ipike_proposal_payload *) ((Ip_u8 *) sa + ipike_sa_header_size(sa)); } /* *=========================================================================== * ipike_sa_proposal_length *=========================================================================== */ IP_STATIC Ip_u16 ipike_sa_proposal_length(Ipike_sa_payload *sa) { return (Ip_u16) (ip_ntohs(sa->header.length) - ipike_sa_header_size(sa)); } /* *=========================================================================== * ipike_sa_next_payload *=========================================================================== */ IP_STATIC Ipike_proposal_payload* ipike_sa_next_payload(Ipike_proposal_payload* payload) { return (Ipike_proposal_payload*)ipike_next_payload_structure(&payload->header); } /* *=========================================================================== * ipike_sa_process_message_ipsec *=========================================================================== */ IP_STATIC Ipike_error ipike_sa_process_message_ipsec( Ipike_isakmp_exchange* ie, Ipike_sa_payload* payload, Ipike_sa* sa) { Ipike_proposal_payload* proposal_payload = ipike_sa_proposal_data(payload); int proposal_count = 0; Ipike_error err; if (payload->situation.ip_sec == 0) { IPCOM_LOG0(WARNING, "No situation specified"); return IPIKE_SITUATION_UNSUPPORTED; } if ((payload->situation.ip_sec & IPIKE_SIT_SECRECY) || (payload->situation.ip_sec & IPIKE_SIT_INTEGRITY)) { IPCOM_LOG2(WARNING, "Situation: [%s%s ] not supported", payload->situation.ip_sec & IPIKE_SIT_SECRECY ? " SECRECY" : "", payload->situation.ip_sec & IPIKE_SIT_INTEGRITY ? " INTEGRITY" : ""); return IPIKE_SITUATION_UNSUPPORTED; } while (proposal_payload != IP_NULL) { Ipike_proposal* proposal; if (IP_NULL == (proposal = ipcom_calloc(1, sizeof(Ipike_proposal)))) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } err = ipike_proposal_extract(ie, proposal_payload, proposal); if (err == IPIKE_SUCCESS) { proposal_count++; err = ipike_array_add((void***) &sa->proposals, proposal); if (IPIKE_SUCCESS != err) return err; } else ipike_proposal_free(proposal); if (err != IPIKE_SUCCESS && err != IPIKE_SA_NO_ACCEPTED_TRANSFORM) return err; proposal_payload = ipike_sa_next_payload(proposal_payload); } sa->proposal_count = proposal_count; /* In phase1, multi proposal in one sa is not allowed */ if ((ie->is_phase1) && (sa->proposal_count > 1)) { return IPIKE_FAILED; } return proposal_count == 0 ? IPIKE_SA_NO_ACCEPTED_PROPOSAL : IPIKE_SUCCESS; } /* *=========================================================================== * ipike_sa_print_payload *=========================================================================== */ IP_STATIC void ipike_sa_print_payload( Ipike_sa_payload* sa, void (*print_proposal_payload)(Ipike_proposal_payload*), Ip_bool phase1) { Ipike_proposal_payload *proposal; if ( ipike_sa_check_payload(sa, phase1) != IPIKE_SUCCESS ) { IPCOM_LOG0(ERR, "ipike_sa_print_payload() :: unable to print invalid sa payload"); return; } IPCOM_LOG1(DEBUG2, " DOI: %s", ipike_doi_to_string(ip_ntohl(sa->doi))); /* The only supported DOI is IPSec */ IPCOM_LOG3(DEBUG2, " situation: [%s%s%s ]", sa->situation.ip_sec & IPIKE_SIT_IDENTITY_ONLY ? " IDENTITY" : "", sa->situation.ip_sec & IPIKE_SIT_SECRECY ? " SECRECY" : "", sa->situation.ip_sec & IPIKE_SIT_INTEGRITY ? " INTEGRITY" : ""); for (proposal = ipike_sa_proposal_data(sa); IP_NULL != proposal; proposal = ipike_sa_next_payload(proposal)) { IPCOM_LOG1(DEBUG2, " << %s >>", ipike_payload_type_to_string(IPIKE_PAYLOAD_PROPOSAL)); IPCOM_LOG2(DEBUG2, " Next Payload: %s (%d)", ipike_payload_type_to_string(proposal->header.next_payload), (int) proposal->header.next_payload); IPCOM_LOG1(DEBUG2, " Payload Length: %d", ip_ntohs(proposal->header.length)); print_proposal_payload(proposal); } } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_sa_new *=========================================================================== */ IP_GLOBAL Ipike_sa_payload* ipike_sa_new( Ip_u32 doi, Ipike_sa_situation situation, Ipike_proposal_payload** proposals) { Ip_u16 sa_header_size; Ip_u16 sa_size; Ipike_sa_payload* sa_payload; Ip_u8* proposal_data; int i; switch (doi) { case IPIKE_DOI_ISAKMP: sa_header_size = (Ip_u16) IPIKE_SA_HEADER_SIZE_ISAKMP; break; case IPIKE_DOI_IPSEC: sa_header_size = (Ip_u16) IPIKE_SA_HEADER_SIZE_IPSEC; break; default: IPCOM_LOG1(ERR, "DOI %d is not supported", doi); return IP_NULL; } sa_size = (Ip_u16)(ipike_payload_array_size((Ipike_payload_header**) proposals) + sa_header_size); if (IP_NULL == (sa_payload = ipcom_calloc(1, sa_size))) { IPCOM_LOG0(CRIT, "ipike_sa_create: out of memory"); return IP_NULL; } sa_payload->header.length = ip_htons(sa_size); sa_payload->header.next_payload = IPIKE_PAYLOAD_SECURITY_ASSOCIATION; sa_payload->doi = ip_htonl(doi); switch (doi) { case IPIKE_DOI_ISAKMP: sa_payload->situation.isakmp = situation.isakmp; break; case IPIKE_DOI_IPSEC: sa_payload->situation.ip_sec = situation.ip_sec; break; } proposal_data = ((Ip_u8*) sa_payload + sa_header_size); for (i = 0; proposals[i] != IP_NULL; i++) { if (proposals[i+1] != IP_NULL) proposals[i]->header.next_payload = IPIKE_PAYLOAD_PROPOSAL; else proposals[i]->header.next_payload = IPIKE_PAYLOAD_NONE; ipcom_memcpy(proposal_data, (void*)proposals[i], ip_ntohs(proposals[i]->header.length)); proposal_data += ip_ntohs(proposals[i]->header.length); } return sa_payload; } /* *=========================================================================== * ipike_sa_free *=========================================================================== */ IP_GLOBAL void ipike_sa_free(Ipike_sa *s, void(*free_func)(void*)) { ipike_array_free((void**) s->proposals, (void(*)(void*)) ipike_proposal_free); ipcom_memset(s, 0, sizeof(Ipike_sa)); if (free_func) free_func(s); } /* *=========================================================================== * ipike_sa_create *=========================================================================== */ IP_GLOBAL Ipike_error ipike_sa_create(Ipike_sa* sa, Ipike_sa_payload** payload) { Ipike_proposal_payload** proposals = IP_NULL; Ipike_proposal_payload* proposal; Ipike_error err = IPIKE_SUCCESS; int i = 0; Ipike_proposal* end_prop = IP_NULL; ip_assert(sa->proposals != IP_NULL); if (sa->selected_proposal >= 0) { /* TODO is this logic valid ? it assumes proposals with same number are consecutive */ i = sa->selected_proposal; end_prop = sa->proposals[i + ipike_sa_selected_proposal_count(sa)]; } for (; sa->proposals[i] != end_prop; i++) { if (IPIKE_SUCCESS != (err = ipike_proposal_create(sa->proposals[i], &proposal))) goto cleanup; if (IPIKE_SUCCESS != (err = ipike_array_add((void***) &proposals, proposal))) { ipcom_free(proposal); goto cleanup; } } if (IP_NULL == (*payload = ipike_sa_new(sa->doi, sa->situation, proposals))) { IP_PANIC(); err = IPIKE_OUT_OF_MEMORY; } cleanup: ipike_array_free((void**) proposals, ipcom_free); /*lint !e123 */ return err; } /* *=========================================================================== * ipike_sa_ike_initialize *=========================================================================== */ IP_GLOBAL Ipike_error ipike_sa_ike_initialize( Ipike_config_ike_sa* conf, Ipike_sa* sa) { Ipike_error err = IPIKE_SUCCESS; Ipike_proposal* p = IP_NULL; if ((IP_NULL == conf->proposals) || (IP_NULL == conf->proposals[0])) { err = IPIKE_SA_NO_VALID_TRANSFORM_CONFIGURED; } else { /* We only support a single proposal with IKE v1, each configuration * proposal is converted to an IKE v1 transform and added to one * single IKE v1 proposal. */ if (IP_NULL == (p = ipcom_calloc(1, sizeof(*p)))) { IP_PANIC(); err = IPIKE_OUT_OF_MEMORY; } if (IPIKE_SUCCESS == err) err = ipike_proposal_ike_initialize(conf, conf->proposals, (Ip_u8)(1), p); if (IPIKE_SUCCESS == err) err = ipike_array_add((void***) &sa->proposals, p); if (IPIKE_SUCCESS != err) ipike_proposal_free(p); } if (IPIKE_SUCCESS == err) { sa->doi = IPIKE_DOI_IPSEC; sa->situation.isakmp = IPIKE_SIT_IDENTITY_ONLY; sa->selected_proposal = -1; sa->selected_proposal2 = -1; sa->proposal_count = 0; } else { IPCOM_LOG0(ERR, "ipike_sa_ike_initialize: failed to intialize IKE SA"); } return err; } /* *=========================================================================== * ipike_sa_ipsec_initialize *=========================================================================== */ IP_GLOBAL Ipike_error ipike_sa_ipsec_initialize( Ipike_config_ipsec_sa* conf, Ipike_sa* sa) { Ipike_error err = IPIKE_SUCCESS; Ipike_proposal* p = IP_NULL; Ip_u8 i = 0; Ip_u8 num = 0; if ((IP_NULL == conf->proposals) || (IP_NULL == conf->proposals[0])) { err = IPIKE_SA_NO_VALID_TRANSFORM_CONFIGURED; } else { for (i=0; (IP_NULL != conf->proposals[i]); i++) { if (IP_NULL == (p = ipcom_calloc(1, sizeof(*p)))) { IP_PANIC(); err = IPIKE_OUT_OF_MEMORY; } if (IPIKE_SUCCESS == err) { if (conf->proposals[i]->number != 0) num = (Ip_u8)conf->proposals[i]->number; else num = (Ip_u8)(i+1); err = ipike_proposal_ipsec_initialize(conf, conf->proposals[i], num, p); } if (IPIKE_SUCCESS == err) err = ipike_array_add((void***) &sa->proposals, p); if (IPIKE_SUCCESS != err) { ipike_proposal_free(p); break; } } } if (IPIKE_SUCCESS == err) { sa->doi = IPIKE_DOI_IPSEC; sa->situation.isakmp = IPIKE_SIT_IDENTITY_ONLY; sa->selected_proposal = -1; sa->selected_proposal2 = -1; sa->proposal_count = i; } else { IPCOM_LOG0(ERR, "ipike_sa_ipsec_initialize: failed to intialize IPSec SA"); } return err; } /* *=========================================================================== * ipike_sa_ipsec_set_behind_nat *=========================================================================== */ IP_GLOBAL void ipike_sa_ipsec_set_behind_nat(Ipike_sa* sa) { int i; int j; /* Change encapsulation mode: * tunnel > udp_tunnel * transport > udp_transport */ for (i=0; i < sa->proposal_count; i++) for (j=0; sa->proposals[i]->transforms[j] != IP_NULL; j++) if (sa->proposals[i]->transforms[j]->ipsec.encapsulation_mode == IPIKE_IPSEC_ENCAPSULATION_MODE_TUNNEL) sa->proposals[i]->transforms[j]->ipsec.encapsulation_mode = IPIKE_IPSEC_ENCAPSULATION_MODE_UDP_TUNNEL; else if (sa->proposals[i]->transforms[j]->ipsec.encapsulation_mode == IPIKE_IPSEC_ENCAPSULATION_MODE_TRANSPORT) { sa->proposals[i]->transforms[j]->ipsec.encapsulation_mode = IPIKE_IPSEC_ENCAPSULATION_MODE_UDP_TRANSPORT; sa->send_original_address = IP_TRUE; } return; } /* *=========================================================================== * ipike_sa_check_payload *=========================================================================== */ IP_GLOBAL Ipike_error ipike_sa_check_payload(Ipike_sa_payload* sa, Ip_bool phase1) { Ipike_proposal_payload* proposal; Ip_u32 proposal_payload_max_len; Ip_s32 chain_length; unsigned int next_payload; int i = 0; if ((sa->header.next_payload == IPIKE_PAYLOAD_PROPOSAL) || (sa->header.next_payload == IPIKE_PAYLOAD_TRANSFORM)) { return IPIKE_WRONG_PAYLOAD; } /* Check DOI field */ if ( ip_ntohl(sa->doi) != IPIKE_DOI_ISAKMP && ip_ntohl(sa->doi) != IPIKE_DOI_IPSEC ) { IPCOM_LOG1(ERR, "ipike_sa_check_payload() :: DOI not supported (%d)", sa->doi); return IPIKE_DOI_UNSUPPORTED; } /* Check situation field */ if ( sa->situation.ip_sec != IPIKE_SIT_IDENTITY_ONLY ) { IPCOM_LOG1(ERR, "ipike_sa_check_payload() :: "\ "DOI situation not supported (0x%.2x)", sa->situation.ip_sec); return IPIKE_SITUATION_UNSUPPORTED; } proposal_payload_max_len = ipike_sa_proposal_length(sa); proposal = ipike_sa_proposal_data(sa); /* Check the length of the proposal payload chain */ chain_length = ipike_payload_chain_size( &proposal->header, proposal_payload_max_len); if (chain_length <= 0) { IPCOM_LOG0(ERR, "the proposal payloads extends beyond the end of the SA payload size"); return IPIKE_ISAKMP_PAYLOAD_LENGTH_ERROR; } if (chain_length != (Ip_s32) proposal_payload_max_len) { IPCOM_LOG0(ERR, "the propsal payload is not as long as indicated by the SA payload header"); return IPIKE_ISAKMP_PAYLOAD_LENGTH_ERROR; } /* Save "next payload" so we can determine if the payload is supported */ next_payload = proposal->header.next_payload; for (;IP_NULL != proposal; proposal = ipike_sa_next_payload(proposal)) { Ipike_error err; if(proposal->header.flags) { return IPIKE_ISAKMP_INVALID_PAYLOAD; } if((phase1 == IP_TRUE) && (i != 0))/* WIND00397389 */ { return IPIKE_ISAKMP_INVALID_PAYLOAD; } /* Ignore unsupported payload */ if ( IPIKE_PAYLOAD_NONE != next_payload && IPIKE_PAYLOAD_PROPOSAL != next_payload ) { IPCOM_LOG3(WARNING, "ipike_check_proposal_payload: ignoring unsupported payload (%u) in field in " "the proposal payload, only %d and %d are supported", next_payload, IPIKE_PAYLOAD_NONE, IPIKE_PAYLOAD_PROPOSAL); return IPIKE_ISAKMP_INVALID_PAYLOAD_PROPOSAL; } if (IPIKE_SUCCESS != (err = ipike_proposal_check_payload(proposal,phase1))) return err; next_payload = proposal->header.next_payload; i++; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_sa_extract *=========================================================================== */ IP_GLOBAL Ipike_error ipike_sa_extract( Ipike_isakmp_exchange* ie, Ipike_sa_payload* payload, Ipike_sa* sa) { Ipike_error err; sa->selected_proposal = -1; /* No selected proposal */ sa->selected_proposal2 = -1; /* For ESP + AH */ sa->doi = ip_ntohl(payload->doi); sa->situation.ip_sec = payload->situation.ip_sec; switch ( sa->doi ) { case IPIKE_DOI_IPSEC: err = ipike_sa_process_message_ipsec(ie, payload, sa); break; default: return IPIKE_DOI_UNSUPPORTED; } return err; } /* *=========================================================================== * ipike_sa_selected_proposal_count *=========================================================================== */ IP_GLOBAL int ipike_sa_selected_proposal_count(Ipike_sa* sa) { int count = 0; Ip_u8 p_number; if (sa->proposals == IP_NULL) return 0; if (sa->selected_proposal < 0) return -1; p_number = sa->proposals[sa->selected_proposal]->number; while (sa->proposals[sa->selected_proposal + count] != IP_NULL && p_number == sa->proposals[sa->selected_proposal + count]->number) count++; return count; } /* *=========================================================================== * ipike_sa_selected_proposal_get *=========================================================================== */ IP_GLOBAL Ipike_proposal * ipike_sa_selected_proposal_get(Ipike_sa* sa) { if (sa->proposals == IP_NULL) return IP_NULL; if (sa->selected_proposal < 0) return IP_NULL; return sa->proposals[sa->selected_proposal]; } /* *=========================================================================== * ipike_sa_selected_transform_get *=========================================================================== */ IP_GLOBAL Ipike_transform * ipike_sa_selected_transform_get(Ipike_sa* sa) { Ipike_proposal * proposal; proposal = ipike_sa_selected_proposal_get(sa); if (proposal == IP_NULL) return IP_NULL; if (proposal->transforms == IP_NULL) return IP_NULL; if (proposal->selected_transform < 0) return IP_NULL; return proposal->transforms[proposal->selected_transform]; } /* *=========================================================================== * ipike_sa_contains_transform_transport_nat *=========================================================================== */ IP_GLOBAL Ip_bool ipike_sa_contains_transform_transport_nat(Ipike_sa * sa) { int i; int j; for (i=0; i < sa->proposal_count; i++) { for (j=0; sa->proposals[i]->transforms[j] != IP_NULL; j++) { if (sa->proposals[i]->transforms[j]->ipsec.encapsulation_mode == IPIKE_IPSEC_ENCAPSULATION_MODE_UDP_TRANSPORT) { return IP_TRUE; } } } return IP_FALSE; } /* *=========================================================================== * ipike_sa_ike_print_payload *=========================================================================== */ IP_GLOBAL void ipike_sa_ike_print_payload(Ipike_sa_payload* sa) { ipike_sa_print_payload(sa, ipike_proposal_ike_print_payload, IP_TRUE); } /* *=========================================================================== * ipike_sa_ipsec_print_payload *=========================================================================== */ IP_GLOBAL void ipike_sa_ipsec_print_payload(Ipike_sa_payload* sa) { ipike_sa_print_payload(sa, ipike_proposal_ipsec_print_payload, IP_FALSE); } /* **************************************************************************** * END OF FILE **************************************************************************** */