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:
parent
e7ac109f0e
commit
2284f87fdb
@ -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
|
||||
|
||||
@ -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
75
sys/include/shell_lock.h
Normal 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 */
|
||||
/** @} */
|
||||
@ -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
1
sys/shell_lock/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
4
sys/shell_lock/Makefile.include
Normal file
4
sys/shell_lock/Makefile.include
Normal 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
152
sys/shell_lock/shell_lock.c
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user