Add role-based user database administration

This commit is contained in:
2026-08-30 00:56:18 +02:00
parent 0c7b763bec
commit cd235445c7
14 changed files with 2235 additions and 16 deletions
+157
View File
@@ -0,0 +1,157 @@
/* SPDX-License-Identifier: GPL-3.0-only */
/* Bounded persistent role-based user and SSH authorized-key database. */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define USER_DATABASE_NVS_NAMESPACE "user_db"
#define USER_DATABASE_NVS_BLOB_KEY "database"
#define USER_DATABASE_MAX_USERS 8U
#define USER_DATABASE_MAX_SSH_KEYS_PER_USER 3U
#define USER_DATABASE_USERNAME_CAPACITY 16U
#define USER_DATABASE_PASSWORD_CAPACITY 64U
#define USER_DATABASE_PASSWORD_MIN_LENGTH 12U
#define USER_DATABASE_GENERATED_PASSWORD_LENGTH 24U
#define USER_DATABASE_SSH_KEY_TYPE_CAPACITY 32U
#define USER_DATABASE_SSH_KEY_BLOB_CAPACITY 128U
#define USER_DATABASE_SHA256_LENGTH 32U
typedef enum {
USER_ROLE_USER = 1,
USER_ROLE_ADMIN = 2,
} user_role_t;
typedef enum {
USER_AUTH_METHOD_PASSWORD = 1,
USER_AUTH_METHOD_SSH_PUBLIC_KEY = 2,
} user_auth_method_t;
typedef enum {
USER_DATABASE_LOAD_STORED = 0,
USER_DATABASE_LOAD_MIGRATED_LEGACY,
USER_DATABASE_LOAD_EMPTY,
} user_database_load_result_t;
typedef struct {
const uint8_t *username;
size_t username_length;
const uint8_t *password;
size_t password_length;
} user_database_legacy_credentials_t;
typedef struct {
uint32_t user_id;
uint32_t auth_generation;
user_role_t role;
user_auth_method_t method;
size_t username_length;
char username[USER_DATABASE_USERNAME_CAPACITY + 1U];
} user_principal_t;
typedef struct {
size_t password_length;
uint8_t password[USER_DATABASE_PASSWORD_CAPACITY + 1U];
} user_database_generated_password_t;
typedef struct {
bool active;
uint8_t index;
size_t key_type_length;
char key_type[USER_DATABASE_SSH_KEY_TYPE_CAPACITY + 1U];
uint8_t sha256_fingerprint[USER_DATABASE_SHA256_LENGTH];
} user_database_key_snapshot_t;
typedef struct {
bool active;
uint32_t user_id;
uint32_t auth_generation;
user_role_t role;
size_t username_length;
char username[USER_DATABASE_USERNAME_CAPACITY + 1U];
uint8_t public_key_count;
user_database_key_snapshot_t public_keys[USER_DATABASE_MAX_SSH_KEYS_PER_USER];
} user_database_user_snapshot_t;
typedef struct {
bool initialized;
bool admin_bootstrapped;
uint32_t generation;
uint8_t user_count;
uint8_t admin_count;
user_database_user_snapshot_t users[USER_DATABASE_MAX_USERS];
} user_database_snapshot_t;
esp_err_t user_database_init(const user_database_legacy_credentials_t *legacy,
user_database_load_result_t *load_result);
/*
* Before the first administrator is established, keep the migrated account in
* sync with Phase 8A's legacy network credential. Once bootstrapped, the two
* credentials deliberately remain independent until the Phase 8B cutover.
*/
esp_err_t user_database_sync_legacy_credentials(
const user_database_legacy_credentials_t *legacy, bool *synchronized);
/* Explicit UART0 recovery: replace unavailable user storage with one legacy user. */
esp_err_t user_database_recover_from_legacy(
const user_database_legacy_credentials_t *legacy);
esp_err_t user_database_get_snapshot(user_database_snapshot_t *snapshot);
esp_err_t user_database_authenticate_password(
const uint8_t *username, size_t username_length,
const uint8_t *password, size_t password_length,
user_principal_t *principal, bool *authenticated);
esp_err_t user_database_authorize_ssh_public_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,
user_principal_t *principal, bool *authorized);
esp_err_t user_database_principal_is_current(const user_principal_t *principal,
bool *current);
esp_err_t user_database_bootstrap_admin(const uint8_t *password,
size_t password_length);
esp_err_t user_database_bootstrap_admin_generated(
user_database_generated_password_t *generated_password);
esp_err_t user_database_create(const uint8_t *username, size_t username_length,
user_role_t role,
const uint8_t *password, size_t password_length);
esp_err_t user_database_create_generated(
const uint8_t *username, size_t username_length, user_role_t role,
user_database_generated_password_t *generated_password);
esp_err_t user_database_delete(const uint8_t *username, size_t username_length);
esp_err_t user_database_set_role(const uint8_t *username, size_t username_length,
user_role_t role);
esp_err_t user_database_set_password(const uint8_t *username, size_t username_length,
const uint8_t *password, size_t password_length);
esp_err_t user_database_generate_password(
const uint8_t *username, size_t username_length,
user_database_generated_password_t *generated_password);
esp_err_t user_database_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);
esp_err_t user_database_remove_ssh_key(const uint8_t *username,
size_t username_length,
uint8_t key_index);
esp_err_t user_database_clear_ssh_keys(const uint8_t *username,
size_t username_length);
bool user_database_username_valid(const uint8_t *username, size_t length);
bool user_database_password_valid(const uint8_t *password, size_t length);
bool user_database_key_valid(const uint8_t *key_type, size_t key_type_length,
const uint8_t *key_blob, size_t key_blob_length);
const char *user_role_to_string(user_role_t role);
bool user_role_parse(const char *text, user_role_t *role);
#ifdef __cplusplus
}
#endif