123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- /*
- * Copyright (c) 2022 Stefan Strobel <stefan.strobel@shimatta.net>
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
- /**
- * @file shellmatta_auth.c
- * @brief simple authentication method
- * @author Stefan Strobel <stefan.strobel@shimatta.net>
- */
- /**
- * @addtogroup shellmatta_auth
- * @{
- */
- #include "shellmatta.h"
- #include "shellmatta_auth.h"
- #include "shellmatta_history.h"
- #include "shellmatta_utils.h"
- #include "shellmatta_escape.h"
- #include <stdint.h>
- #include <stddef.h>
- #include <string.h>
- /**
- * @brief wraps the input to the login command and mimics the editability of the standard shell input
- * @param[in, out] inst shellmatta instance to work with
- * @param[in] data one byte of data the user put in
- * @param[in] hide true: do not print anything tho the output (e.g. password input)
- * @return #SHELLMATTA_BUSY => reading in data
- * #SHELLMATTA_OK => delimiter (e.g. enter) detected
- */
- shellmatta_retCode_t inputWrapper(shellmatta_instance_t *inst, char data, bool hide)
- {
- shellmatta_retCode_t ret = SHELLMATTA_BUSY;
- /** -# handle escape sequences */
- if(inst->escapeCounter != 0u)
- {
- escape_handleSequence(inst, data);
- }
- else if (inst->delimiter == data)
- {
- ret = SHELLMATTA_OK;
- }
- /** -# ignore newline as first character (to be compatible to
- * terminals sending newline after return */
- else if((0u == inst->inputCount) && ('\n' == data))
- {
- /* do nothing */
- }
- /** -# check for backspace */
- else if( ('\b' == data)
- || ('\x7f' == data))
- {
- utils_removeChars(inst, 1u, true);
- }
- /** -# check for start of escape sequence */
- else if('\x1b' == data)
- {
- inst->escapeCounter = 1u;
- }
- else
- {
- utils_insertChars(inst, &data, 1u, hide);
- }
- return ret;
- }
- /**
- * @brief searches the user list for a passed username and returns the userId
- * @param[in] inst shellmatta instance to work with
- * @param[in] username username to search for - pointer to string
- * @return userId or 0 if user was not found
- */
- static uint32_t getUserIdFromName(shellmatta_instance_t *inst, char *username)
- {
- uint32_t userId = 0u;
- uint32_t i;
- for (i = 0u; i < inst->userListLength; i++)
- {
- /** -# search for user */
- if (0 == strcmp(inst->userList[i].username, username))
- {
- userId = inst->userList[i].userId;
- break;
- }
- }
- return userId;
- }
- /**
- * @brief checks the passed password and logs the user into the instance on success
- * @param[in, out] handle shellmatta handle to work with
- * @param[in] userId userId to check the password for
- * @param[in] password password to check for - pointer to string
- */
- void checkPassword(shellmatta_handle_t handle, uint32_t userId, char *password)
- {
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- uint32_t approvedUserId = 0u;
- uint32_t i;
- if (NULL != inst->checkFct)
- {
- approvedUserId = inst->checkFct(userId, password);
- }
- else
- {
- for (i = 0u; i < inst->userListLength; i++)
- {
- /** -# search for user */
- if (inst->userList[i].userId == userId)
- {
- /** -# check password */
- if (0 == strcmp(inst->userList[i].password, password))
- {
- approvedUserId = inst->userList[i].userId;
- }
- break;
- }
- }
- }
- /** -# call log function - only for unsuccessful logins - successful logins are handled by #shellmatta_auth_login */
- if ((inst->logFct) && (0u == approvedUserId))
- {
- inst->logFct(userId, false);
- }
- /** -# print login result */
- if (0 == approvedUserId)
- {
- shellmatta_write(handle, "username or password is wrong\r\n", 31);
- }
- else
- {
- shellmatta_write(handle, "login successful\r\n", 18);
- }
- (void)shellmatta_auth_login(handle, approvedUserId);
- }
- /**
- * @brief handles the login based on username and password
- * @param[in] handle handle shellmatta instance handle
- * @param[in] arguments arguments containing a command name or alias
- * @param[in] length length of the arguments
- * @return #SHELLMATTA_OK
- * #SHELLMATTA_CONTINUE (waiting for input)
- */
- static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
- {
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- char option;
- char *argument;
- uint32_t argLen;
- char *username = NULL;
- char *password = NULL;
- static const shellmatta_opt_long_t options[] =
- {
- {"username" , 'u', SHELLMATTA_OPT_ARG_REQUIRED},
- {"password" , 'p', SHELLMATTA_OPT_ARG_REQUIRED},
- {NULL , '\0', SHELLMATTA_OPT_ARG_NONE}
- };
- /** -# handle the login state machine */
- switch(inst->loginState)
- {
- /** -# init the login procedure - use directly passed credentials if any */
- case SHELLMATTA_AUTH_IDLE:
- ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
- while(SHELLMATTA_OK == ret)
- {
- switch(option)
- {
- case 'u':
- if(NULL != argument)
- {
- username = argument;
- }
- break;
- case 'p':
- if(NULL != argument)
- {
- password = argument;
- }
- break;
- default:
- shellmatta_printf(handle, "Unknown option: %c\r\n", option);
- break;
- }
- ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
- }
- if ((NULL != username) && (NULL != password))
- {
- inst->tmpUserId = getUserIdFromName(inst, username);
- checkPassword(handle, inst->tmpUserId, password);
- }
- else
- {
- /** -# no credentials are passed with the command - start the input */
- inst->inputCount = 0u;
- inst->cursor = 0u;
- /** -# store pointer to username in buffer */
- shellmatta_write(handle, "enter username:\r\n", 17);
- inst->loginState = SHELLMATTA_AUTH_USERNAME;
- return SHELLMATTA_CONTINUE;
- }
- break;
- case SHELLMATTA_AUTH_USERNAME:
- /** -# read in password characters to the instance buffer */
- (void)shellmatta_read(handle, &argument, &argLen);
- if ((1 == argLen) && (SHELLMATTA_OK == inputWrapper(inst, argument[0], false)))
- {
- /** -# store user id */
- inst->buffer[inst->inputCount] = '\0';
- inst->tmpUserId = getUserIdFromName(handle, inst->buffer);
- /** -# reinitialize input */
- inst->inputCount = 0u;
- inst->cursor = 0u;
- shellmatta_write(handle, "\r\nenter password:\r\n", 19);
- inst->loginState = SHELLMATTA_AUTH_PASSWORD;
- }
- ret = SHELLMATTA_CONTINUE;
- break;
- case SHELLMATTA_AUTH_PASSWORD:
- /** -# read in password characters to the instance buffer - hiding output */
- (void)shellmatta_read(handle, &argument, &argLen);
- if ((1 == argLen) && (SHELLMATTA_OK == inputWrapper(inst, argument[0], true)))
- {
- /** -# check input username and password */
- inst->buffer[inst->inputCount] = '\0';
- shellmatta_write(handle, "\r\n", 2);
- checkPassword(handle, inst->tmpUserId, inst->buffer);
- inst->loginState = SHELLMATTA_AUTH_IDLE;
- }
- else
- {
- ret = SHELLMATTA_CONTINUE;
- }
- break;
- default:
- break;
- }
- (void)arguments;
- (void)length;
- return ret;
- }
- const shellmatta_cmd_t shellmatta_auth_loginCmd = {"login"
- , "li"
- , "Login command"
- , "interactive or use -u <username> -p <password>"
- , loginCmdFct
- , NULL
- , NULL};
- /**
- * @brief handles the logout of the current session
- * @param[in] handle handle shellmatta instance handle
- * @param[in] arguments arguments containing a command name or alias
- * @param[in] length length of the arguments
- * @return #SHELLMATTA_OK
- */
- static shellmatta_retCode_t logoutCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
- {
- shellmatta_auth_logout(handle);
- shellmatta_write(handle, "good bye\r\n", 10);
- (void)arguments;
- (void)length;
- return SHELLMATTA_OK;
- }
- const shellmatta_cmd_t shellmatta_auth_logoutCmd = {"logout"
- , "lo"
- , "Logout command"
- , "logs out the currently logged in user"
- , logoutCmdFct
- , NULL
- , NULL};
- /**
- * @brief initializes the shellmatta authentication system
- * @param[in] handle shellmatta instance handle
- * @param[in] userList list to specify all users pointer to a list of type shellmatta_user_t
- * @param[in] userListLength length of the user list
- * @param[in] permList list to specify the command permissions pointer to a list of type shellmatta_perm_t
- * @param[in] permListLength length of the perm list
- * @param[in] customLogin true: no internal login command is provided
- * @param[in] checkFct pointer to custom credential check function of type shellmatta_check_t
- * @param[in] logFct pointer to login log function of type shellmatta_auth_log_t
- * @return errorcode #SHELLMATTA_OK
- * #SHELLMATTA_USE_FAULT (param err)
- */
- shellmatta_retCode_t shellmatta_auth_init(shellmatta_handle_t handle,
- shellmatta_auth_user_t *userList,
- uint32_t userListLength,
- shellmatta_auth_perm_t *permList,
- uint32_t permListLength,
- bool customLogin,
- shellmatta_auth_check_t checkFct,
- shellmatta_auth_log_t logFct)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- uint32_t i;
- shellmatta_cmd_t *cmd;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic)
- && (NULL != userList)
- && (0 != userListLength)
- && (NULL != permList)
- && (0 != permListLength))
- {
- inst->loginState = SHELLMATTA_AUTH_IDLE;
- inst->userId = 0u;
- inst->tmpUserId = 0u;
- inst->userPointer = NULL;
- inst->userList = userList;
- inst->userListLength = userListLength;
- inst->permList = permList;
- inst->permListLength = permListLength;
- if(customLogin)
- {
- inst->helpCmd.next = &inst->logoutCmd;
- }
- inst->checkFct = checkFct;
- inst->logFct = logFct;
- /** -# assign links to the perm list to each command */
- cmd = inst->cmdList;
- /** -# search for a matching command */
- while (NULL != cmd)
- {
- /** -# Search for command in perm list */
- for (i = 0u; i < permListLength; i++)
- {
- if (0 == strcmp(cmd->cmd, permList[i].cmd))
- {
- cmd->authLink = &permList[i];
- break;
- }
- }
- cmd = cmd->next;
- }
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- /**
- * @brief manually logs the passed user id in
- * @param[in] handle shellmatta instance handle
- * @param[in] userId userId to login
- * @return errorcode #SHELLMATTA_OK
- * #SHELLMATTA_USE_FAULT (param err)
- */
- shellmatta_retCode_t shellmatta_auth_login(shellmatta_handle_t handle, uint32_t userId)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- uint32_t i;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic))
- {
- /** -# log passed user id into the instance */
- inst->userId = userId;
- if (0 != userId)
- {
- /** -# set user pointer to print the name in the prompt */
- for (i = 0u; i < inst->userListLength; i++)
- {
- if (inst->userList[i].userId == userId)
- {
- inst->userPointer = &inst->userList[i];
- break;
- }
- }
- }
- else
- {
- inst->userPointer = NULL;
- }
- /** -# call log function */
- if (inst->logFct)
- {
- inst->logFct(userId, userId != 0u ? true : false);
- }
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- /**
- * @brief logs the currently logged in user out of the instance
- * @param[in] handle shellmatta instance handle
- * @return errorcode #SHELLMATTA_OK
- * #SHELLMATTA_USE_FAULT (param err)
- */
- shellmatta_retCode_t shellmatta_auth_logout(shellmatta_handle_t handle)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic))
- {
- inst->userId = 0u;
- inst->userPointer = NULL;
- /** -# clear the history buffer */
- (void)history_clear(handle);
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- /**
- * @brief returns the currently logged in userId
- * @param[in] handle shellmatta instance handle
- * @return userId or 0 if no user is logged in
- */
- uint32_t shellmatta_auth_getLoggedInUserId(shellmatta_handle_t handle)
- {
- uint32_t userId = 0u;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic))
- {
- userId = inst->userId;
- }
- return userId;
- }
- /**
- * @brief copies the username into the passed buffer
- * @param[in] handle shellmatta instance handle
- * @param[out] data pointer to buffer to write the username into
- * @param[in, out] length in - length of the passed buffer, out - actual stringlength of the username
- * @return errorcode #SHELLMATTA_OK
- * #SHELLMATTA_ERROR (passed buffer is too small)
- * #SHELLMATTA_USE_FAULT (param err)
- */
- shellmatta_retCode_t shellmatta_auth_getLoggedInUserName(shellmatta_handle_t handle, char *data, uint32_t *length)
- {
- shellmatta_retCode_t ret = SHELLMATTA_OK;
- shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
- size_t userNameLength;
- /** -# check parameters for plausibility */
- if( (NULL != inst)
- && (SHELLMATTA_MAGIC == inst->magic)
- && (data != NULL)
- && (length != NULL))
- {
- if (NULL != inst->userPointer)
- {
- userNameLength = strlen(inst->userPointer->username);
- if (userNameLength < *length)
- {
- (void)strcpy(data, inst->userPointer->username);
- *length = userNameLength;
- }
- else
- {
- ret = SHELLMATTA_ERROR;
- }
- }
- }
- else
- {
- ret = SHELLMATTA_USE_FAULT;
- }
- return ret;
- }
- /**
- * @brief checks if a command is accessible by the logged in user
- * @param[in] inst shellmatta instance to work with
- * @param[in] cmd command to check permissions for
- * @return errorcode #SHELLMATTA_OK => permitted
- * #SHELLMATTA_ERROR => not permitted
- */
- shellmatta_retCode_t shellmatta_auth_is_cmd_permitted(const shellmatta_instance_t *inst, shellmatta_cmd_t *cmd)
- {
- shellmatta_retCode_t ret = SHELLMATTA_ERROR;
- shellmatta_auth_perm_t *permList;
- uint32_t i;
- if (NULL == cmd->authLink)
- {
- ret = SHELLMATTA_OK;
- }
- else
- {
- permList = cmd->authLink;
- for (i = 0u; i < permList->userIdslength; i++)
- {
- if (inst->userId == permList->userIds[i])
- {
- ret = SHELLMATTA_OK;
- break;
- }
- }
- }
- return ret;
- }
- /**
- * @}
- */
|