Implement SSH authorized key management

This commit is contained in:
2026-09-08 16:37:47 +02:00
parent 22a7c7b0a5
commit 42f6423d4e
19 changed files with 930 additions and 83 deletions
+96 -10
View File
@@ -1091,6 +1091,44 @@ static bool target_matches_locked(const uint8_t *username, size_t length,
s_database.users[index].auth_generation == expected->auth_generation;
}
esp_err_t user_database_get_account_keys(const user_database_account_t *expected,
user_database_user_snapshot_t *snapshot)
{
if (!snapshot) return ESP_ERR_INVALID_ARG;
memset(snapshot, 0, sizeof(*snapshot));
if (!expected) return ESP_ERR_INVALID_ARG;
size_t length = strnlen(expected->username, sizeof(expected->username));
if (!user_database_username_valid((const uint8_t *)expected->username, length))
return ESP_ERR_INVALID_ARG;
if (!s_initialized || !s_mutex) return ESP_ERR_INVALID_STATE;
if (xSemaphoreTake(s_mutex, 0U) != pdTRUE) return ESP_ERR_TIMEOUT;
esp_err_t error = ESP_ERR_NOT_FOUND;
if (target_matches_locked((const uint8_t *)expected->username, length, expected)) {
const stored_user_t *user = &s_database.users[find_user(&s_database,
(const uint8_t *)expected->username, length)];
snapshot->active = true;
snapshot->user_id = user->user_id;
snapshot->auth_generation = user->auth_generation;
snapshot->role = (user_role_t)user->role;
snapshot->username_length = length;
memcpy(snapshot->username, user->username, length);
snapshot->public_key_count = user->key_count;
for (size_t i = 0; i < USER_DATABASE_MAX_SSH_KEYS_PER_USER; ++i) {
const stored_key_t *key = &user->keys[i];
if (!key->active) continue;
user_database_key_snapshot_t *out = &snapshot->public_keys[i];
out->active = true;
out->index = (uint8_t)i;
out->key_type_length = key->type_length;
memcpy(out->key_type, key->type, key->type_length);
memcpy(out->sha256_fingerprint, key->fingerprint, sizeof(out->sha256_fingerprint));
}
error = ESP_OK;
}
xSemaphoreGive(s_mutex);
return error;
}
static esp_err_t delete_user(const uint8_t *username, size_t username_length,
const user_database_account_t *expected)
{
@@ -1245,11 +1283,11 @@ esp_err_t user_database_generate_password(
return error;
}
esp_err_t user_database_add_ssh_key(
static esp_err_t add_ssh_key(
const uint8_t *username, size_t username_length,
const uint8_t *key_type, size_t key_type_length,
const uint8_t *key_blob, size_t key_blob_length,
uint8_t *key_index)
uint8_t *key_index, const user_database_account_t *expected)
{
if (!s_initialized || s_mutex == NULL || key_index == NULL ||
!user_database_key_valid(key_type, key_type_length, key_blob, key_blob_length)) {
@@ -1257,7 +1295,8 @@ esp_err_t user_database_add_ssh_key(
}
xSemaphoreTake(s_mutex, portMAX_DELAY);
int user_index;
esp_err_t error = mutate_user_begin(username, username_length, &user_index);
esp_err_t error = target_matches_locked(username, username_length, expected)
? mutate_user_begin(username, username_length, &user_index) : ESP_ERR_NOT_FOUND;
if (error == ESP_OK) {
stored_user_t *user = &s_candidate->users[user_index];
int free_index = -1;
@@ -1308,9 +1347,9 @@ esp_err_t user_database_add_ssh_key(
return error;
}
esp_err_t user_database_remove_ssh_key(const uint8_t *username,
size_t username_length,
uint8_t key_index)
static esp_err_t remove_ssh_key(const uint8_t *username,
size_t username_length, uint8_t key_index,
const user_database_account_t *expected)
{
if (!s_initialized || s_mutex == NULL ||
key_index >= USER_DATABASE_MAX_SSH_KEYS_PER_USER) {
@@ -1318,7 +1357,8 @@ esp_err_t user_database_remove_ssh_key(const uint8_t *username,
}
xSemaphoreTake(s_mutex, portMAX_DELAY);
int user_index;
esp_err_t error = mutate_user_begin(username, username_length, &user_index);
esp_err_t error = target_matches_locked(username, username_length, expected)
? mutate_user_begin(username, username_length, &user_index) : ESP_ERR_NOT_FOUND;
if (error == ESP_OK) {
stored_user_t *user = &s_candidate->users[user_index];
if (user->keys[key_index].active == 0U) {
@@ -1339,15 +1379,17 @@ esp_err_t user_database_remove_ssh_key(const uint8_t *username,
return error;
}
esp_err_t user_database_clear_ssh_keys(const uint8_t *username,
size_t username_length)
static esp_err_t clear_ssh_keys(const uint8_t *username,
size_t username_length,
const user_database_account_t *expected)
{
if (!s_initialized || s_mutex == NULL) {
return ESP_ERR_INVALID_STATE;
}
xSemaphoreTake(s_mutex, portMAX_DELAY);
int user_index;
esp_err_t error = mutate_user_begin(username, username_length, &user_index);
esp_err_t error = target_matches_locked(username, username_length, expected)
? mutate_user_begin(username, username_length, &user_index) : ESP_ERR_NOT_FOUND;
if (error == ESP_OK) {
stored_user_t *user = &s_candidate->users[user_index];
if (user->key_count == 0U) {
@@ -1367,3 +1409,47 @@ esp_err_t user_database_clear_ssh_keys(const uint8_t *username,
xSemaphoreGive(s_mutex);
return error;
}
esp_err_t user_database_add_ssh_key(const uint8_t *username, size_t length,
const uint8_t *type, size_t type_length, const uint8_t *blob, size_t blob_length,
uint8_t *index)
{
return add_ssh_key(username, length, type, type_length, blob, blob_length, index, NULL);
}
esp_err_t user_database_remove_ssh_key(const uint8_t *username, size_t length, uint8_t index)
{
return remove_ssh_key(username, length, index, NULL);
}
esp_err_t user_database_clear_ssh_keys(const uint8_t *username, size_t length)
{
return clear_ssh_keys(username, length, NULL);
}
static bool key_target_valid(const user_database_account_t *expected)
{
return expected && user_database_username_valid((const uint8_t *)expected->username,
strnlen(expected->username, sizeof(expected->username)));
}
esp_err_t user_database_add_ssh_key_current(const user_database_account_t *expected,
const uint8_t *type, size_t type_length, const uint8_t *blob, size_t blob_length,
uint8_t *index)
{
if (!key_target_valid(expected)) return ESP_ERR_INVALID_ARG;
return add_ssh_key((const uint8_t *)expected->username, strlen(expected->username),
type, type_length, blob, blob_length, index, expected);
}
esp_err_t user_database_remove_ssh_key_current(const user_database_account_t *expected, uint8_t index)
{
if (!key_target_valid(expected)) return ESP_ERR_INVALID_ARG;
return remove_ssh_key((const uint8_t *)expected->username, strlen(expected->username), index, expected);
}
esp_err_t user_database_clear_ssh_keys_current(const user_database_account_t *expected)
{
if (!key_target_valid(expected)) return ESP_ERR_INVALID_ARG;
return clear_ssh_keys((const uint8_t *)expected->username, strlen(expected->username), expected);
}