Add SSH host identity rotation controls

This commit is contained in:
2026-09-13 19:58:05 +02:00
parent aa4bbc2c8c
commit 8df1d2218b
19 changed files with 626 additions and 107 deletions
+54 -33
View File
@@ -707,6 +707,11 @@ static esp_err_t create_listener(void)
static esp_err_t start_runtime(void)
{
/* Never overwrite an orphaned context/listener or sessions after failed stop. */
if (s_context != NULL || s_listen_fd >= 0) return ESP_ERR_INVALID_STATE;
for (size_t index = 0U; index < SSH_TRANSPORT_MAX_SESSIONS; ++index) {
if (s_slots[index].state != SSH_TRANSPORT_SESSION_FREE) return ESP_ERR_INVALID_STATE;
}
esp_err_t error = create_context();
if (error == ESP_OK) {
error = create_listener();
@@ -752,7 +757,7 @@ static esp_err_t stop_runtime(void)
break;
}
}
if (s_context != NULL) {
if (all_free && s_context != NULL) {
wolfSSH_CTX_free(s_context);
s_context = NULL;
}
@@ -1393,10 +1398,18 @@ static void process_slots(void)
}
if (all_free) {
taskENTER_CRITICAL(&s_lock);
if (!s_running) {
s_cleanup_pending = false;
}
bool stopped = !s_running;
taskEXIT_CRITICAL(&s_lock);
/* The owner alone retires the retained context, before reopening admission. */
if (stopped) {
if (s_context != NULL) {
wolfSSH_CTX_free(s_context);
s_context = NULL;
}
taskENTER_CRITICAL(&s_lock);
s_cleanup_pending = false;
taskEXIT_CRITICAL(&s_lock);
}
}
}
@@ -1563,46 +1576,54 @@ esp_err_t ssh_transport_stop(void)
return request_running(false);
}
esp_err_t ssh_transport_replace_host_key(bool reset)
esp_err_t ssh_transport_replace_identity(uint32_t service_generation,
uint32_t identity_generation,
bool reset, bool *committed)
{
if (s_command_mutex == NULL) {
return ESP_ERR_INVALID_STATE;
}
xSemaphoreTake(s_command_mutex, portMAX_DELAY);
if (!committed || (!!service_generation != !!identity_generation) ||
(reset && service_generation) || service_generation == UINT32_MAX ||
identity_generation == UINT32_MAX) return ESP_ERR_INVALID_ARG;
*committed = false;
if (!s_command_mutex) return ESP_ERR_INVALID_STATE;
if (xSemaphoreTake(s_command_mutex, 0U) != pdTRUE) return ESP_ERR_TIMEOUT;
bool was_running;
bool cleanup_pending;
taskENTER_CRITICAL(&s_lock);
if (!s_initialized || s_transitioning) {
taskEXIT_CRITICAL(&s_lock);
xSemaphoreGive(s_command_mutex);
return ESP_ERR_INVALID_STATE;
}
was_running = s_running;
cleanup_pending = s_cleanup_pending;
bool valid = s_initialized && !s_transitioning &&
(!service_generation || (!s_cleanup_pending && service_generation == s_management_generation));
bool was_running = s_running, cleanup_pending = s_cleanup_pending;
taskEXIT_CRITICAL(&s_lock);
esp_err_t error = ESP_OK;
if (was_running || cleanup_pending) {
error = request_running_locked(false);
}
uint32_t token = 0;
esp_err_t error = valid ? ssh_security_reserve_identity(identity_generation, reset, &token)
: ESP_ERR_INVALID_STATE;
if (error == ESP_OK) {
error = reset ? ssh_security_reset() : ssh_security_rotate();
}
if (error != ESP_OK) {
if (was_running) {
(void)request_running_locked(true);
taskENTER_CRITICAL(&s_lock);
if (s_management_generation != UINT32_MAX) ++s_management_generation;
taskEXIT_CRITICAL(&s_lock);
/* Keep the service mutex and identity reservation through stop/replace/start.
* Failed stop must never mutate identity or attempt another start. */
if (was_running || cleanup_pending) error = request_running_locked(false);
if (error == ESP_OK) {
error = ssh_security_replace_reserved(token);
*committed = error == ESP_OK;
if (error != ESP_OK && was_running) {
/* Stop succeeded: restore service using unchanged committed material. */
(void)request_running_locked(true);
} else if (error == ESP_OK && (was_running || reset)) {
error = request_running_locked(true);
}
}
xSemaphoreGive(s_command_mutex);
return error;
}
if (was_running || reset) {
error = request_running_locked(true);
}
ssh_security_release_identity(token);
xSemaphoreGive(s_command_mutex);
return error;
}
esp_err_t ssh_transport_replace_host_key(bool reset)
{
bool committed;
return ssh_transport_replace_identity(0, 0, reset, &committed);
}
esp_err_t ssh_transport_get_snapshot(ssh_transport_snapshot_t *snapshot)
{
if (snapshot == NULL) {