Add Serialized SSH Administrative Console
This commit is contained in:
+97
-40
@@ -7,6 +7,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "admin_command_gate.h"
|
||||
#include "admin_ssh_console.h"
|
||||
#include "console_input.h"
|
||||
#include "esp_console.h"
|
||||
#include "mbedtls/base64.h"
|
||||
@@ -18,6 +20,9 @@
|
||||
|
||||
#define USER_CONSOLE_KEY_LINE_CAPACITY 256U
|
||||
|
||||
/* `user` commands are serialized by the administration gate. */
|
||||
static user_database_snapshot_t s_user_snapshot;
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
@@ -30,6 +35,7 @@ static void print_usage(void)
|
||||
printf(" user role <username> <user|admin> --force\n");
|
||||
printf(" user password <username> [--generate]\n");
|
||||
printf(" user key add <username>\n");
|
||||
printf(" user key add <username> <type> <base64>\n");
|
||||
printf(" user key delete <username> <0..2> --force\n");
|
||||
printf(" user key clear <username> --force\n");
|
||||
}
|
||||
@@ -88,23 +94,22 @@ static void print_user(const user_database_user_snapshot_t *user)
|
||||
|
||||
static int show_users(const char *selected)
|
||||
{
|
||||
user_database_snapshot_t snapshot;
|
||||
esp_err_t error = user_database_get_snapshot(&snapshot);
|
||||
esp_err_t error = user_database_get_snapshot(&s_user_snapshot);
|
||||
if (error != ESP_OK) {
|
||||
printf("User database unavailable: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
if (selected == NULL) {
|
||||
printf("User database: generation=%lu users=%u/%u admins=%u bootstrapped=%s\n",
|
||||
(unsigned long)snapshot.generation,
|
||||
(unsigned int)snapshot.user_count,
|
||||
(unsigned long)s_user_snapshot.generation,
|
||||
(unsigned int)s_user_snapshot.user_count,
|
||||
USER_DATABASE_MAX_USERS,
|
||||
(unsigned int)snapshot.admin_count,
|
||||
snapshot.admin_bootstrapped ? "yes" : "no");
|
||||
(unsigned int)s_user_snapshot.admin_count,
|
||||
s_user_snapshot.admin_bootstrapped ? "yes" : "no");
|
||||
}
|
||||
bool found = false;
|
||||
for (size_t index = 0U; index < USER_DATABASE_MAX_USERS; ++index) {
|
||||
const user_database_user_snapshot_t *user = &snapshot.users[index];
|
||||
const user_database_user_snapshot_t *user = &s_user_snapshot.users[index];
|
||||
if (!user->active ||
|
||||
(selected != NULL &&
|
||||
(strlen(selected) != user->username_length ||
|
||||
@@ -118,7 +123,7 @@ static int show_users(const char *selected)
|
||||
printf("User '%s' not found.\n", selected);
|
||||
return 1;
|
||||
}
|
||||
if (!snapshot.admin_bootstrapped) {
|
||||
if (!s_user_snapshot.admin_bootstrapped) {
|
||||
printf("Administrative network access is not bootstrapped; use 'user bootstrap'.\n");
|
||||
}
|
||||
return 0;
|
||||
@@ -297,6 +302,42 @@ static bool key_delimiter(uint8_t value)
|
||||
return value == ' ' || value == '\t';
|
||||
}
|
||||
|
||||
static int add_key_parts(const char *username,
|
||||
const uint8_t *type, size_t type_length,
|
||||
const uint8_t *encoded, size_t encoded_length)
|
||||
{
|
||||
uint8_t blob[USER_DATABASE_SSH_KEY_BLOB_CAPACITY] = {0};
|
||||
size_t blob_length = 0U;
|
||||
int decoded = mbedtls_base64_decode(blob, sizeof(blob), &blob_length,
|
||||
encoded, encoded_length);
|
||||
if (decoded != 0 || !user_database_key_valid(type, type_length, blob, blob_length)) {
|
||||
printf("Unsupported or malformed key; use ssh-ed25519 or ecdsa-sha2-nistp256.\n");
|
||||
secure_wipe(blob, sizeof(blob));
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t key_index = 0U;
|
||||
esp_err_t error = user_database_add_ssh_key(
|
||||
(const uint8_t *)username, strlen(username), type, type_length,
|
||||
blob, blob_length, &key_index);
|
||||
secure_wipe(blob, sizeof(blob));
|
||||
if (error != ESP_OK) {
|
||||
if (error == USER_DATABASE_ERR_DUPLICATE_SSH_KEY) {
|
||||
printf("Could not add SSH key: that public key is already assigned to this account.\n");
|
||||
} else if (error == ESP_ERR_NO_MEM) {
|
||||
printf("Could not add SSH key: the account already has %u keys.\n",
|
||||
USER_DATABASE_MAX_SSH_KEYS_PER_USER);
|
||||
} else {
|
||||
printf("Could not add SSH key: %s\n", esp_err_to_name(error));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
revoke_user_network_sessions(username);
|
||||
printf("SSH public key added at index %u. Public-key login is active.\n",
|
||||
(unsigned int)key_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_key(const char *username)
|
||||
{
|
||||
uint8_t line[USER_CONSOLE_KEY_LINE_CAPACITY] = {0};
|
||||
@@ -338,42 +379,15 @@ static int add_key(const char *username)
|
||||
size_t encoded_length = encoded_end == NULL
|
||||
? remaining
|
||||
: (size_t)(encoded_end - encoded);
|
||||
uint8_t blob[USER_DATABASE_SSH_KEY_BLOB_CAPACITY] = {0};
|
||||
size_t blob_length = 0U;
|
||||
int decoded = mbedtls_base64_decode(blob, sizeof(blob), &blob_length,
|
||||
encoded, encoded_length);
|
||||
if (decoded != 0 ||
|
||||
!user_database_key_valid(line, type_length, blob, blob_length)) {
|
||||
printf("Unsupported or malformed key; use ssh-ed25519 or ecdsa-sha2-nistp256.\n");
|
||||
secure_wipe(blob, sizeof(blob));
|
||||
secure_wipe(line, sizeof(line));
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t key_index = 0U;
|
||||
error = user_database_add_ssh_key((const uint8_t *)username, strlen(username),
|
||||
line, type_length, blob, blob_length, &key_index);
|
||||
secure_wipe(blob, sizeof(blob));
|
||||
int result = add_key_parts(username, line, type_length, encoded, encoded_length);
|
||||
secure_wipe(line, sizeof(line));
|
||||
if (error != ESP_OK) {
|
||||
if (error == USER_DATABASE_ERR_DUPLICATE_SSH_KEY) {
|
||||
printf("Could not add SSH key: that public key is already assigned to this account.\n");
|
||||
} else if (error == ESP_ERR_NO_MEM) {
|
||||
printf("Could not add SSH key: the account already has %u keys.\n",
|
||||
USER_DATABASE_MAX_SSH_KEYS_PER_USER);
|
||||
} else {
|
||||
printf("Could not add SSH key: %s\n", esp_err_to_name(error));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
revoke_user_network_sessions(username);
|
||||
printf("SSH public key added at index %u. Public-key login is active.\n",
|
||||
(unsigned int)key_index);
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int command_user(int argc, char **argv)
|
||||
static int command_user_inner(int argc, char **argv)
|
||||
{
|
||||
bool remote = admin_ssh_console_dispatch_is_remote();
|
||||
const user_principal_t *principal = admin_ssh_console_dispatch_principal();
|
||||
if (argc == 1 || (argc == 2 && strcmp(argv[1], "status") == 0) ||
|
||||
(argc == 2 && strcmp(argv[1], "list") == 0)) {
|
||||
return show_users(NULL);
|
||||
@@ -383,6 +397,10 @@ static int command_user(int argc, char **argv)
|
||||
}
|
||||
if (argc == 3 && strcmp(argv[1], "recover") == 0 &&
|
||||
strcmp(argv[2], "--force") == 0) {
|
||||
if (remote) {
|
||||
printf("User database recovery is restricted to physical UART0.\n");
|
||||
return 1;
|
||||
}
|
||||
return recover_database();
|
||||
}
|
||||
if ((argc == 2 || argc == 3) && strcmp(argv[1], "bootstrap") == 0) {
|
||||
@@ -391,6 +409,10 @@ static int command_user(int argc, char **argv)
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
if (remote) {
|
||||
printf("Administrator bootstrap is restricted to physical UART0.\n");
|
||||
return 1;
|
||||
}
|
||||
return bootstrap(generated);
|
||||
}
|
||||
if ((argc == 4 || argc == 5) && strcmp(argv[1], "add") == 0) {
|
||||
@@ -399,6 +421,10 @@ static int command_user(int argc, char **argv)
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
if (remote && !generated) {
|
||||
printf("Interactive password entry is restricted to physical UART0; use --generate.\n");
|
||||
return 1;
|
||||
}
|
||||
return add_user(argv[2], argv[3], generated);
|
||||
}
|
||||
if (argc == 4 && strcmp(argv[1], "delete") == 0 &&
|
||||
@@ -438,12 +464,31 @@ static int command_user(int argc, char **argv)
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
if (remote && !generated) {
|
||||
printf("Interactive password entry is restricted to physical UART0; use --generate.\n");
|
||||
return 1;
|
||||
}
|
||||
if (remote && generated && principal != NULL &&
|
||||
strlen(argv[2]) == principal->username_length &&
|
||||
memcmp(argv[2], principal->username, principal->username_length) == 0) {
|
||||
printf("Remote generated-password changes for the current admin are disabled; use UART0.\n");
|
||||
return 1;
|
||||
}
|
||||
return change_password(argv[2], generated);
|
||||
}
|
||||
if (argc == 4 && strcmp(argv[1], "key") == 0 &&
|
||||
strcmp(argv[2], "add") == 0) {
|
||||
if (remote) {
|
||||
printf("Interactive SSH key entry is restricted to physical UART0; provide type and Base64 arguments.\n");
|
||||
return 1;
|
||||
}
|
||||
return add_key(argv[3]);
|
||||
}
|
||||
if (argc == 6 && strcmp(argv[1], "key") == 0 &&
|
||||
strcmp(argv[2], "add") == 0) {
|
||||
return add_key_parts(argv[3], (const uint8_t *)argv[4], strlen(argv[4]),
|
||||
(const uint8_t *)argv[5], strlen(argv[5]));
|
||||
}
|
||||
if (argc == 6 && strcmp(argv[1], "key") == 0 &&
|
||||
strcmp(argv[2], "delete") == 0 && strcmp(argv[5], "--force") == 0) {
|
||||
uint8_t index;
|
||||
@@ -477,6 +522,18 @@ static int command_user(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int command_user(int argc, char **argv)
|
||||
{
|
||||
esp_err_t error = admin_command_gate_take();
|
||||
if (error != ESP_OK) {
|
||||
printf("Administrative command gate unavailable: %s\n", esp_err_to_name(error));
|
||||
return 1;
|
||||
}
|
||||
int result = command_user_inner(argc, argv);
|
||||
admin_command_gate_give();
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t user_console_register_commands(void)
|
||||
{
|
||||
const esp_console_cmd_t command = {
|
||||
|
||||
Reference in New Issue
Block a user