shellmatta_auth.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. * Copyright (c) 2022 Stefan Strobel <stefan.strobel@shimatta.net>
  3. *
  4. * This Source Code Form is subject to the terms of the Mozilla Public
  5. * License, v. 2.0. If a copy of the MPL was not distributed with this
  6. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
  7. */
  8. /**
  9. * @file shellmatta_auth.c
  10. * @brief simple authentication method
  11. * @author Stefan Strobel <stefan.strobel@shimatta.net>
  12. */
  13. /**
  14. * @addtogroup shellmatta_auth
  15. * @{
  16. */
  17. #include "shellmatta.h"
  18. #include "shellmatta_auth.h"
  19. #include "shellmatta_history.h"
  20. #include "shellmatta_utils.h"
  21. #include "shellmatta_escape.h"
  22. #include <stdint.h>
  23. #include <stddef.h>
  24. #include <string.h>
  25. /**
  26. * @brief wraps the input to the login command and mimics the editability of the standard shell input
  27. * @param[in, out] inst shellmatta instance to work with
  28. * @param[in] data one byte of data the user put in
  29. * @param[in] hide true: do not print anything tho the output (e.g. password input)
  30. * @return #SHELLMATTA_BUSY => reading in data
  31. * #SHELLMATTA_OK => delimiter (e.g. enter) detected
  32. */
  33. shellmatta_retCode_t inputWrapper(shellmatta_instance_t *inst, char data, bool hide)
  34. {
  35. shellmatta_retCode_t ret = SHELLMATTA_BUSY;
  36. /** -# handle escape sequences */
  37. if(inst->escapeCounter != 0u)
  38. {
  39. escape_handleSequence(inst, data);
  40. }
  41. else if (inst->delimiter == data)
  42. {
  43. ret = SHELLMATTA_OK;
  44. }
  45. /** -# ignore newline as first character (to be compatible to
  46. * terminals sending newline after return */
  47. else if((0u == inst->inputCount) && ('\n' == data))
  48. {
  49. /* do nothing */
  50. }
  51. /** -# check for backspace */
  52. else if( ('\b' == data)
  53. || ('\x7f' == data))
  54. {
  55. utils_removeChars(inst, 1u, true);
  56. }
  57. /** -# check for start of escape sequence */
  58. else if('\x1b' == data)
  59. {
  60. inst->escapeCounter = 1u;
  61. }
  62. else
  63. {
  64. utils_insertChars(inst, &data, 1u, hide);
  65. }
  66. return ret;
  67. }
  68. /**
  69. * @brief searches the user list for a passed username and returns the userId
  70. * @param[in] inst shellmatta instance to work with
  71. * @param[in] username username to search for - pointer to string
  72. * @return userId or 0 if user was not found
  73. */
  74. static uint32_t getUserIdFromName(shellmatta_instance_t *inst, char *username)
  75. {
  76. uint32_t userId = 0u;
  77. uint32_t i;
  78. for (i = 0u; i < inst->userListLength; i++)
  79. {
  80. /** -# search for user */
  81. if (0 == strcmp(inst->userList[i].username, username))
  82. {
  83. userId = inst->userList[i].userId;
  84. break;
  85. }
  86. }
  87. return userId;
  88. }
  89. /**
  90. * @brief checks the passed password and logs the user into the instance on success
  91. * @param[in, out] handle shellmatta handle to work with
  92. * @param[in] userId userId to check the password for
  93. * @param[in] password password to check for - pointer to string
  94. */
  95. void checkPassword(shellmatta_handle_t handle, uint32_t userId, char *password)
  96. {
  97. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  98. uint32_t approvedUserId = 0u;
  99. uint32_t i;
  100. if (NULL != inst->checkFct)
  101. {
  102. approvedUserId = inst->checkFct(userId, password);
  103. }
  104. else
  105. {
  106. for (i = 0u; i < inst->userListLength; i++)
  107. {
  108. /** -# search for user */
  109. if (inst->userList[i].userId == userId)
  110. {
  111. /** -# check password */
  112. if (0 == strcmp(inst->userList[i].password, password))
  113. {
  114. approvedUserId = inst->userList[i].userId;
  115. }
  116. break;
  117. }
  118. }
  119. }
  120. /** -# call log function - only for unsuccessful logins - successful logins are handled by #shellmatta_auth_login */
  121. if ((inst->logFct) && (0u == approvedUserId))
  122. {
  123. inst->logFct(userId, false);
  124. }
  125. /** -# print login result */
  126. if (0 == approvedUserId)
  127. {
  128. shellmatta_write(handle, "username or password is wrong\r\n", 31);
  129. }
  130. else
  131. {
  132. shellmatta_write(handle, "login successful\r\n", 18);
  133. }
  134. (void)shellmatta_auth_login(handle, approvedUserId);
  135. }
  136. /**
  137. * @brief handles the login based on username and password
  138. * @param[in] handle handle shellmatta instance handle
  139. * @param[in] arguments arguments containing a command name or alias
  140. * @param[in] length length of the arguments
  141. * @return #SHELLMATTA_OK
  142. * #SHELLMATTA_CONTINUE (waiting for input)
  143. */
  144. static shellmatta_retCode_t loginCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
  145. {
  146. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  147. shellmatta_retCode_t ret = SHELLMATTA_OK;
  148. char option;
  149. char *argument;
  150. uint32_t argLen;
  151. char *username = NULL;
  152. char *password = NULL;
  153. static const shellmatta_opt_long_t options[] =
  154. {
  155. {"username" , 'u', SHELLMATTA_OPT_ARG_REQUIRED},
  156. {"password" , 'p', SHELLMATTA_OPT_ARG_REQUIRED},
  157. {NULL , '\0', SHELLMATTA_OPT_ARG_NONE}
  158. };
  159. /** -# handle the login state machine */
  160. switch(inst->loginState)
  161. {
  162. /** -# init the login procedure - use directly passed credentials if any */
  163. case SHELLMATTA_AUTH_IDLE:
  164. ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
  165. while(SHELLMATTA_OK == ret)
  166. {
  167. switch(option)
  168. {
  169. case 'u':
  170. if(NULL != argument)
  171. {
  172. username = argument;
  173. }
  174. break;
  175. case 'p':
  176. if(NULL != argument)
  177. {
  178. password = argument;
  179. }
  180. break;
  181. default:
  182. shellmatta_printf(handle, "Unknown option: %c\r\n", option);
  183. break;
  184. }
  185. ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
  186. }
  187. if ((NULL != username) && (NULL != password))
  188. {
  189. inst->tmpUserId = getUserIdFromName(inst, username);
  190. checkPassword(handle, inst->tmpUserId, password);
  191. }
  192. else
  193. {
  194. /** -# no credentials are passed with the command - start the input */
  195. inst->inputCount = 0u;
  196. inst->cursor = 0u;
  197. /** -# store pointer to username in buffer */
  198. shellmatta_write(handle, "enter username:\r\n", 17);
  199. inst->loginState = SHELLMATTA_AUTH_USERNAME;
  200. return SHELLMATTA_CONTINUE;
  201. }
  202. break;
  203. case SHELLMATTA_AUTH_USERNAME:
  204. /** -# read in password characters to the instance buffer */
  205. (void)shellmatta_read(handle, &argument, &argLen);
  206. if ((1 == argLen) && (SHELLMATTA_OK == inputWrapper(inst, argument[0], false)))
  207. {
  208. /** -# store user id */
  209. inst->buffer[inst->inputCount] = '\0';
  210. inst->tmpUserId = getUserIdFromName(handle, inst->buffer);
  211. /** -# reinitialize input */
  212. inst->inputCount = 0u;
  213. inst->cursor = 0u;
  214. shellmatta_write(handle, "\r\nenter password:\r\n", 19);
  215. inst->loginState = SHELLMATTA_AUTH_PASSWORD;
  216. }
  217. ret = SHELLMATTA_CONTINUE;
  218. break;
  219. case SHELLMATTA_AUTH_PASSWORD:
  220. /** -# read in password characters to the instance buffer - hiding output */
  221. (void)shellmatta_read(handle, &argument, &argLen);
  222. if ((1 == argLen) && (SHELLMATTA_OK == inputWrapper(inst, argument[0], true)))
  223. {
  224. /** -# check input username and password */
  225. inst->buffer[inst->inputCount] = '\0';
  226. shellmatta_write(handle, "\r\n", 2);
  227. checkPassword(handle, inst->tmpUserId, inst->buffer);
  228. inst->loginState = SHELLMATTA_AUTH_IDLE;
  229. }
  230. else
  231. {
  232. ret = SHELLMATTA_CONTINUE;
  233. }
  234. break;
  235. default:
  236. break;
  237. }
  238. (void)arguments;
  239. (void)length;
  240. return ret;
  241. }
  242. const shellmatta_cmd_t shellmatta_auth_loginCmd = {"login"
  243. , "li"
  244. , "Login command"
  245. , "interactive or use -u <username> -p <password>"
  246. , loginCmdFct
  247. , NULL
  248. , NULL};
  249. /**
  250. * @brief handles the logout of the current session
  251. * @param[in] handle handle shellmatta instance handle
  252. * @param[in] arguments arguments containing a command name or alias
  253. * @param[in] length length of the arguments
  254. * @return #SHELLMATTA_OK
  255. */
  256. static shellmatta_retCode_t logoutCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
  257. {
  258. shellmatta_auth_logout(handle);
  259. shellmatta_write(handle, "good bye\r\n", 10);
  260. (void)arguments;
  261. (void)length;
  262. return SHELLMATTA_OK;
  263. }
  264. const shellmatta_cmd_t shellmatta_auth_logoutCmd = {"logout"
  265. , "lo"
  266. , "Logout command"
  267. , "logs out the currently logged in user"
  268. , logoutCmdFct
  269. , NULL
  270. , NULL};
  271. /**
  272. * @brief initializes the shellmatta authentication system
  273. * @param[in] handle shellmatta instance handle
  274. * @param[in] userList list to specify all users pointer to a list of type shellmatta_user_t
  275. * @param[in] userListLength length of the user list
  276. * @param[in] permList list to specify the command permissions pointer to a list of type shellmatta_perm_t
  277. * @param[in] permListLength length of the perm list
  278. * @param[in] customLogin true: no internal login command is provided
  279. * @param[in] checkFct pointer to custom credential check function of type shellmatta_check_t
  280. * @param[in] logFct pointer to login log function of type shellmatta_auth_log_t
  281. * @return errorcode #SHELLMATTA_OK
  282. * #SHELLMATTA_USE_FAULT (param err)
  283. */
  284. shellmatta_retCode_t shellmatta_auth_init(shellmatta_handle_t handle,
  285. shellmatta_auth_user_t *userList,
  286. uint32_t userListLength,
  287. shellmatta_auth_perm_t *permList,
  288. uint32_t permListLength,
  289. bool customLogin,
  290. shellmatta_auth_check_t checkFct,
  291. shellmatta_auth_log_t logFct)
  292. {
  293. shellmatta_retCode_t ret = SHELLMATTA_OK;
  294. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  295. uint32_t i;
  296. shellmatta_cmd_t *cmd;
  297. /** -# check parameters for plausibility */
  298. if( (NULL != inst)
  299. && (SHELLMATTA_MAGIC == inst->magic)
  300. && (NULL != userList)
  301. && (0 != userListLength)
  302. && (NULL != permList)
  303. && (0 != permListLength))
  304. {
  305. inst->loginState = SHELLMATTA_AUTH_IDLE;
  306. inst->userId = 0u;
  307. inst->tmpUserId = 0u;
  308. inst->userPointer = NULL;
  309. inst->userList = userList;
  310. inst->userListLength = userListLength;
  311. inst->permList = permList;
  312. inst->permListLength = permListLength;
  313. if(customLogin)
  314. {
  315. inst->helpCmd.next = &inst->logoutCmd;
  316. }
  317. inst->checkFct = checkFct;
  318. inst->logFct = logFct;
  319. /** -# assign links to the perm list to each command */
  320. cmd = inst->cmdList;
  321. /** -# search for a matching command */
  322. while (NULL != cmd)
  323. {
  324. /** -# Search for command in perm list */
  325. for (i = 0u; i < permListLength; i++)
  326. {
  327. if (0 == strcmp(cmd->cmd, permList[i].cmd))
  328. {
  329. cmd->authLink = &permList[i];
  330. break;
  331. }
  332. }
  333. cmd = cmd->next;
  334. }
  335. }
  336. else
  337. {
  338. ret = SHELLMATTA_USE_FAULT;
  339. }
  340. return ret;
  341. }
  342. /**
  343. * @brief manually logs the passed user id in
  344. * @param[in] handle shellmatta instance handle
  345. * @param[in] userId userId to login
  346. * @return errorcode #SHELLMATTA_OK
  347. * #SHELLMATTA_USE_FAULT (param err)
  348. */
  349. shellmatta_retCode_t shellmatta_auth_login(shellmatta_handle_t handle, uint32_t userId)
  350. {
  351. shellmatta_retCode_t ret = SHELLMATTA_OK;
  352. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  353. uint32_t i;
  354. /** -# check parameters for plausibility */
  355. if( (NULL != inst)
  356. && (SHELLMATTA_MAGIC == inst->magic))
  357. {
  358. /** -# log passed user id into the instance */
  359. inst->userId = userId;
  360. if (0 != userId)
  361. {
  362. /** -# set user pointer to print the name in the prompt */
  363. for (i = 0u; i < inst->userListLength; i++)
  364. {
  365. if (inst->userList[i].userId == userId)
  366. {
  367. inst->userPointer = &inst->userList[i];
  368. break;
  369. }
  370. }
  371. }
  372. else
  373. {
  374. inst->userPointer = NULL;
  375. }
  376. /** -# call log function */
  377. if (inst->logFct)
  378. {
  379. inst->logFct(userId, userId != 0u ? true : false);
  380. }
  381. }
  382. else
  383. {
  384. ret = SHELLMATTA_USE_FAULT;
  385. }
  386. return ret;
  387. }
  388. /**
  389. * @brief logs the currently logged in user out of the instance
  390. * @param[in] handle shellmatta instance handle
  391. * @return errorcode #SHELLMATTA_OK
  392. * #SHELLMATTA_USE_FAULT (param err)
  393. */
  394. shellmatta_retCode_t shellmatta_auth_logout(shellmatta_handle_t handle)
  395. {
  396. shellmatta_retCode_t ret = SHELLMATTA_OK;
  397. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  398. /** -# check parameters for plausibility */
  399. if( (NULL != inst)
  400. && (SHELLMATTA_MAGIC == inst->magic))
  401. {
  402. inst->userId = 0u;
  403. inst->userPointer = NULL;
  404. /** -# clear the history buffer */
  405. (void)history_clear(handle);
  406. }
  407. else
  408. {
  409. ret = SHELLMATTA_USE_FAULT;
  410. }
  411. return ret;
  412. }
  413. /**
  414. * @brief returns the currently logged in userId
  415. * @param[in] handle shellmatta instance handle
  416. * @return userId or 0 if no user is logged in
  417. */
  418. uint32_t shellmatta_auth_getLoggedInUserId(shellmatta_handle_t handle)
  419. {
  420. uint32_t userId = 0u;
  421. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  422. /** -# check parameters for plausibility */
  423. if( (NULL != inst)
  424. && (SHELLMATTA_MAGIC == inst->magic))
  425. {
  426. userId = inst->userId;
  427. }
  428. return userId;
  429. }
  430. /**
  431. * @brief copies the username into the passed buffer
  432. * @param[in] handle shellmatta instance handle
  433. * @param[out] data pointer to buffer to write the username into
  434. * @param[in, out] length in - length of the passed buffer, out - actual stringlength of the username
  435. * @return errorcode #SHELLMATTA_OK
  436. * #SHELLMATTA_ERROR (passed buffer is too small)
  437. * #SHELLMATTA_USE_FAULT (param err)
  438. */
  439. shellmatta_retCode_t shellmatta_auth_getLoggedInUserName(shellmatta_handle_t handle, char *data, uint32_t *length)
  440. {
  441. shellmatta_retCode_t ret = SHELLMATTA_OK;
  442. shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
  443. size_t userNameLength;
  444. /** -# check parameters for plausibility */
  445. if( (NULL != inst)
  446. && (SHELLMATTA_MAGIC == inst->magic)
  447. && (data != NULL)
  448. && (length != NULL))
  449. {
  450. if (NULL != inst->userPointer)
  451. {
  452. userNameLength = strlen(inst->userPointer->username);
  453. if (userNameLength < *length)
  454. {
  455. (void)strcpy(data, inst->userPointer->username);
  456. *length = userNameLength;
  457. }
  458. else
  459. {
  460. ret = SHELLMATTA_ERROR;
  461. }
  462. }
  463. }
  464. else
  465. {
  466. ret = SHELLMATTA_USE_FAULT;
  467. }
  468. return ret;
  469. }
  470. /**
  471. * @brief checks if a command is accessible by the logged in user
  472. * @param[in] inst shellmatta instance to work with
  473. * @param[in] cmd command to check permissions for
  474. * @return errorcode #SHELLMATTA_OK => permitted
  475. * #SHELLMATTA_ERROR => not permitted
  476. */
  477. shellmatta_retCode_t shellmatta_auth_is_cmd_permitted(const shellmatta_instance_t *inst, shellmatta_cmd_t *cmd)
  478. {
  479. shellmatta_retCode_t ret = SHELLMATTA_ERROR;
  480. shellmatta_auth_perm_t *permList;
  481. uint32_t i;
  482. if (NULL == cmd->authLink)
  483. {
  484. ret = SHELLMATTA_OK;
  485. }
  486. else
  487. {
  488. permList = cmd->authLink;
  489. for (i = 0u; i < permList->userIdslength; i++)
  490. {
  491. if (inst->userId == permList->userIds[i])
  492. {
  493. ret = SHELLMATTA_OK;
  494. break;
  495. }
  496. }
  497. }
  498. return ret;
  499. }
  500. /**
  501. * @}
  502. */