Harden cleanup and reduce internal RAM use. Phase 8C nearly validated
and somewhat stable.
This commit is contained in:
+65
-33
@@ -6,6 +6,7 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "mbedtls/ecp.h"
|
||||
@@ -66,7 +67,7 @@ typedef struct {
|
||||
|
||||
static SemaphoreHandle_t s_mutex;
|
||||
static stored_database_t s_database;
|
||||
static stored_database_t s_candidate;
|
||||
static stored_database_t *s_candidate;
|
||||
static bool s_initialized;
|
||||
static uint8_t s_dummy_salt[USER_DATABASE_PASSWORD_SALT_LENGTH];
|
||||
static uint8_t s_dummy_hash[USER_DATABASE_PASSWORD_HASH_LENGTH];
|
||||
@@ -80,9 +81,30 @@ static esp_err_t initialize_dummy_verifier(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
static esp_err_t allocate_candidate(void)
|
||||
{
|
||||
if (s_candidate != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
s_candidate = heap_caps_calloc_prefer(
|
||||
1U, sizeof(*s_candidate), 2,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT,
|
||||
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
return s_candidate != NULL ? ESP_OK : ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
static void discard_candidate(void)
|
||||
{
|
||||
secure_wipe(&s_candidate, sizeof(s_candidate));
|
||||
if (s_candidate != NULL) {
|
||||
secure_wipe(s_candidate, sizeof(*s_candidate));
|
||||
}
|
||||
}
|
||||
|
||||
static void release_candidate(void)
|
||||
{
|
||||
discard_candidate();
|
||||
heap_caps_free(s_candidate);
|
||||
s_candidate = NULL;
|
||||
}
|
||||
|
||||
static bool constant_time_equal(const uint8_t *left, const uint8_t *right, size_t length)
|
||||
@@ -437,10 +459,10 @@ static esp_err_t next_generation(uint32_t *generation)
|
||||
|
||||
static esp_err_t commit_candidate_locked(void)
|
||||
{
|
||||
recount(&s_candidate);
|
||||
esp_err_t error = next_generation(&s_candidate.generation);
|
||||
recount(s_candidate);
|
||||
esp_err_t error = next_generation(&s_candidate->generation);
|
||||
if (error == ESP_OK) {
|
||||
error = validate_database(&s_candidate);
|
||||
error = validate_database(s_candidate);
|
||||
}
|
||||
if (error != ESP_OK) {
|
||||
discard_candidate();
|
||||
@@ -451,7 +473,7 @@ static esp_err_t commit_candidate_locked(void)
|
||||
error = nvs_open(USER_DATABASE_NVS_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (error == ESP_OK) {
|
||||
error = nvs_set_blob(handle, USER_DATABASE_NVS_BLOB_KEY,
|
||||
&s_candidate, sizeof(s_candidate));
|
||||
s_candidate, sizeof(*s_candidate));
|
||||
if (error == ESP_OK) {
|
||||
error = nvs_commit(handle);
|
||||
}
|
||||
@@ -459,7 +481,7 @@ static esp_err_t commit_candidate_locked(void)
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
secure_wipe(&s_database, sizeof(s_database));
|
||||
s_database = s_candidate;
|
||||
s_database = *s_candidate;
|
||||
}
|
||||
discard_candidate();
|
||||
return error;
|
||||
@@ -530,8 +552,8 @@ static esp_err_t synchronize_legacy_locked(
|
||||
return error;
|
||||
}
|
||||
|
||||
s_candidate = s_database;
|
||||
stored_user_t *candidate_user = &s_candidate.users[index];
|
||||
*s_candidate = s_database;
|
||||
stored_user_t *candidate_user = &s_candidate->users[index];
|
||||
error = set_record_password(candidate_user, legacy->password,
|
||||
legacy->password_length);
|
||||
if (error == ESP_OK) {
|
||||
@@ -557,12 +579,16 @@ esp_err_t user_database_init(const user_database_legacy_credentials_t *legacy,
|
||||
if (s_mutex == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_err_t error = nvs_flash_init();
|
||||
esp_err_t error = allocate_candidate();
|
||||
if (error != ESP_OK) {
|
||||
vSemaphoreDelete(s_mutex);
|
||||
s_mutex = NULL;
|
||||
return error;
|
||||
}
|
||||
error = nvs_flash_init();
|
||||
if (error != ESP_OK) {
|
||||
goto init_failed;
|
||||
}
|
||||
|
||||
bool storage_missing = false;
|
||||
nvs_handle_t handle;
|
||||
@@ -621,7 +647,7 @@ esp_err_t user_database_init(const user_database_legacy_credentials_t *legacy,
|
||||
}
|
||||
*load_result = USER_DATABASE_LOAD_MIGRATED_LEGACY;
|
||||
recount(&s_database);
|
||||
s_candidate = s_database;
|
||||
*s_candidate = s_database;
|
||||
error = commit_candidate_locked();
|
||||
if (error != ESP_OK) {
|
||||
goto init_failed;
|
||||
@@ -635,7 +661,7 @@ esp_err_t user_database_init(const user_database_legacy_credentials_t *legacy,
|
||||
|
||||
init_failed:
|
||||
secure_wipe(&s_database, sizeof(s_database));
|
||||
discard_candidate();
|
||||
release_candidate();
|
||||
secure_wipe(s_dummy_salt, sizeof(s_dummy_salt));
|
||||
secure_wipe(s_dummy_hash, sizeof(s_dummy_hash));
|
||||
vSemaphoreDelete(s_mutex);
|
||||
@@ -673,17 +699,23 @@ esp_err_t user_database_recover_from_legacy(
|
||||
if (s_mutex == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_err_t error = nvs_flash_init();
|
||||
esp_err_t error = allocate_candidate();
|
||||
if (error != ESP_OK) {
|
||||
vSemaphoreDelete(s_mutex);
|
||||
s_mutex = NULL;
|
||||
return error;
|
||||
}
|
||||
error = nvs_flash_init();
|
||||
if (error != ESP_OK) {
|
||||
goto recovery_failed;
|
||||
}
|
||||
|
||||
memset(&s_database, 0, sizeof(s_database));
|
||||
memset(&s_candidate, 0, sizeof(s_candidate));
|
||||
s_candidate.version = USER_DATABASE_SCHEMA_VERSION;
|
||||
s_candidate.size = sizeof(s_candidate);
|
||||
s_candidate.generation = 1U;
|
||||
error = initialize_user(&s_candidate.users[0], legacy->username,
|
||||
memset(s_candidate, 0, sizeof(*s_candidate));
|
||||
s_candidate->version = USER_DATABASE_SCHEMA_VERSION;
|
||||
s_candidate->size = sizeof(*s_candidate);
|
||||
s_candidate->generation = 1U;
|
||||
error = initialize_user(&s_candidate->users[0], legacy->username,
|
||||
legacy->username_length, USER_ROLE_USER,
|
||||
legacy->password, legacy->password_length);
|
||||
if (error == ESP_OK) {
|
||||
@@ -701,7 +733,7 @@ esp_err_t user_database_recover_from_legacy(
|
||||
|
||||
recovery_failed:
|
||||
secure_wipe(&s_database, sizeof(s_database));
|
||||
discard_candidate();
|
||||
release_candidate();
|
||||
secure_wipe(s_dummy_salt, sizeof(s_dummy_salt));
|
||||
secure_wipe(s_dummy_hash, sizeof(s_dummy_hash));
|
||||
vSemaphoreDelete(s_mutex);
|
||||
@@ -918,11 +950,11 @@ static esp_err_t create_locked(const uint8_t *username, size_t username_length,
|
||||
if (free_index < 0) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
s_candidate = s_database;
|
||||
esp_err_t error = initialize_user(&s_candidate.users[free_index], username,
|
||||
*s_candidate = s_database;
|
||||
esp_err_t error = initialize_user(&s_candidate->users[free_index], username,
|
||||
username_length, role, password, password_length);
|
||||
if (error == ESP_OK && role == USER_ROLE_ADMIN) {
|
||||
s_candidate.admin_bootstrapped = 1U;
|
||||
s_candidate->admin_bootstrapped = 1U;
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
return commit_candidate_locked();
|
||||
@@ -984,15 +1016,15 @@ esp_err_t user_database_bootstrap_admin(const uint8_t *password,
|
||||
error = create_locked(s_admin_username, sizeof(s_admin_username) - 1U,
|
||||
USER_ROLE_ADMIN, password, password_length);
|
||||
} else {
|
||||
s_candidate = s_database;
|
||||
stored_user_t *user = &s_candidate.users[index];
|
||||
*s_candidate = s_database;
|
||||
stored_user_t *user = &s_candidate->users[index];
|
||||
error = set_record_password(user, password, password_length);
|
||||
if (error == ESP_OK) {
|
||||
user->role = USER_ROLE_ADMIN;
|
||||
error = next_generation(&user->auth_generation);
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
s_candidate.admin_bootstrapped = 1U;
|
||||
s_candidate->admin_bootstrapped = 1U;
|
||||
error = commit_candidate_locked();
|
||||
} else {
|
||||
discard_candidate();
|
||||
@@ -1026,7 +1058,7 @@ static esp_err_t mutate_user_begin(const uint8_t *username, size_t username_leng
|
||||
if (*index < 0) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
s_candidate = s_database;
|
||||
*s_candidate = s_database;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -1050,7 +1082,7 @@ esp_err_t user_database_delete(const uint8_t *username, size_t username_length)
|
||||
error = ESP_ERR_INVALID_STATE;
|
||||
discard_candidate();
|
||||
} else {
|
||||
secure_wipe(&s_candidate.users[index], sizeof(s_candidate.users[index]));
|
||||
secure_wipe(&s_candidate->users[index], sizeof(s_candidate->users[index]));
|
||||
error = commit_candidate_locked();
|
||||
}
|
||||
}
|
||||
@@ -1069,7 +1101,7 @@ esp_err_t user_database_set_role(const uint8_t *username, size_t username_length
|
||||
int index;
|
||||
esp_err_t error = mutate_user_begin(username, username_length, &index);
|
||||
if (error == ESP_OK) {
|
||||
stored_user_t *user = &s_candidate.users[index];
|
||||
stored_user_t *user = &s_candidate->users[index];
|
||||
if (user->role == role) {
|
||||
error = ESP_OK;
|
||||
discard_candidate();
|
||||
@@ -1080,7 +1112,7 @@ esp_err_t user_database_set_role(const uint8_t *username, size_t username_length
|
||||
user->role = (uint8_t)role;
|
||||
error = next_generation(&user->auth_generation);
|
||||
if (error == ESP_OK && role == USER_ROLE_ADMIN) {
|
||||
s_candidate.admin_bootstrapped = 1U;
|
||||
s_candidate->admin_bootstrapped = 1U;
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
error = commit_candidate_locked();
|
||||
@@ -1104,7 +1136,7 @@ esp_err_t user_database_set_password(const uint8_t *username, size_t username_le
|
||||
int index;
|
||||
esp_err_t error = mutate_user_begin(username, username_length, &index);
|
||||
if (error == ESP_OK) {
|
||||
stored_user_t *user = &s_candidate.users[index];
|
||||
stored_user_t *user = &s_candidate->users[index];
|
||||
error = set_record_password(user, password, password_length);
|
||||
if (error == ESP_OK) {
|
||||
error = next_generation(&user->auth_generation);
|
||||
@@ -1149,7 +1181,7 @@ esp_err_t user_database_add_ssh_key(
|
||||
int user_index;
|
||||
esp_err_t error = mutate_user_begin(username, username_length, &user_index);
|
||||
if (error == ESP_OK) {
|
||||
stored_user_t *user = &s_candidate.users[user_index];
|
||||
stored_user_t *user = &s_candidate->users[user_index];
|
||||
int free_index = -1;
|
||||
for (size_t index = 0U;
|
||||
index < USER_DATABASE_MAX_SSH_KEYS_PER_USER; ++index) {
|
||||
@@ -1210,7 +1242,7 @@ esp_err_t user_database_remove_ssh_key(const uint8_t *username,
|
||||
int user_index;
|
||||
esp_err_t error = mutate_user_begin(username, username_length, &user_index);
|
||||
if (error == ESP_OK) {
|
||||
stored_user_t *user = &s_candidate.users[user_index];
|
||||
stored_user_t *user = &s_candidate->users[user_index];
|
||||
if (user->keys[key_index].active == 0U) {
|
||||
error = ESP_ERR_NOT_FOUND;
|
||||
discard_candidate();
|
||||
@@ -1239,7 +1271,7 @@ esp_err_t user_database_clear_ssh_keys(const uint8_t *username,
|
||||
int user_index;
|
||||
esp_err_t error = mutate_user_begin(username, username_length, &user_index);
|
||||
if (error == ESP_OK) {
|
||||
stored_user_t *user = &s_candidate.users[user_index];
|
||||
stored_user_t *user = &s_candidate->users[user_index];
|
||||
if (user->key_count == 0U) {
|
||||
error = ESP_OK;
|
||||
discard_candidate();
|
||||
|
||||
Reference in New Issue
Block a user