160 lines
5.9 KiB
C
160 lines
5.9 KiB
C
/* 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
|
|
#define USER_DATABASE_ERR_DUPLICATE_SSH_KEY ESP_ERR_NOT_ALLOWED
|
|
|
|
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 the legacy recovery credential. Once bootstrapped, that credential
|
|
* remains independent and no longer authenticates Phase 8B network services.
|
|
*/
|
|
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
|