170 lines
8.3 KiB
C
170 lines
8.3 KiB
C
/* SPDX-License-Identifier: GPL-3.0-only */
|
|
/* Bounded administrative dispatcher with a small remote-owner boundary. */
|
|
|
|
#pragma once
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include "esp_err.h"
|
|
#include "user_database.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Nonblocking typed settings admission to the canonical dispatcher. */
|
|
esp_err_t admin_ssh_console_submit_serial_settings(uint32_t id);
|
|
esp_err_t admin_ssh_console_submit_account_settings(uint32_t id);
|
|
esp_err_t admin_ssh_console_submit_network_settings(uint32_t id);
|
|
esp_err_t admin_ssh_console_submit_display_settings(uint32_t id);
|
|
esp_err_t admin_ssh_console_submit_broker_settings(uint32_t id);
|
|
esp_err_t admin_ssh_console_submit_ssh_settings(uint32_t id);
|
|
esp_err_t admin_ssh_console_submit_lifecycle_settings(uint32_t id);
|
|
|
|
/* Fits the longest supported ECDSA P-256 OpenSSH key import command. */
|
|
#define ADMIN_SSH_CONSOLE_COMMAND_LINE_CAPACITY 256U
|
|
|
|
#define ADMIN_CONSOLE_TRANSPORT_SSH 0U
|
|
#define ADMIN_CONSOLE_TRANSPORT_WEB 1U
|
|
|
|
typedef struct {
|
|
uint8_t slot_index;
|
|
uint32_t session_id;
|
|
uint32_t slot_generation;
|
|
uint8_t transport; /* Zero is SSH, including legacy designated initializers. */
|
|
} admin_ssh_console_token_t;
|
|
|
|
typedef enum {
|
|
ADMIN_SSH_DEFER_NONE = 0,
|
|
ADMIN_SSH_DEFER_REBOOT,
|
|
ADMIN_SSH_DEFER_STOP,
|
|
ADMIN_SSH_DEFER_DISCONNECT,
|
|
ADMIN_SSH_DEFER_HOST_KEY_ROTATE,
|
|
ADMIN_SSH_DEFER_HOST_KEY_RESET,
|
|
ADMIN_CONSOLE_DEFER_SELF_CLOSE,
|
|
ADMIN_CONSOLE_DEFER_WEB_STOP,
|
|
ADMIN_CONSOLE_DEFER_WEB_CERTIFICATE_ROTATE,
|
|
} admin_ssh_deferred_action_type_t;
|
|
|
|
/* Small owner boundary; module/API names are retained for existing SSH callers.
|
|
* Exactly two shared console slots, not two per transport. slot_index addresses
|
|
* this pool; open_available atomically selects a free slot. Owners must not reuse an identity while
|
|
* old work can exist. transport is a firmware-assigned namespace (0 = SSH).
|
|
* An occupied or still-executing slot cannot be replaced by open_owned().
|
|
*
|
|
* The immutable adapter lives for firmware lifetime. Callbacks run on the
|
|
* control task OUTSIDE console locks for drained/perform, except perform actions
|
|
* selected by dispatcher_actions run serialized on the existing 12KiB dispatcher
|
|
* after drain/delay and queued identity/principal revalidation (no command replay).
|
|
* Zero dispatcher_actions preserves legacy control-task execution. Required is_current
|
|
* runs on the dispatcher outside console locks; it must be bounded and validate
|
|
* full transport identity, originating-session liveness and principal binding,
|
|
* without calling socket libraries or handlers. Core separately checks accounts.
|
|
* drained must be nonblocking, validate the full identity and include pending
|
|
* owner output. perform must revalidate identity and marshal lifecycle work to
|
|
* its owner, never call socket libraries here. Neither callback may call console
|
|
* handlers. supported_actions is a bitmask (1U << action); reject unsupported
|
|
* actions before side effects. Legacy STOP/DISCONNECT/key actions mean SSH;
|
|
* SELF_CLOSE means this frontend; WEB_STOP means HTTPS, not SSH.
|
|
* WEB_CERTIFICATE_ROTATE replaces the HTTPS identity and restarts HTTPS.
|
|
* These WEB actions and SELF_CLOSE ignore argument.
|
|
*
|
|
* One owner serializes feed calls per session; different owners may feed in
|
|
* parallel. Shared completion scratch is nonblocking/serialized by the core.
|
|
* The owner alone consumes output, maintains authentication/session liveness,
|
|
* and calls close on disconnect/revocation. Core copies/rechecks principals at
|
|
* admission and dispatch. Dispatch and prompts also check owner currentness;
|
|
* blocked prompts recheck every 250ms (plus check/scheduling latency). This does
|
|
* not cancel or roll back arbitrary executing handlers. Admission remains the
|
|
* owner's responsibility; is_current need not accept unpublished admission.
|
|
* Close wakes prompts; executing state is retained until the handler returns.
|
|
* Output remains bounded (5s write backpressure); deferred work waits at most
|
|
* 10s for application drain plus 200ms, NOT peer-delivery confirmation.
|
|
* Dispatcher actions then wait behind queued commands/prompts, with input gated
|
|
* until completion or cancellation; the drain bound is not an execution deadline.
|
|
* No new tasks, queues, slots, or browser endpoint are provided by this API.
|
|
*/
|
|
typedef struct {
|
|
uint32_t supported_actions;
|
|
uint32_t dispatcher_actions; /* Subset of supported_actions; immutable. */
|
|
bool (*is_current)(const admin_ssh_console_token_t *token,
|
|
const user_principal_t *principal);
|
|
bool (*drained)(const admin_ssh_console_token_t *token);
|
|
esp_err_t (*perform)(const admin_ssh_console_token_t *token,
|
|
admin_ssh_deferred_action_type_t action, uint32_t argument);
|
|
} admin_console_owner_t;
|
|
|
|
/* Selects any inactive, nonexecuting slot from the shared two-slot pool.
|
|
* Input slot_index is ignored; only slot_index changes, and only on success.
|
|
* Caller supplies transport/session_id/slot_generation and must retain the
|
|
* returned token. Full pool returns ESP_ERR_INVALID_STATE, like open_owned.
|
|
*/
|
|
esp_err_t admin_ssh_console_open_available(admin_ssh_console_token_t *token,
|
|
const user_principal_t *principal,
|
|
const admin_console_owner_t *owner);
|
|
|
|
esp_err_t admin_ssh_console_open_owned(const admin_ssh_console_token_t *token,
|
|
const user_principal_t *principal,
|
|
const admin_console_owner_t *owner);
|
|
|
|
typedef struct {
|
|
bool active;
|
|
bool command_pending;
|
|
bool input_pending;
|
|
bool output_pending;
|
|
bool deferred_action_pending;
|
|
size_t input_length;
|
|
size_t output_length;
|
|
} admin_ssh_console_session_snapshot_t;
|
|
|
|
/* Starts the single command worker. It is the sole esp_console_run() caller. */
|
|
esp_err_t admin_ssh_console_init(void);
|
|
/* Register administration-shell-only root commands after esp_console initialization. */
|
|
esp_err_t admin_ssh_console_register_commands(void);
|
|
/* Called after all ESP-IDF commands are registered; starts the UART0 frontend. */
|
|
esp_err_t admin_ssh_console_start_uart_frontend(void);
|
|
|
|
/* Valid only while a registered command callback runs on the dispatcher task. */
|
|
bool admin_ssh_console_dispatch_is_remote(void);
|
|
bool admin_ssh_console_dispatch_is_web(void);
|
|
const user_principal_t *admin_ssh_console_dispatch_principal(void);
|
|
/* Revalidate account, originating owner/session and token before side effects.
|
|
* False outside the dispatcher; UART0 dispatch remains physically trusted. */
|
|
bool admin_ssh_console_dispatch_is_current(void);
|
|
/* Shared parsed browser account policy: dispatcher admission + handler defense. */
|
|
bool admin_ssh_console_web_user_command_allowed(
|
|
size_t argc, char **argv, const user_principal_t *principal);
|
|
esp_err_t admin_ssh_console_dispatch_read_input(
|
|
const char *prompt, uint8_t *output, size_t capacity,
|
|
bool hidden, size_t *output_length);
|
|
esp_err_t admin_ssh_console_dispatch_defer(
|
|
admin_ssh_deferred_action_type_t action, uint32_t argument);
|
|
|
|
/* SSH compatibility entry point, implemented by the owner in ssh_transport.c.
|
|
* Token/principal are copied; no SSH or socket objects cross this boundary.
|
|
* Existing feed/close/read/snapshot APIs below also accept open_owned tokens.
|
|
*/
|
|
esp_err_t admin_ssh_console_open(const admin_ssh_console_token_t *token,
|
|
const user_principal_t *principal);
|
|
void admin_ssh_console_close(const admin_ssh_console_token_t *token);
|
|
|
|
/* Called by the session owner. Returns false when input must be backpressured. */
|
|
bool admin_ssh_console_accepts_input(const admin_ssh_console_token_t *token);
|
|
bool admin_ssh_console_feed_input(const admin_ssh_console_token_t *token,
|
|
const uint8_t *data, size_t length,
|
|
size_t *consumed);
|
|
|
|
/* Called by the session owner; copies already-produced output without blocking. */
|
|
esp_err_t admin_ssh_console_read_output(const admin_ssh_console_token_t *token,
|
|
uint8_t *data, size_t capacity,
|
|
size_t *received);
|
|
esp_err_t admin_ssh_console_get_session_snapshot(
|
|
const admin_ssh_console_token_t *token,
|
|
admin_ssh_console_session_snapshot_t *snapshot);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|