shell.c 14.27 KiB
#include "shell.h"
#include "../ds1307.h"
#include "../log.h"
#include "../door.h"
#include "../cardreader_interface.h"
#define SHELL_LOGIN_PROMPT "\nMezu Login: "
#define MIN(a,b) ((a) < (b) ? (a) : (b))
uint8_t shell_check_password(uint8_t user, char* password);
void shell_set_password(uint8_t user, char* password);
uint8_t shell_find_user(char* username);
void cmd_help(readline_parsed_cmd_t* cmd);
void cmd_passwd(readline_parsed_cmd_t* cmd);
void cmd_keyslot_init(readline_parsed_cmd_t* cmd);
void cmd_keyslot_clear(readline_parsed_cmd_t* cmd);
void cmd_keyslot_list(readline_parsed_cmd_t* cmd);
void cmd_keyslot_disable(readline_parsed_cmd_t* cmd);
void cmd_keyslot_enable(readline_parsed_cmd_t* cmd);
void cmd_keyslot_restore(readline_parsed_cmd_t* cmd);
void cmd_lock(readline_parsed_cmd_t* cmd);
void cmd_unlock(readline_parsed_cmd_t* cmd);
void cmd_logout(readline_parsed_cmd_t* cmd);
void cmd_status(readline_parsed_cmd_t* cmd);
void cmd_keyslot_alarm_stop(readline_parsed_cmd_t* cmd);
void cmd_reset(readline_parsed_cmd_t* cmd);
void cmd_date(readline_parsed_cmd_t* cmd);
void cmd_show_log(readline_parsed_cmd_t* cmd);
void cmd_card_init_key(readline_parsed_cmd_t* cmd);
void cmd_card_clear_key(readline_parsed_cmd_t* cmd);
readline_supported_cmd_t readline_commands[] = {{"help" , 0, 0, cmd_help},
{"logout" , 0, 0, cmd_logout},
{"exit" , 0, 0, cmd_logout},
{"passwd" , 0, 1, cmd_passwd},
{"unlock" , 0, 0, cmd_unlock},
{"lock" , 0, 1, cmd_lock},
{"status" , 0, 0, cmd_status},
{"reset" , 0, 0, cmd_reset},
{"initks" , 0, 2, cmd_keyslot_init},
{"clearks" , 1, 1, cmd_keyslot_clear},
{"disableks" , 1, 1, cmd_keyslot_disable},
{"enableks" , 1, 1, cmd_keyslot_enable},
{"restoreks" , 1, 1, cmd_keyslot_restore},
{"urusai" , 0, 0, cmd_keyslot_alarm_stop},
{"ls" , 0, 0, cmd_keyslot_list},
{"date" , 0, 2, cmd_date},
{"log" , 0, 0, cmd_show_log},
{"initcardkey" , 1, 1, cmd_card_init_key},
{"clearcardkey", 0, 0, cmd_card_clear_key},
{0,0,0,0}};
#define SHELL_STATE_READ_USER 0
#define SHELL_STATE_READ_PASSWORD 1
#define SHELL_STATE_LOGGED_IN 3
#define SHELL_STATE_INVALID_LOGIN 4
#define SHELL_STATE_PASSWD1 5
#define SHELL_STATE_PASSWD2 6
static uint8_t shell_state = SHELL_STATE_READ_USER;
static char* users[NUM_USERS] = {"root", "admin", 0 };
#define SHELL_USER_INVALID 0xFF
static uint8_t login_user = SHELL_USER_INVALID;
static uint8_t passwd_user = SHELL_USER_INVALID;
#define SHELL_MAX_PW_LEN 20
static char passwd_pw[SHELL_MAX_PW_LEN];
#define SHELL_LOGIN_DELAY 200
static uint8_t shell_login_delay = SHELL_LOGIN_DELAY;
void shell_init(void){
readline_set_mode(SHELL_LOGIN_PROMPT, 1 ,0);
readline_init();
}
void shell_tick(void){
if (shell_login_delay > 0){
shell_login_delay--;
if (shell_login_delay == 0 && shell_state == SHELL_STATE_INVALID_LOGIN){
printf_P(PSTR("Login incorrect\n"));
login_user = SHELL_USER_INVALID;
shell_state = SHELL_STATE_READ_USER;
readline_set_mode(SHELL_LOGIN_PROMPT, 1 ,0);
readline_write_prompt();
}
}
}
void shell_show_welcome_prompt(void){
readline_show_welcome_prompt();
}
void shell_process(void){
readline_process();
}
void readline_handle_buffer(char* input_buffer){
if (shell_state == SHELL_STATE_READ_USER){
if (strlen(input_buffer)){
login_user = shell_find_user(input_buffer);
shell_state = SHELL_STATE_READ_PASSWORD;
readline_set_mode("Password: ", 0 ,0);
}
} else if (shell_state == SHELL_STATE_READ_PASSWORD){
if ((login_user != SHELL_USER_INVALID) && (shell_check_password(login_user, input_buffer))){
shell_state = SHELL_STATE_LOGGED_IN;
printf_P(PSTR("Login successful\n"));
ds1307_write_current_date();
if (door_get_status() & DOOR_STATUS_ALARM) log_append(LOG_EVENT_ALARM_LOGIN, login_user);
printf_P(PSTR("\n"));
readline_set_mode("> ", 1 ,1); //start parsing commands
} else {
login_user = SHELL_USER_INVALID;
shell_state = SHELL_STATE_INVALID_LOGIN;
shell_login_delay = SHELL_LOGIN_DELAY;
printf_P(PSTR("\n"));
readline_set_mode(0, 0 ,0);
}
} else if (shell_state == SHELL_STATE_INVALID_LOGIN){
//ignore
} else if (shell_state == SHELL_STATE_PASSWD1){
memcpy(passwd_pw, input_buffer, MIN(strlen(input_buffer), SHELL_MAX_PW_LEN));
readline_set_mode("Confirm Password: ", 0 ,0);
shell_state = SHELL_STATE_PASSWD2;
} else if (shell_state == SHELL_STATE_PASSWD2){
if (strcmp(passwd_pw, input_buffer) == 0){
shell_set_password(passwd_user, passwd_pw);
printf_P(PSTR("Password changed\n"));
} else {
printf_P(PSTR("Passwords don't match\n"));
}
memset(passwd_pw, 0, SHELL_MAX_PW_LEN);
shell_state = SHELL_STATE_LOGGED_IN;
readline_set_mode("> ", 1 ,1);
}
}
void cmd_help(readline_parsed_cmd_t* cmd){
printf_P(PSTR("Known Commands:\n\
lock [-f] - Lock Door\n\
unlock - Unlock door\n\
status - Display system status\n\
reset - Reset controller\n\
passwd [user] - Change user password\n\
initks x - Initialize keyslot x with a new random key\n\
clearks x - Clear keyslot x (delete key)\n\
disableks x - Disable keyslot x (but keep key)\n\
enableks x - Enable a disabled keyslot\n\
restoreks x - Restore keyslot content from last valid key\n\
ls - List keyslots (only key stubs)\n\
initcardkey x - Program the key of keyslot x on the card in the cardreader\n\
clearcardkey - Clear the key of the card currently in the cardreader\n\
date [dd-mm-yy] hh:mm:ss - Set system date and time\n\
log - Show systemlog\n\
urusai - Turn Alarm off\n\
logout - Exit shell\n\
quit - Exit shell\n"));
}
extern void MGMT_transmit_buffer_hex(unsigned char* data, unsigned char length);
void cmd_keyslot_init(readline_parsed_cmd_t* cmd){
uint8_t force = 0;
uint8_t usage_error = 0;
char* numarg = 0;
uint16_t keyslot_index = 0xFFFF;
if (cmd->num_args == 0){
keyslot_index = keystore_find_empty_slot();
} else if (cmd->num_args == 1){
if (strcmp(cmd->args[0], "-f") == 0){
force = 1;
keyslot_index = keystore_find_empty_slot();
} else {
numarg = cmd->args[0];
}
} else if (cmd->num_args == 2){
if (strcmp(cmd->args[0], "-f") == 0){
force = 1;
numarg = cmd->args[1];
} else {
usage_error = 1;
}
} else {
usage_error = 1;
}
if (numarg) {
char* endptr;
keyslot_index = strtol(numarg, &endptr, 10);
printf_P(PSTR("Keyslot '%s' %i\n"), numarg, keyslot_index);
if (*endptr != 0) usage_error = 1; //number parsing didn't reach the end of the string
}
if (usage_error){
printf_P(PSTR("Usage: initks [-f] index\n"));
return;
}
if (keyslot_index > KEY_COUNT){
printf_P(PSTR("Slot number too big (%i)\n"), keyslot_index);
return;
}
if (!force){
if (keystore_read_slot_status(keyslot_index) != KEYSLOT_EMPTY){
printf_P(PSTR("Slot %i not empty\n"), keyslot_index);
return;
}
}
KEY new_key;
if (keystore_init_slot(keyslot_index, new_key)){
printf_P(PSTR("Slot %i initialized\nNew key: "), keyslot_index);
MGMT_transmit_buffer_hex(new_key, sizeof(KEY));
printf_P(PSTR("\n"));
} else {
printf_P(PSTR("Failed!\n"));
}
}
void cmd_keyslot_disable(readline_parsed_cmd_t* cmd){
uint16_t keyslot_index;
char* endptr;
keyslot_index = strtol(cmd->args[0], &endptr, 10);
// printf_P(PSTR("Keyslot '%s' %i\n"), cmd->args[0], keyslot_index);
if (*endptr != 0){//number parsing didn't reach the end of the string
printf_P(PSTR("Usage: disableks index\n"));
}
if (keystore_disable_slot(keyslot_index)){
printf_P(PSTR("OK\n"));
} else {
printf_P(PSTR("Failed\n"));
}
}
void cmd_keyslot_enable(readline_parsed_cmd_t* cmd){
uint16_t keyslot_index;
char* endptr;
keyslot_index = strtol(cmd->args[0], &endptr, 10);
// printf_P(PSTR("Keyslot '%s' %i\n"), cmd->args[0], keyslot_index);
if (*endptr != 0){//number parsing didn't reach the end of the string
printf_P(PSTR("Usage: enableks index\n"));
}
if (keystore_enable_slot(keyslot_index)){
printf_P(PSTR("OK\n"));
} else {
printf_P(PSTR("Failed\n"));
}
}
void cmd_keyslot_clear(readline_parsed_cmd_t* cmd){
uint16_t keyslot_index;
char* endptr;
keyslot_index = strtol(cmd->args[0], &endptr, 10);
// printf_P(PSTR("Keyslot '%s' %i\n"), cmd->args[0], keyslot_index);
if (*endptr != 0){//number parsing didn't reach the end of the string
printf_P(PSTR("Usage: clearks index\n"));
}
if (keystore_clear_slot(keyslot_index)){
printf_P(PSTR("OK\n"));
} else {
printf_P(PSTR("Failed\n"));
}
}
void cmd_keyslot_restore(readline_parsed_cmd_t* cmd){
uint16_t keyslot_index;
char* endptr;
keyslot_index = strtol(cmd->args[0], &endptr, 10);
// printf_P(PSTR("Keyslot '%s' %i\n"), cmd->args[0], keyslot_index);
if (*endptr != 0){//number parsing didn't reach the end of the string
printf_P(PSTR("Usage: restoreks index\n"));
}
if (keystore_restore_slot(keyslot_index)){
printf_P(PSTR("OK\n"));
} else {
printf_P(PSTR("Failed\n"));
}
}
void cmd_keyslot_list(readline_parsed_cmd_t* cmd){
keystore_list();
}
void print_P(const char* pstr){
printf_P(pstr);
}
void cmd_lock(readline_parsed_cmd_t* cmd){
if ((cmd->num_args > 0) && (strcmp(cmd->args[0], "-f") == 0)){
lock();
} else {
lock_checked(print_P);
}
}
void cmd_unlock(readline_parsed_cmd_t* cmd){
if ((cmd->num_args > 0) && (strcmp(cmd->args[0], "-f") == 0)){
unlock();
} else {
unlock_checked(print_P);
}
}
void cmd_status(readline_parsed_cmd_t* cmd){
door_write_status();
printf_P(PSTR("\n"));
power_write_status();
printf_P(PSTR("\n"));
}
void cmd_reset(readline_parsed_cmd_t* cmd){
printf_P(PSTR("Restarting...\n"));
wdt_enable(WDTO_15MS);
while(1) {;}
}
void cmd_logout(readline_parsed_cmd_t* cmd){
shell_state = SHELL_STATE_READ_USER;
login_user = SHELL_USER_INVALID;
readline_set_mode(SHELL_LOGIN_PROMPT, 1 ,0);
}
void cmd_passwd(readline_parsed_cmd_t* cmd){
uint8_t user;
if (cmd->num_args){
user = shell_find_user(cmd->args[0]);
} else {
user = login_user;
}
if (user == SHELL_USER_INVALID){
printf_P(PSTR("Unknown user\n"));
return;
}
if (user != login_user && login_user != 0){
printf_P(PSTR("Must be root\n"));
return;
}
passwd_user = user;
shell_state = SHELL_STATE_PASSWD1;
readline_set_mode("New Password: ", 0 ,0);
}
void cmd_keyslot_alarm_stop(readline_parsed_cmd_t* cmd){
if (!door_clear_alarm()){
printf_P(PSTR("Error: Alarm condition still present\n"));
}
}
void cmd_date(readline_parsed_cmd_t* cmd){
if (cmd->num_args == 0){
printf_P(PSTR("Current Date: "));
} else if ((cmd->num_args == 1) || (cmd->num_args == 2)) {
date_t new_date;
uint8_t date_valid = 0;
// uint8_t time_valid = 0;
uint16_t h,m,s,d,y;
if (cmd->num_args == 2){
if (sscanf_P(cmd->args[0], PSTR("%x-%x-%x"), &d, &m, &y) == 3){
date_valid = 1;
//printf_P(PSTR("%02x-%02x-%02x "), d, m, y);
new_date.day = d;
new_date.month = m;
new_date.year = y;
} else {
printf_P(PSTR("Date '%s' invalid, use format: dd-mm-yy\n"), cmd->args[1]);
return;
}
}
if (sscanf_P(cmd->args[cmd->num_args - 1], PSTR("%x:%x:%x"), &h, &m, &s) == 3){
//printf_P(PSTR("%02x:%02x:%02x\n"), h, m, s);
new_date.hour = h;
new_date.minute = m;
new_date.second = s;
} else {
printf_P(PSTR("Time '%s' invalid, use format: hh-mm-ss\n"), cmd->args[0]);
}
if (date_valid){
ds1307_set_date(&new_date);
} else {
ds1307_set_time((time_t*)&new_date);
}
printf_P(PSTR("New Date: "));
}
ds1307_write_current_date();
printf_P(PSTR("\n"));
}
void cmd_card_init_key(readline_parsed_cmd_t* cmd){
uint16_t keyslot_index;
char* endptr;
keyslot_index = strtol(cmd->args[0], &endptr, 10);
if (*endptr != 0){//number parsing didn't reach the end of the string
printf_P(PSTR("Usage: initcardkey index\n"));
}
uint8_t slot_status = keystore_read_slot_status(keyslot_index);
if (slot_status == KEYSLOT_USED || slot_status == KEYSLOT_DISABLED){
printf_P(PSTR("Programming Keyslot %i -> Card:"),keyslot_index);
KEY key;
if (keystore_read_key(keyslot_index, key)){
cardreader_init_card_key(key);
} else {
printf_P(PSTR("Key read error\n"));
}
} else {
printf_P(PSTR("Invalid keyslot %i\n"), keyslot_index);
}
}
void cmd_card_clear_key(readline_parsed_cmd_t* cmd){
printf_P(PSTR("Clearing card key:"));
cardreader_clear_card_key();
}
uint8_t shell_find_user(char* username){
uint8_t i = 0;
while (users[i]){
if (strcmp(username, users[i]) == 0){
return i;
}
i++;
}
return SHELL_USER_INVALID;
}
uint8_t shell_check_password(uint8_t user, char* password){
HASH ref_hash;
HASH input_hash;
uint8_t pw_len = MIN(strlen(password), SHELL_MAX_PW_LEN);
uint16_t eeprom_addr = 32*user;
uint32_t salt;
eeprom_read_block(ref_hash, ((char*)eeprom_addr), sizeof(HASH));
eeprom_read_block(&salt, ((char*)eeprom_addr) + sizeof(HASH), 4);
uint8_t empty = 1;
for (uint8_t i = 0; i < sizeof(HASH); i++){
empty &= (ref_hash[i] == 0xFF);
}
if (empty) return user == 0; //if no password is set, allow root (for initial login, but not others)
memcpy(password + pw_len, &salt, 4);
sha1(&input_hash, password, (pw_len + 4) * 8);
return memcmp(ref_hash, input_hash, sizeof(HASH)) == 0;
}
void shell_set_password(uint8_t user, char* password){
HASH hash;
uint16_t eeprom_addr = 32*user;
uint8_t pw_len = MIN(strlen(password), SHELL_MAX_PW_LEN);
uint32_t salt;
rnd_get_bytes((uint8_t*)&salt, 4);
memcpy(password + pw_len, &salt, 4);
sha1(&hash, password, (pw_len+4) * 8);
eeprom_write_block(hash, ((char*)eeprom_addr), sizeof(HASH));
eeprom_write_block(&salt, ((char*)eeprom_addr) + sizeof(HASH), 4);
}
void cmd_show_log(readline_parsed_cmd_t* cmd){
log_dump();
}