1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-15 09:33:50 +01:00

sys/shell: add module shell_lock

Module to lock the running shell with a password. Shell is proceeded only
when the valid password was entered by the user. After 3 failed attempts,
the input is blocked for a few seconds to slow down brute force attacks.
Does not make use of any cryptographic features yet.
This commit is contained in:
Hendrik van Essen 2020-01-18 01:31:25 +01:00
parent e7ac109f0e
commit 2284f87fdb
7 changed files with 257 additions and 1 deletions

View File

@ -97,6 +97,10 @@ ifneq (,$(filter trace,$(USEMODULE)))
USEMODULE += ztimer_usec
endif
ifneq (,$(filter shell_lock,$(USEMODULE)))
USEMODULE += ztimer_msec
endif
ifneq (,$(filter ssp,$(USEMODULE)))
FEATURES_REQUIRED += ssp
endif

View File

@ -155,3 +155,7 @@ endif
ifneq (,$(filter test_utils_netdev_eth_minimal,$(USEMODULE)))
CFLAGS += -DCONFIG_NETDEV_REGISTER_SIGNAL
endif
ifneq (,$(filter shell_lock,$(USEMODULE)))
include $(RIOTBASE)/sys/shell_lock/Makefile.include
endif

75
sys/include/shell_lock.h Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2020 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_shell_lock Shell lock
* @ingroup sys
* @brief Simple module to provide a password protection for the shell.
* @experimental This module is an experimental feature and only shows as a proof of concept how
* the shell could be protected with a password. Do not expect relevant security from
* it for production, since Man-in-the-Middle attacks are possible depending on the
* used connection method!
*
* @{
*
* @file
* @brief Shell interface definition
*/
#ifndef SHELL_LOCK_H
#define SHELL_LOCK_H
#ifdef __cplusplus
extern "C" {
#endif
#include "shell.h"
#ifdef MODULE_SHELL_LOCK
#ifndef CONFIG_SHELL_LOCK_PASSWORD
#error Using MODULE_SHELL_LOCK requires defining CONFIG_SHELL_LOCK_PASSWORD
#endif /* CONFIG_SHELL_LOCK_PASSWORD */
#endif /* MODULE_SHELL_LOCK */
/**
* @brief Lock the login process after given attempts of failed logins for
* a few seconds
*/
#define CONFIG_SHELL_LOCK_ATTEMPTS_BEFORE_TIME_LOCK 3
/**
* @brief Entry point for the lock mechanism. If locked, the user will
* be asked for a password. This function won't return until the
* correct password has been entered.
*
* @param[in] line_buf Buffer for reading in the password from stdin
* @param[in] buf_size Buffer size
*/
void shell_lock_checkpoint(char *line_buf, int buf_size);
/**
* @brief Returns true, if the shell is in the locked state.
*
* @return Whether the shell is locked or not.
*/
bool shell_lock_is_locked(void);
#ifdef MODULE_SHELL_LOCK_AUTO_LOCKING
/**
* @brief Restart the timeout interval before the shell is locked
* automatically.
*/
void shell_lock_auto_lock_refresh(void);
#endif /* MODULE_SHELL_LOCK_AUTO_LOCKING */
#ifdef __cplusplus
}
#endif
#endif /* SHELL_LOCK_H */
/** @} */

View File

@ -38,6 +38,7 @@
#include "kernel_defines.h"
#include "xfa.h"
#include "shell.h"
#include "shell_lock.h"
/* define shell command cross file array */
XFA_INIT_CONST(shell_command_t*, shell_commands_xfa);
@ -61,6 +62,10 @@ XFA_INIT_CONST(shell_command_t*, shell_commands_xfa);
#define PARSE_ESCAPE_MASK 0x4;
extern void shell_lock_checkpoint(char *line_buf, int len);
extern bool shell_lock_is_locked(void);
extern void shell_lock_reset(void);
enum parse_state {
PARSE_BLANK = 0x0,
@ -106,6 +111,7 @@ static shell_command_handler_t find_handler(
const shell_command_t *command_list, char *command)
{
shell_command_handler_t handler = NULL;
if (command_list != NULL) {
handler = search_commands(command_list, command);
}
@ -404,7 +410,7 @@ static inline void new_line(void)
* @return EOF, if the end of the input stream was reached.
* @return -ENOBUFS if the buffer size was exceeded.
*/
static int readline(char *buf, size_t size)
int readline(char *buf, size_t size) /* needed externally by module shell_lock */
{
int curr_pos = 0;
bool length_exceeded = false;
@ -471,11 +477,21 @@ static int readline(char *buf, size_t size)
void shell_run_once(const shell_command_t *shell_commands,
char *line_buf, int len)
{
if (IS_USED(MODULE_SHELL_LOCK)) {
shell_lock_checkpoint(line_buf, len);
}
print_prompt();
while (1) {
int res = readline(line_buf, len);
if (IS_USED(MODULE_SHELL_LOCK)) {
if (shell_lock_is_locked()) {
break;
}
}
switch (res) {
case EOF:

1
sys/shell_lock/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,4 @@
$(shell $(COLOR_ECHO) "$(COLOR_YELLOW)shell_lock is an experimental feature and only shows as a \
proof of concept how the shell could be protected with a password. Do not expect relevant \
security from it for production, since Man-in-the-Middle attacks are possible depending on the \
used connection method!$(COLOR_RESET)" 1>&2)

152
sys/shell_lock/shell_lock.c Normal file
View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2020 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_shell_lock
* @{
*
* @file
* @brief Module to lock the running shell with a password.
*
* The Shell is proceeded only when the valid password was entered by the user.
* After 3 (default) failed attempts, the input is blocked for a few seconds to
* slow down brute force attacks.
* Does not make use of any cryptographic features yet.
*
* @author Hendrik van Essen <hendrik.ve@fu-berlin.de>
*
* @}
*/
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "xtimer.h"
#include "shell_lock.h"
#if defined(MODULE_NEWLIB) || defined(MODULE_PICOLIBC)
#define flush_if_needed() fflush(stdout)
#else
#define flush_if_needed()
#endif /* MODULE_NEWLIB || MODULE_PICOLIBC */
static bool _shell_is_locked = true;
/* defined in shell.c */
extern int readline(char *buf, size_t size);
static int _lock_handler(int argc, char **argv)
{
(void) argc;
(void) argv;
_shell_is_locked = true;
return 0;
}
SHELL_COMMAND(lock, "Lock the shell", _lock_handler);
static inline void _print_password_prompt(void)
{
printf("Password: ");
flush_if_needed();
}
/* Implementation of strcmp that does not return after the first difference
* which could give away information about the first n correct characters of
* the password. The length of the loop is only dependent on the input string.
* Don't optimize this function by a compiler. */
static bool __attribute__((optimize("O0"))) _safe_strcmp(const char* input, const char* pwd)
{
bool the_same = true;
int input_len = strlen(input);
int pwd_len = strlen(pwd);
int input_index = 0;
int pwd_index = 0;
do {
if (input[input_index] != pwd[pwd_index]) {
the_same &= false;
}
else {
the_same &= true;
}
/* keep indices at last index of respective string */
if (input_index < input_len) {
input_index++;
}
if (pwd_index < pwd_len) {
pwd_index++;
}
} while (input[input_index] != '\0');
if (input_len != pwd_len) {
/* substring of the password doesn't count */
return false;
}
return the_same;
}
static bool _login(char *line_buf, size_t buf_size)
{
_print_password_prompt();
if (readline(line_buf, buf_size) > 0) {
return _safe_strcmp(line_buf, CONFIG_SHELL_LOCK_PASSWORD);
}
return false;
}
/**
* Repeatedly prompt for the password.
*
* This function won't return until the correct password has been entered.
*/
static void _login_barrier(char *line_buf, size_t buf_size)
{
while (1) {
int attempts = CONFIG_SHELL_LOCK_ATTEMPTS_BEFORE_TIME_LOCK;
while (attempts--) {
if (_login(line_buf, buf_size)) {
return;
}
puts("Wrong password");
ztimer_sleep(ZTIMER_MSEC, 1000);
}
ztimer_sleep(ZTIMER_MSEC, 7000);
}
}
bool shell_lock_is_locked(void)
{
return _shell_is_locked;
}
void shell_lock_checkpoint(char *line_buf, int buf_size)
{
if (_shell_is_locked) {
printf("The shell is locked. Enter a valid password to unlock.\n\n");
_login_barrier(line_buf, buf_size);
_shell_is_locked = false;
}
}