2020-01-18 01:31:25 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
2020-01-18 01:33:55 +01:00
|
|
|
* This module also provides a pseudomodule for automated locking after a given
|
|
|
|
* interval. Add "USEMODULE += shell_lock_auto_locking" to your Makefile to
|
|
|
|
* enable this feature.
|
|
|
|
*
|
2020-01-18 01:31:25 +01:00
|
|
|
* @author Hendrik van Essen <hendrik.ve@fu-berlin.de>
|
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2022-06-03 22:20:46 +02:00
|
|
|
#ifdef MODULE_STDIO_TELNET
|
|
|
|
#include "net/telnet.h"
|
|
|
|
#endif
|
2020-01-18 01:33:55 +01:00
|
|
|
#include "ztimer.h"
|
2020-01-18 01:31:25 +01:00
|
|
|
|
|
|
|
#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;
|
|
|
|
|
2020-01-18 01:33:55 +01:00
|
|
|
#ifdef MODULE_SHELL_LOCK_AUTO_LOCKING
|
|
|
|
static ztimer_t _shell_auto_lock_ztimer;
|
|
|
|
#endif
|
|
|
|
|
2020-01-18 01:31:25 +01:00
|
|
|
/* 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. */
|
2023-05-20 22:11:20 +02:00
|
|
|
static bool _safe_strcmp(const char* input, const char* pwd)
|
2020-01-18 01:31:25 +01:00
|
|
|
{
|
2023-05-20 22:11:20 +02:00
|
|
|
volatile bool the_same = true;
|
2020-01-18 01:31:25 +01:00
|
|
|
|
|
|
|
int input_index = 0;
|
|
|
|
int pwd_index = 0;
|
|
|
|
|
|
|
|
do {
|
2023-01-16 21:08:29 +01:00
|
|
|
the_same &= input[input_index] == pwd[pwd_index];
|
2020-01-18 01:31:25 +01:00
|
|
|
|
|
|
|
/* keep indices at last index of respective string */
|
2023-01-16 21:08:29 +01:00
|
|
|
input_index += input[input_index] != '\0';
|
|
|
|
pwd_index += pwd[pwd_index] != '\0';
|
2020-01-18 01:31:25 +01:00
|
|
|
|
2023-01-16 21:08:29 +01:00
|
|
|
} while (input[input_index] != '\0' );
|
2020-01-18 01:31:25 +01:00
|
|
|
|
2023-01-16 21:08:29 +01:00
|
|
|
/* ensure last char is the same */
|
|
|
|
the_same &= input[input_index] == pwd[pwd_index];
|
2020-01-18 01:31:25 +01:00
|
|
|
|
2023-01-16 21:08:29 +01:00
|
|
|
/* ensure last index is the same */
|
|
|
|
the_same &= input_index == pwd_index;
|
2020-01-18 01:31:25 +01:00
|
|
|
|
|
|
|
return the_same;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool _login(char *line_buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
_print_password_prompt();
|
|
|
|
|
2022-06-03 22:20:46 +02:00
|
|
|
while (1) {
|
|
|
|
memset(line_buf, 0, buf_size);
|
|
|
|
while (readline(line_buf, buf_size) > 0) {
|
|
|
|
return _safe_strcmp(line_buf, CONFIG_SHELL_LOCK_PASSWORD);
|
|
|
|
}
|
2020-01-18 01:31:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2022-06-03 22:20:46 +02:00
|
|
|
#ifdef MODULE_STDIO_TELNET
|
|
|
|
telnet_server_disconnect();
|
|
|
|
#endif
|
2020-01-18 01:31:25 +01:00
|
|
|
ztimer_sleep(ZTIMER_MSEC, 7000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-03 22:20:46 +02:00
|
|
|
#ifdef MODULE_STDIO_TELNET
|
|
|
|
void telnet_cb_disconneced(void)
|
|
|
|
{
|
2023-02-14 01:14:51 +01:00
|
|
|
shell_lock_do_lock();
|
2022-06-03 22:20:46 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-02-14 01:14:51 +01:00
|
|
|
void shell_lock_do_lock(void)
|
|
|
|
{
|
|
|
|
_shell_is_locked = true;
|
|
|
|
}
|
|
|
|
|
2020-01-18 01:33:55 +01:00
|
|
|
#ifdef MODULE_SHELL_LOCK_AUTO_LOCKING
|
|
|
|
static void _shell_auto_lock_ztimer_callback(void *arg)
|
|
|
|
{
|
|
|
|
(void) arg;
|
|
|
|
|
2022-06-03 22:20:46 +02:00
|
|
|
#ifdef MODULE_STDIO_TELNET
|
|
|
|
telnet_server_disconnect();
|
|
|
|
#endif
|
2020-01-18 01:33:55 +01:00
|
|
|
_shell_is_locked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void shell_lock_auto_lock_refresh(void)
|
|
|
|
{
|
|
|
|
ztimer_remove(ZTIMER_MSEC, &_shell_auto_lock_ztimer);
|
|
|
|
ztimer_set(ZTIMER_MSEC, &_shell_auto_lock_ztimer,
|
|
|
|
CONFIG_SHELL_LOCK_AUTO_LOCK_TIMEOUT_MS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-01-18 01:31:25 +01:00
|
|
|
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);
|
|
|
|
|
2020-01-18 01:33:55 +01:00
|
|
|
if (IS_USED(MODULE_SHELL_LOCK_AUTO_LOCKING)) {
|
|
|
|
printf("Shell was unlocked.\n\n");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
printf("Shell was unlocked.\n\n"
|
|
|
|
"IMPORTANT: Don't forget to lock the shell after usage, "
|
|
|
|
"because it won't lock itself.\n\n");
|
|
|
|
}
|
|
|
|
|
2020-01-18 01:31:25 +01:00
|
|
|
_shell_is_locked = false;
|
|
|
|
}
|
2020-01-18 01:33:55 +01:00
|
|
|
|
|
|
|
#ifdef MODULE_SHELL_LOCK_AUTO_LOCKING
|
|
|
|
_shell_auto_lock_ztimer.callback = &_shell_auto_lock_ztimer_callback;
|
|
|
|
shell_lock_auto_lock_refresh();
|
|
|
|
#endif
|
2020-01-18 01:31:25 +01:00
|
|
|
}
|