/* * Copyright (c) 2017-2019, Pelion and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "nsconfig.h" #include "ns_types.h" #include "eventOS_event.h" #include "ns_trace.h" #include "string.h" #include "Core/include/ns_socket.h" #include "NWK_INTERFACE/Include/protocol.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan.h" #include "6LoWPAN/Bootstraps/protocol_6lowpan_bootstrap.h" #ifdef ECC #include "libX509_V3.h" #include "ecc.h" #endif #include "Security/Common/sec_lib.h" #include "net_nvm_api.h" #include "Security/PANA/pana.h" #include "common_functions.h" #include "Security/PANA/pana_eap_header.h" #ifdef PANA #define TRACE_GROUP "eap" const uint8_t EAP_ANYMOUS[9] = {'a', 'n', 'o', 'n', 'y', 'm', 'o', 'u', 's'}; static bool force_frag_last_retry = false; static bool force_frag_start_fail = false; static bool force_frag_timeout = false; static void eap_seq_back_to_accept(sec_suite_t *suite) { if (suite->pana_session.eap_id_seq == 0) { suite->pana_session.eap_id_seq = 0xff; } else { suite->pana_session.eap_id_seq--; } } void pana_eap_fragmetation_start_filter(bool state) { tr_debug("Set start state %u", state); force_frag_start_fail = state; } void pana_eap_fragmetation_force_timeout(bool state) { force_frag_timeout = state; } void pana_eap_fragmetation_force_retry(bool state) { force_frag_last_retry = state; } static buffer_t *eap_common_headroom_get_to_buffer(buffer_t *buf, uint16_t header_size) { if ((buf = buffer_headroom(buf, header_size)) == 0) { return NULL; } buffer_data_reserve_header(buf, header_size); return buf; } void pana_eap_identity_build(buffer_t *buf, sec_suite_t *suite) { uint8_t *ptr; uint8_t eap_code; buf->buf_ptr = 0; const uint8_t *nonce_ptr; if (suite->pana_session.user_server) { buf->buf_end = 0; eap_code = EAP_REQ; nonce_ptr = suite->pana_session.pana_heap->agent_nonce; } else { buf->buf_end = 9; ptr = buf->buf; memcpy(ptr, EAP_ANYMOUS, 9); eap_code = EAP_RESPONSE; nonce_ptr = suite->pana_session.pana_heap->client_nonce; } buf = eap_common_headroom_get_to_buffer(buf, eap_header_size(eap_code)); if (!buf) { return; } eap_header_build(buffer_data_pointer(buf), buffer_data_length(buf), eap_code, suite->pana_session.eap_id_seq, EAP_IDENTITY); pana_eap_payload_down(buf, nonce_ptr, suite); tr_warn("TX EAP Identity!"); } void pana_eap_tls_start_build(buffer_t *buf, sec_suite_t *suite) { buf->buf_ptr = 0; buf->buf_end = 0; if (suite->tls_session->tls_heap) { tls_heap_t *tls_heap = suite->tls_session->tls_heap; tls_heap->tls_handshake_h_len = 0; } uint8_t header_size = eap_header_size(EAP_REQ); header_size += eap_tls_header_size(EAP_TLS_START); buf = eap_common_headroom_get_to_buffer(buf, header_size); if (!buf) { return; } uint8_t *ptr = eap_header_build(buffer_data_pointer(buf), buffer_data_length(buf), EAP_REQ, suite->pana_session.eap_id_seq, EAP_TLS); eap_tls_header_build(ptr, EAP_TLS_START, 0); pana_eap_payload_down(buf, NULL, suite); } void pana_eap_tls_finnish_build(buffer_t *buf, sec_suite_t *suite) { buffer_data_clear(buf); uint8_t eap_code; if (suite->state == TLS_ALERT) { eap_code = EAP_FAILURE; } else { eap_code = EAP_RESPONSE; } uint8_t header_size = eap_header_size(eap_code); header_size += eap_tls_header_size(0); buf = eap_common_headroom_get_to_buffer(buf, header_size); if (!buf) { return; } uint8_t *ptr = eap_header_build(buffer_data_pointer(buf), buffer_data_length(buf), eap_code, suite->pana_session.eap_id_seq, EAP_TLS); eap_tls_header_build(ptr, 0, 0); pana_eap_payload_down(buf, NULL, suite); } bool pana_eap_frag_re_tx(sec_suite_t *suite) { buffer_t *f_buf; if (suite->pana_session.eap_assy_buf) { buffer_t *buf = 0; f_buf = buffer_get(suite->pana_session.last_assy_size + 50); buf = suite->pana_session.eap_assy_buf; if (f_buf) { memcpy(buffer_data_pointer(f_buf), buffer_data_pointer(buf) + suite->pana_session.assy_off_set, suite->pana_session.last_assy_size); buffer_data_length_set(f_buf, suite->pana_session.last_assy_size); goto success_push; } } else if (suite->pana_session.eap_frag_buf || suite->pana_session.packet_delivered) { f_buf = buffer_get(127); if (f_buf) { //tr_debug("EAP assembly re REQ timer"); f_buf->buf_ptr = f_buf->buf_end; goto success_push; } } return false; success_push: f_buf->interface = suite->interface; memcpy(f_buf->dst_sa.address, suite->session_address, 16); f_buf->src_sa.addr_type = ADDR_NONE; pana_eap_down(f_buf, suite); return true; } buffer_t *eap_down(buffer_t *buf, sec_suite_t *suite) { //tr_debug("EAP Down"); uint16_t tls_payload_len = 0; uint8_t eap_code; uint8_t eap_tls_flags = 0; tls_payload_len = buffer_data_length(buf); if (tls_payload_len > EAP_MTU_SIZE) { //Check Fragmentation if (suite->pana_session.assy_length) { tr_debug("Already Fragmentation"); buffer_free(buf); buf = (buffer_t *)0; return buf; } else { pana_lib_parameters_s *parameters = pana_parameters_get(); buffer_t *f_buf = buffer_get(parameters->EAP_FRAGMENT_SIZE + 50); buf->seq = suite->pana_session.eap_id_seq; suite->pana_session.eap_assy_buf = buf; suite->pana_session.assy_length = tls_payload_len; suite->pana_session.assy_off_set = 0; suite->pana_session.last_assy_size = parameters->EAP_FRAGMENT_SIZE; if (suite->pana_session.user_server) { //tr_debug("Set Re Tx Timer"); pana_timeout_timer_set(suite, suite->state); } else { sec_set_auth_timeout(suite, TLS_KEY_CHANGE); } if (f_buf) { f_buf->interface = suite->interface; eap_tls_flags = EAP_TLS_FRAGMENT_LENGTH | EAP_TLS_MORE_FRAGMENTS; memcpy(buffer_data_pointer(f_buf), buffer_data_pointer(buf), parameters->EAP_FRAGMENT_SIZE); memcpy(f_buf->dst_sa.address, buf->src_sa.address, 16); buf->src_sa.addr_type = ADDR_NONE; f_buf->src_sa.addr_type = ADDR_NONE; buffer_data_length_set(f_buf, parameters->EAP_FRAGMENT_SIZE); buf = f_buf; } else { return NULL; } } } else { if (suite->pana_session.assy_length) { if (suite->pana_session.assy_length > (suite->pana_session.assy_off_set + suite->pana_session.last_assy_size)) { eap_tls_flags = EAP_TLS_MORE_FRAGMENTS; if (suite->pana_session.assy_off_set == 0) { eap_tls_flags |= EAP_TLS_FRAGMENT_LENGTH; //tr_debug("Retry First"); } } } } if (suite->pana_session.user_server) { eap_code = EAP_REQ; } else { eap_code = EAP_RESPONSE; } uint8_t header_size = eap_header_size(eap_code); header_size += eap_tls_header_size(eap_tls_flags); buf = eap_common_headroom_get_to_buffer(buf, header_size); if (!buf) { return NULL; } uint8_t *ptr = eap_header_build(buffer_data_pointer(buf), buffer_data_length(buf), eap_code, suite->pana_session.eap_id_seq, EAP_TLS); eap_tls_header_build(ptr, eap_tls_flags, suite->pana_session.assy_length); return (buf); } buffer_t *eap_up(buffer_t *buf, sec_suite_t *suite) { eap_header_t header; uint8_t *ptr = buffer_data_pointer(buf); uint16_t payload_length = buffer_data_length(buf); uint8_t response_counter = suite->retry_counter; if (!eap_header_parse(ptr, payload_length, &header)) { return buffer_free(buf); } if (header.eap_code == EAP_RESPONSE) { if (header.id_seq != suite->pana_session.eap_id_seq) { tr_warn("EAP:Drop Packet by ID"); suite->timer = 65; return buffer_free(buf); } //tr_debug("EAP RES"); suite->pana_session.eap_id_seq++; suite->retry_counter = 0; } else if (header.eap_code == EAP_REQ) { if (header.type == EAP_IDENTITY) { suite->pana_session.eap_id_seq = header.id_seq; } else { uint8_t eap_seq = suite->pana_session.eap_id_seq; if (eap_seq == 0xff) { eap_seq = 0; } else { eap_seq++; } if (eap_seq != header.id_seq) { if (suite->pana_session.eap_id_seq == header.id_seq) { //tr_debug("EAP:Same SEQ"); if (suite->pana_session.assy_length) { tr_debug("SEND ACK. Should send same packet again"); } else if (suite->pana_session.frag_length) { tr_debug("SEND ACK. Should send same packet again"); buf->buf_ptr = buf->buf_end; buf = buffer_turnaround(buf); memcpy(buf->dst_sa.address, buf->src_sa.address, 16); buf->src_sa.addr_type = ADDR_NONE; return buf; } else { tr_debug("REQ but Frag ready"); return buffer_free(buf); } } else { tr_debug("EAP:Drop unknown Req ID. MSG: %x, ses: %x", header.id_seq, suite->pana_session.eap_id_seq); return buffer_free(buf); } } else { suite->pana_session.eap_id_seq = header.id_seq; } } } switch (header.type) { case EAP_IDENTITY: if (header.eap_code == EAP_REQ) { sec_lib_state_machine_trig(suite, EAP_IDENTITY_RES); } else { #ifdef PANA_SERVER_API if (suite->pana_session.user_server) { sec_lib_state_machine_trig(suite, TLS_START); } #endif } return buffer_free(buf); case EAP_TLS: case EAP_TTLS: { eap_tls_header_t eap_tls_header; if (!eap_tls_header_parse(header.data_ptr, header.length - 5, &eap_tls_header)) { return buffer_free(buf); } if (eap_tls_header.eap_tls_flags & EAP_TLS_START) { suite->pana_session.eap_id_seq = header.id_seq; sec_lib_state_machine_trig(suite, TLS_INIT); return buffer_free(buf); } buffer_data_pointer_set(buf, eap_tls_header.data_ptr); if (header.eap_code == EAP_RESPONSE) { if (eap_tls_header.eap_tls_flags) { if (suite->pana_session.assy_length) { suite->pana_session.assy_off_set += suite->pana_session.last_assy_size; if (suite->pana_session.assy_off_set < suite->pana_session.assy_length) { tr_warn("Packet TX process fail"); } else { tr_debug("EAP Frag TX Done & Start RX frag"); } //tr_debug("Free Frag Buf"); if (suite->pana_session.eap_assy_buf) { tr_debug("Free Frag Buf"); buffer_free(suite->pana_session.eap_assy_buf); suite->pana_session.eap_assy_buf = NULL; } suite->pana_session.assy_length = 0; suite->pana_session.assy_off_set = 0; suite->pana_session.last_assy_size = 0; suite->pana_session.packet_delivered = true; suite->retry_counter = 0; } } } if ((eap_tls_header.eap_tls_flags & EAP_TLS_MORE_FRAGMENTS) == 0) { if (suite->pana_session.frag_length) { if (force_frag_last_retry || force_frag_timeout) { force_frag_last_retry = false; if (header.eap_code == EAP_RESPONSE) { suite->retry_counter = response_counter; } eap_seq_back_to_accept(suite); return buffer_free(buf); } buffer_t *t_buf = suite->pana_session.eap_frag_buf; uint16_t check_len = suite->pana_session.frag_off_set; check_len += eap_tls_header.tls_frame_length; suite->pana_session.eap_frag_buf = NULL; suite->pana_session.frag_length = 0; if (check_len == buffer_data_length(t_buf)) { memcpy(buffer_data_pointer(t_buf) + suite->pana_session.frag_off_set, eap_tls_header.data_ptr, eap_tls_header.tls_frame_length); t_buf->seq = header.id_seq; tr_debug("Full packet RX"); buffer_free(buf); buf = t_buf; suite->pana_session.frag_off_set = 0; } else { buffer_free(t_buf); suite->pana_session.frag_off_set = 0; sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL); return buffer_free(buf); } } else if (suite->pana_session.assy_length) { buffer_t *t_buf = suite->pana_session.eap_assy_buf; if (header.eap_code == EAP_RESPONSE) { suite->pana_session.assy_off_set += suite->pana_session.last_assy_size; if (eap_tls_header.tls_frame_length == 0) { t_buf->seq = header.id_seq; } } else { uint8_t cmp_seq = t_buf->seq; if (cmp_seq == 0xff) { cmp_seq = 0; } else { cmp_seq++; } if (cmp_seq == header.id_seq) { suite->pana_session.assy_off_set += suite->pana_session.last_assy_size; if (eap_tls_header.tls_frame_length == 0) { t_buf->seq = header.id_seq; } } else { tr_debug("RETX"); } } if (suite->pana_session.assy_off_set < suite->pana_session.assy_length) { if (eap_tls_header.tls_frame_length == 0) { uint16_t len = 0; buffer_t *f_buf = 0; buffer_free(buf); suite->retry_counter = 0; pana_lib_parameters_s *parameters = pana_parameters_get(); if (suite->pana_session.assy_off_set + parameters->EAP_FRAGMENT_SIZE < suite->pana_session.assy_length) { len = parameters->EAP_FRAGMENT_SIZE; } else { len = suite->pana_session.assy_length - suite->pana_session.assy_off_set; } f_buf = buffer_get(len + 50); if (f_buf) { //memcpy(f_buf->dst_sa.address, t_buf->src_sa.address, 16); f_buf->interface = suite->interface; f_buf->src_sa.addr_type = ADDR_NONE; f_buf->dst_sa.addr_type = ADDR_NONE; memcpy(buffer_data_pointer(f_buf), buffer_data_pointer(t_buf) + suite->pana_session.assy_off_set, len); buffer_data_length_set(f_buf, len); suite->pana_session.last_assy_size = len; tr_debug("Push frag"); return (f_buf); } else { tr_warn("No Mem"); return NULL; } } else { //IT cuold be alert buf->session_ptr = suite; eap_tls_payload_push(buf); return NULL; } } else { tr_debug("EAP Frag TX Done"); suite->pana_session.eap_assy_buf = buffer_free(t_buf); suite->pana_session.assy_length = 0; suite->pana_session.assy_off_set = 0; suite->pana_session.last_assy_size = 0; } } else if (!eap_tls_header.tls_frame_length) { if (header.eap_code == EAP_RESPONSE) { #ifdef PANA_SERVER_API if (suite->state == TLS_KEY_CHANGE && suite->pana_session.user_server) { //tr_debug("TLS Auth Ready"); pana_key_calculation(suite); sec_lib_state_machine_trig(suite, TLS_EAP_END_PANA_VERIFY); } #endif } return buffer_free(buf); } buf->session_ptr = suite; eap_tls_payload_push(buf); return NULL; } //More flag is active if (!eap_tls_header.tls_frame_length) { tr_debug("More without Data"); return buffer_free(buf); } bool skip_packet = false; if (eap_tls_header.tls_length) { //Check did we have a already action if (suite->pana_session.frag_length == 0) { buffer_t *f_buf = NULL; if (force_frag_start_fail) { tr_debug("Force to drop fragment"); force_frag_start_fail = false; } else { tr_debug("First Fragment"); f_buf = buffer_get(eap_tls_header.tls_length); } if (f_buf) { buffer_data_length_set(f_buf, eap_tls_header.tls_length); memcpy(buffer_data_pointer(f_buf), eap_tls_header.data_ptr, eap_tls_header.tls_frame_length); suite->pana_session.frag_off_set = eap_tls_header.tls_frame_length; f_buf->seq = header.id_seq; suite->pana_session.eap_frag_buf = f_buf; suite->pana_session.frag_length = eap_tls_header.tls_length; skip_packet = true; } else { tr_debug("No free men for Fragment: %i", eap_tls_header.tls_length); sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL); return buffer_free(buf); } } else if (suite->pana_session.frag_length != eap_tls_header.tls_length) { tr_debug("Fragment started already"); skip_packet = true; } else { } } if (!skip_packet && suite->pana_session.frag_length) { uint16_t check_len = 0; buffer_t *t_buf = suite->pana_session.eap_frag_buf; check_len = suite->pana_session.frag_off_set; check_len += eap_tls_header.tls_frame_length; //new sequency if (check_len < buffer_data_length(t_buf)) { tr_debug("Copy Data to Fragment"); memcpy(buffer_data_pointer(t_buf) + suite->pana_session.frag_off_set, eap_tls_header.data_ptr, eap_tls_header.tls_frame_length); suite->pana_session.frag_off_set += eap_tls_header.tls_frame_length; t_buf->seq = suite->pana_session.eap_id_seq; } else { tr_debug("Overflow possible Free Current entry and set Alert"); buffer_free(t_buf); suite->pana_session.eap_frag_buf = NULL; suite->pana_session.frag_length = 0; suite->pana_session.frag_off_set = 0; sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL); return buffer_free(buf); } } if (header.eap_code == EAP_RESPONSE) { #ifdef PANA_SERVER_API if (suite->state == TLS_TX_SERVER_KEY_EXCHANGE) { if (suite->pana_session.assy_length) { suite->retry_counter = 0; } } #endif pana_timeout_timer_set(suite, suite->state); } /* TX ACK or new request to sender*/ buf = buffer_turnaround(buf); buf->buf_ptr = buf->buf_end; memcpy(buf->dst_sa.address, buf->src_sa.address, 16); buf->src_sa.addr_type = ADDR_NONE; return (buf); } default: tr_debug("%x", header.type); return buffer_free(buf); } } #else void pana_eap_fragmetation_start_filter(bool state) { (void) state; } void pana_eap_fragmetation_force_timeout(bool state) { (void) state; } void pana_eap_fragmetation_force_retry(bool state) { (void) state; } #endif