mbed-os/connectivity/nanostack/sal-stack-nanostack/source/6LoWPAN/ws/ws_pae_lib.c

512 lines
14 KiB
C

/*
* Copyright (c) 2018-2019, Arm Limited 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 <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "fhss_config.h"
#include "ws_management_api.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/ws/ws_cfg_settings.h"
#include "6LoWPAN/ws/ws_config.h"
#include "Security/protocols/sec_prot_cfg.h"
#include "6LoWPAN/ws/ws_pae_timers.h"
#include "Security/kmp/kmp_addr.h"
#include "Security/kmp/kmp_api.h"
#include "Security/protocols/sec_prot_certs.h"
#include "Security/protocols/sec_prot_keys.h"
#include "6LoWPAN/ws/ws_pae_lib.h"
#include "6LoWPAN/ws/ws_pae_key_storage.h"
#ifdef HAVE_WS
#define TRACE_GROUP "wspl"
void ws_pae_lib_kmp_list_init(kmp_list_t *kmp_list)
{
ns_list_init(kmp_list);
}
kmp_entry_t *ws_pae_lib_kmp_list_add(kmp_list_t *kmp_list, kmp_api_t *kmp)
{
// Already in list
kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(kmp_list, kmp);
if (entry) {
return entry;
}
entry = ns_dyn_mem_alloc(sizeof(kmp_entry_t));
if (!entry) {
return NULL;
}
entry->kmp = kmp;
entry->timer_running = false;
ns_list_add_to_end(kmp_list, entry);
return entry;
}
int8_t ws_pae_lib_kmp_list_delete(kmp_list_t *kmp_list, kmp_api_t *kmp)
{
kmp_entry_t *entry = ws_pae_lib_kmp_list_entry_get(kmp_list, kmp);
if (entry) {
ns_list_remove(kmp_list, entry);
kmp_api_delete(entry->kmp);
ns_dyn_mem_free(entry);
return 0;
}
return -1;
}
kmp_api_t *ws_pae_lib_kmp_list_type_get(kmp_list_t *kmp_list, kmp_type_e type)
{
kmp_api_t *kmp = NULL;
ns_list_foreach(kmp_entry_t, cur, kmp_list) {
// If kmp type matches
if (kmp_api_type_get(cur->kmp) == type) {
/* If receiving of messages has not been disabled for the kmp (kmp is not
in terminating phase) prioritizes that kmp */
if (!kmp_api_receive_disable(cur->kmp)) {
return cur->kmp;
}
// Otherwise returns any kmp that matches
kmp = cur->kmp;
}
}
return kmp;
}
kmp_api_t *ws_pae_lib_kmp_list_instance_id_get(kmp_list_t *kmp_list, uint8_t instance_id)
{
ns_list_foreach(kmp_entry_t, cur, kmp_list) {
if (kmp_api_instance_id_get(cur->kmp) == instance_id) {
return cur->kmp;
}
}
return NULL;
}
void ws_pae_lib_kmp_list_free(kmp_list_t *kmp_list)
{
ns_list_foreach_safe(kmp_entry_t, cur, kmp_list) {
kmp_api_delete(cur->kmp);
ns_dyn_mem_free(cur);
}
}
kmp_entry_t *ws_pae_lib_kmp_list_entry_get(kmp_list_t *kmp_list, kmp_api_t *kmp)
{
ns_list_foreach(kmp_entry_t, cur, kmp_list) {
if (cur->kmp == kmp) {
return cur;
}
}
return 0;
}
bool ws_pae_lib_kmp_list_empty(kmp_list_t *kmp_list)
{
return ns_list_is_empty(kmp_list);
}
uint8_t ws_pae_lib_kmp_list_count(kmp_list_t *kmp_list)
{
return ns_list_count(kmp_list);
}
void ws_pae_lib_kmp_timer_start(kmp_list_t *kmp_list, kmp_entry_t *entry)
{
if (ns_list_get_first(kmp_list) != entry) {
ns_list_remove(kmp_list, entry);
ns_list_add_to_start(kmp_list, entry);
}
entry->timer_running = true;
}
void ws_pae_lib_kmp_timer_stop(kmp_list_t *kmp_list, kmp_entry_t *entry)
{
if (ns_list_get_last(kmp_list) != entry) {
ns_list_remove(kmp_list, entry);
ns_list_add_to_end(kmp_list, entry);
}
entry->timer_running = false;
}
bool ws_pae_lib_kmp_timer_update(kmp_list_t *kmp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout)
{
if (ns_list_is_empty(kmp_list)) {
return false;
}
bool timer_running = false;
ns_list_foreach_safe(kmp_entry_t, entry, kmp_list) {
if (entry->timer_running) {
timeout(entry->kmp, ticks);
timer_running = true;
} else {
break;
}
}
return timer_running;
}
void ws_pae_lib_supp_list_init(supp_list_t *supp_list)
{
ns_list_init(supp_list);
}
supp_entry_t *ws_pae_lib_supp_list_add(supp_list_t *supp_list, const kmp_addr_t *addr)
{
supp_entry_t *entry = ns_dyn_mem_temporary_alloc(sizeof(supp_entry_t));
if (!entry) {
return NULL;
}
ws_pae_lib_supp_init(entry);
memset(&entry->addr, 0, sizeof(kmp_addr_t));
entry->addr.type = KMP_ADDR_EUI_64_AND_IP;
kmp_address_copy(&entry->addr, addr);
ns_list_add_to_end(supp_list, entry);
return entry;
}
int8_t ws_pae_lib_supp_list_remove(supp_list_t *supp_list, supp_entry_t *supp)
{
ns_list_remove(supp_list, supp);
ws_pae_lib_supp_delete(supp);
ns_dyn_mem_free(supp);
return 0;
}
supp_entry_t *ws_pae_lib_supp_list_entry_eui_64_get(const supp_list_t *supp_list, const uint8_t *eui_64)
{
ns_list_foreach(supp_entry_t, cur, supp_list) {
if (memcmp(cur->addr.eui_64, eui_64, 8) == 0) {
return cur;
}
}
return 0;
}
void ws_pae_lib_supp_list_delete(supp_list_t *supp_list)
{
ns_list_foreach_safe(supp_entry_t, entry, supp_list) {
ws_pae_lib_supp_list_remove(supp_list, entry);
}
}
bool ws_pae_lib_supp_list_timer_update(void *instance, supp_list_t *active_supp_list, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout)
{
bool timer_running = false;
ns_list_foreach_safe(supp_entry_t, entry, active_supp_list) {
bool running = ws_pae_lib_supp_timer_update(instance, entry, ticks, timeout);
if (running) {
timer_running = true;
} else {
ws_pae_lib_supp_list_to_inactive(instance, active_supp_list, entry);
}
}
return timer_running;
}
void ws_pae_lib_supp_list_slow_timer_update(supp_list_t *supp_list, uint16_t seconds)
{
ns_list_foreach(supp_entry_t, entry, supp_list) {
if (sec_prot_keys_pmk_lifetime_decrement(&entry->sec_keys, seconds)) {
tr_info("PMK and PTK expired, eui-64: %s, system time: %"PRIu32"", trace_array(entry->addr.eui_64, 8), protocol_core_monotonic_time / 10);
}
if (sec_prot_keys_ptk_lifetime_decrement(&entry->sec_keys, seconds)) {
tr_info("PTK expired, eui-64: %s, system time: %"PRIu32"", trace_array(entry->addr.eui_64, 8), protocol_core_monotonic_time / 10);
}
}
}
void ws_pae_lib_supp_init(supp_entry_t *entry)
{
ws_pae_lib_kmp_list_init(&entry->kmp_list);
memset(&entry->addr, 0, sizeof(kmp_addr_t));
memset(&entry->sec_keys, 0, sizeof(sec_prot_keys_t));
entry->ticks = 0;
entry->retry_ticks = 0;
entry->store_ticks = ws_pae_key_storage_storing_interval_get() * 1000;
entry->active = true;
entry->access_revoked = false;
}
void ws_pae_lib_supp_delete(supp_entry_t *entry)
{
ws_pae_lib_kmp_list_free(&entry->kmp_list);
}
bool ws_pae_lib_supp_timer_update(void *instance, supp_entry_t *entry, uint16_t ticks, ws_pae_lib_kmp_timer_timeout timeout)
{
// Updates KMP timers and calls timeout callback
bool keep_timer_running = ws_pae_lib_kmp_timer_update(&entry->kmp_list, ticks, timeout);
// If KMPs are not active updates supplicant timer
if (!keep_timer_running) {
if (entry->ticks > ticks) {
keep_timer_running = true;
entry->ticks -= ticks;
} else {
entry->ticks = 0;
entry->retry_ticks = 0;
}
}
// Updates retry timer
if (entry->retry_ticks > ticks) {
entry->retry_ticks -= ticks;
} else {
if (entry->retry_ticks > 0) {
tr_info("EAP-TLS max ongoing delay timeout eui-64: %s", trace_array(entry->addr.eui_64, 8));
}
entry->retry_ticks = 0;
}
if (!instance) {
return keep_timer_running;
}
// Updates retry timer
if (entry->store_ticks > ticks) {
entry->store_ticks -= ticks;
} else {
tr_info("PAE active entry key storage update timeout");
ws_pae_key_storage_supp_write(instance, entry);
entry->store_ticks = ws_pae_key_storage_storing_interval_get() * 1000;
}
return keep_timer_running;
}
void ws_pae_lib_supp_timer_ticks_set(supp_entry_t *entry, uint32_t ticks)
{
entry->ticks = ticks;
}
void ws_pae_lib_supp_timer_ticks_add(supp_entry_t *entry, uint32_t ticks)
{
entry->ticks += ticks;
}
bool ws_pae_lib_supp_timer_is_running(supp_entry_t *entry)
{
if (entry->ticks == 0) {
return false;
}
return true;
}
void ws_pae_lib_supp_list_to_active(supp_list_t *active_supp_list, supp_list_t *inactive_supp_list, supp_entry_t *entry)
{
if (entry->active) {
return;
}
tr_debug("PAE: to active, eui-64: %s", trace_array(entry->addr.eui_64, 8));
ns_list_remove(inactive_supp_list, entry);
ns_list_add_to_start(active_supp_list, entry);
entry->active = true;
entry->ticks = 0;
// Adds relay address data
entry->addr.type = KMP_ADDR_EUI_64_AND_IP;
}
void ws_pae_lib_supp_list_to_inactive(void *instance, supp_list_t *active_supp_list, supp_entry_t *entry)
{
if (!entry->active) {
return;
}
tr_debug("PAE: to inactive, eui-64: %s", trace_array(entry->addr.eui_64, 8));
if (entry->access_revoked) {
tr_info("Access revoked; deleted, eui-64: %s", trace_array(entry->addr.eui_64, 8));
ws_pae_lib_supp_list_remove(active_supp_list, entry);
return;
}
// Store to key storage
ws_pae_key_storage_supp_write(instance, entry);
// Remove supplicant entry
ws_pae_lib_supp_list_remove(active_supp_list, entry);
}
void ws_pae_lib_supp_list_purge(supp_list_t *active_supp_list, uint16_t max_number, uint8_t max_purge)
{
uint16_t active_supp = ns_list_count(active_supp_list);
if (active_supp > max_number) {
uint16_t remove_count = active_supp - max_number;
if (max_purge > 0 && remove_count > max_purge) {
remove_count = max_purge;
}
// Remove entries from active list if there are no active KMPs ongoing for the entry
ns_list_foreach_safe(supp_entry_t, entry, active_supp_list) {
if (remove_count > 0 && ws_pae_lib_kmp_list_empty(&entry->kmp_list)) {
tr_info("Active supplicant removed, eui-64: %s", trace_array(kmp_address_eui_64_get(&entry->addr), 8));
ws_pae_lib_supp_list_remove(active_supp_list, entry);
remove_count--;
} else {
break;
}
}
}
}
bool ws_pae_lib_supp_list_active_limit_reached(supp_list_t *active_supp_list, uint16_t max_number)
{
uint16_t active_supp = ns_list_count(active_supp_list);
if (active_supp > max_number) {
return true;
}
return false;
}
uint16_t ws_pae_lib_supp_list_kmp_count(supp_list_t *supp_list, kmp_type_e type)
{
uint16_t kmp_count = 0;
ns_list_foreach(supp_entry_t, entry, supp_list) {
ns_list_foreach(kmp_entry_t, kmp_entry, &entry->kmp_list) {
if (kmp_api_type_get(kmp_entry->kmp) == type) {
kmp_count++;
}
}
}
return kmp_count;
}
kmp_api_t *ws_pae_lib_supp_list_kmp_receive_check(supp_list_t *supp_list, const void *pdu, uint16_t size)
{
ns_list_foreach(supp_entry_t, entry, supp_list) {
ns_list_foreach(kmp_entry_t, kmp_entry, &entry->kmp_list) {
if (kmp_api_receive_check(kmp_entry->kmp, pdu, size)) {
return kmp_entry->kmp;
}
}
}
return NULL;
}
supp_entry_t *ws_pae_lib_supp_list_entry_retry_timer_get(supp_list_t *supp_list)
{
supp_entry_t *retry_supp = NULL;
ns_list_foreach(supp_entry_t, entry, supp_list) {
// Finds entry with shortest timeout i.e. oldest one
if (entry->retry_ticks > 0) {
if (!retry_supp || retry_supp->retry_ticks > entry->retry_ticks) {
retry_supp = entry;
}
}
}
return retry_supp;
}
int8_t ws_pae_lib_shared_comp_list_init(shared_comp_list_t *comp_list)
{
ns_list_init(comp_list);
return 0;
}
int8_t ws_pae_lib_shared_comp_list_free(shared_comp_list_t *comp_list)
{
ns_list_foreach_safe(shared_comp_entry_t, entry, comp_list) {
if (entry->data->delete) {
entry->data->delete ();
}
ns_list_remove(comp_list, entry);
ns_dyn_mem_free(entry);
}
return 0;
}
int8_t ws_pae_lib_shared_comp_list_add(shared_comp_list_t *comp_list, kmp_shared_comp_t *data)
{
ns_list_foreach(shared_comp_entry_t, entry, comp_list) {
if (entry->data == data) {
return -1;
}
}
shared_comp_entry_t *entry = ns_dyn_mem_alloc(sizeof(shared_comp_entry_t));
if (!entry) {
return -1;
}
entry->data = data;
ns_list_add_to_end(comp_list, entry);
return 0;
}
int8_t ws_pae_lib_shared_comp_list_remove(shared_comp_list_t *comp_list, kmp_shared_comp_t *data)
{
ns_list_foreach(shared_comp_entry_t, entry, comp_list) {
if (entry->data == data) {
ns_list_remove(comp_list, entry);
ns_dyn_mem_free(entry);
return 0;
}
}
return 0;
}
int8_t ws_pae_lib_shared_comp_list_timeout(shared_comp_list_t *comp_list, uint16_t ticks)
{
ns_list_foreach(shared_comp_entry_t, entry, comp_list) {
if (entry->data->timeout) {
entry->data->timeout(ticks);
}
}
return 0;
}
#endif /* HAVE_WS */