Implement role-aware HTTPS and SSH authentication

This commit is contained in:
2026-08-30 01:31:05 +02:00
parent cd235445c7
commit 0c058b6a8f
16 changed files with 707 additions and 331 deletions
+35 -12
View File
@@ -20,14 +20,19 @@
#include "serial_service.h"
#include "session_broker.h"
#include "usb_cdc_transport.h"
#include "user_database.h"
#include "web_security.h"
#include "web_serial_transport.h"
#include "web_ui.h"
#include "wifi_manager.h"
#define WEB_SERVER_PORT 443U
#define WEB_SERVER_MAX_AUTHORIZATION 128U
#define WEB_SERVER_MAX_BASIC_DECODED 64U
#define WEB_SERVER_MAX_BASIC_DECODED \
(USER_DATABASE_USERNAME_CAPACITY + 1U + USER_DATABASE_PASSWORD_CAPACITY)
#define WEB_SERVER_MAX_BASIC_ENCODED \
(((WEB_SERVER_MAX_BASIC_DECODED + 2U) / 3U) * 4U)
#define WEB_SERVER_MAX_AUTHORIZATION \
((sizeof("Basic ") - 1U) + WEB_SERVER_MAX_BASIC_ENCODED + 1U)
#define WEB_SERVER_STATUS_JSON_CAPACITY 3072U
static SemaphoreHandle_t s_server_mutex;
@@ -101,12 +106,15 @@ static esp_err_t send_authentication_required(httpd_req_t *request)
return send_plain_error(request, "401 Unauthorized", "Authentication required.\n");
}
static esp_err_t authenticate_request(httpd_req_t *request, bool *authenticated)
static esp_err_t authenticate_request(httpd_req_t *request,
user_principal_t *principal,
bool *authenticated)
{
char authorization[WEB_SERVER_MAX_AUTHORIZATION] = {0};
uint8_t decoded[WEB_SERVER_MAX_BASIC_DECODED] = {0};
size_t decoded_length = 0U;
esp_err_t result = ESP_OK;
memset(principal, 0, sizeof(*principal));
*authenticated = false;
increment_counter(&s_counters.requests);
@@ -133,28 +141,32 @@ static esp_err_t authenticate_request(httpd_req_t *request, bool *authenticated)
}
size_t username_length = (size_t)(separator - decoded);
size_t password_length = decoded_length - username_length - 1U;
result = web_security_authenticate_basic(decoded, username_length,
separator + 1U, password_length,
authenticated);
result = user_database_authenticate_password(
decoded, username_length, separator + 1U, password_length,
principal, authenticated);
cleanup:
secure_wipe(authorization, sizeof(authorization));
secure_wipe(decoded, sizeof(decoded));
if (result != ESP_OK) {
memset(principal, 0, sizeof(*principal));
return result;
}
if (*authenticated) {
increment_counter(&s_counters.authenticated_requests);
} else {
memset(principal, 0, sizeof(*principal));
increment_counter(&s_counters.authentication_failures);
}
return ESP_OK;
}
static esp_err_t authorize_or_respond(httpd_req_t *request, bool *authorized)
static esp_err_t authorize_or_respond(httpd_req_t *request,
user_principal_t *principal,
bool *authorized)
{
*authorized = false;
esp_err_t error = authenticate_request(request, authorized);
esp_err_t error = authenticate_request(request, principal, authorized);
if (error != ESP_OK) {
*authorized = false;
return send_plain_error(request, "503 Service Unavailable",
@@ -167,14 +179,17 @@ static esp_err_t send_authenticated_ui(httpd_req_t *request,
web_ui_resource_t resource,
uint64_t *counter)
{
user_principal_t principal = {0};
bool authorized = false;
esp_err_t error = authorize_or_respond(request, &authorized);
esp_err_t error = authorize_or_respond(request, &principal, &authorized);
if (error != ESP_OK || !authorized) {
secure_wipe(&principal, sizeof(principal));
return error;
}
increment_counter(counter);
error = web_ui_send_response(request, resource);
secure_wipe(&principal, sizeof(principal));
if (error != ESP_OK) {
increment_counter(&s_counters.response_errors);
}
@@ -195,18 +210,23 @@ static esp_err_t asset_handler(httpd_req_t *request)
static esp_err_t ticket_handler(httpd_req_t *request)
{
user_principal_t principal = {0};
bool authorized = false;
esp_err_t error = authorize_or_respond(request, &authorized);
esp_err_t error = authorize_or_respond(request, &principal, &authorized);
if (error != ESP_OK || !authorized) {
secure_wipe(&principal, sizeof(principal));
return error;
}
increment_counter(&s_counters.ticket_requests);
if (request->content_len != 0U) {
secure_wipe(&principal, sizeof(principal));
return send_plain_error(request, "400 Bad Request",
"Ticket requests must have an empty body.\n");
}
error = web_serial_transport_handle_authenticated_ticket_request(request);
error = web_serial_transport_handle_authenticated_ticket_request(
request, &principal);
secure_wipe(&principal, sizeof(principal));
if (error == ESP_OK) {
return ESP_OK;
}
@@ -254,11 +274,14 @@ static void format_fingerprint(const uint8_t fingerprint[WEB_SECURITY_SHA256_LEN
static esp_err_t status_handler(httpd_req_t *request)
{
user_principal_t principal = {0};
bool authorized = false;
esp_err_t error = authorize_or_respond(request, &authorized);
esp_err_t error = authorize_or_respond(request, &principal, &authorized);
if (error != ESP_OK || !authorized) {
secure_wipe(&principal, sizeof(principal));
return error;
}
secure_wipe(&principal, sizeof(principal));
increment_counter(&s_counters.status_requests);
wifi_manager_snapshot_t wifi = {0};