logo

skeud

Simple and portable utilities to deal with user accounts (su, login)git clone https://anongit.hacktivis.me/git/skeud.git/

common.c (2561B)


  1. // SPDX-FileCopyrightText: 2022 Haelwenn (lanodan) Monnier <contact+skeud@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _POSIX_C_SOURCE 200809L
  4. // for explicit_bzero and termios
  5. #define _DEFAULT_SOURCE
  6. #include "common.h"
  7. #include <errno.h> // errno
  8. #include <stdio.h> // fclose, fopen, fprintf, perror, getline, fileno
  9. #include <string.h> // explicit_bzero
  10. #include <termios.h> // tcgetattr, tcsetattr
  11. #include <unistd.h> // crypt
  12. #ifndef MIN
  13. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  14. #endif
  15. // Needs to be constant-time so the hash can't be guessed via using a rainbow-table
  16. bool
  17. hash_match(const char *a, const char *b)
  18. {
  19. /* flawfinder: ignore CWE-126 */
  20. size_t len_a = strlen(a);
  21. /* flawfinder: ignore CWE-126 */
  22. size_t len_b = strlen(b);
  23. size_t n = MIN(len_a, len_b);
  24. bool ret = true;
  25. for(size_t i = 0; i < n; i++)
  26. if(a[i] != b[i]) ret = false;
  27. if(len_a != len_b)
  28. return false;
  29. else
  30. return ret;
  31. }
  32. bool
  33. skeud_crypt_check(const char *hash, const char *password)
  34. {
  35. if(!hash) return false;
  36. if(strcmp(hash, "") == 0) return false;
  37. /* flawfinder: ignore CWE-327, crypt is the only relevant function */
  38. char *chk_hash = crypt(password, hash);
  39. if(chk_hash == NULL)
  40. {
  41. perror("skeud_crypt_check: error: crypt");
  42. return false;
  43. }
  44. bool match = hash_match(hash, chk_hash);
  45. // cleanup
  46. chk_hash = crypt("", hash);
  47. return match;
  48. }
  49. ssize_t
  50. skeud_getpass(char **password)
  51. {
  52. struct termios t;
  53. size_t len = 0;
  54. ssize_t got = -1;
  55. /* flawfinder: ignore CWE-362 */
  56. FILE *tty = fopen("/dev/tty", "rb+");
  57. if(tty == NULL)
  58. {
  59. perror("skeud_getpass: error: open(\"/dev/tty\")");
  60. return got;
  61. }
  62. int tty_fd = fileno(tty);
  63. if(tty_fd < 0)
  64. {
  65. perror("skeud_getpass: error: fileno(tty)");
  66. goto getpass_end;
  67. }
  68. if(tcgetattr(tty_fd, &t) < 0)
  69. {
  70. perror("skeud_getpass: error: tcgetattr");
  71. goto getpass_end;
  72. }
  73. const char *prompt = "Password: ";
  74. write(tty_fd, prompt, strlen(prompt));
  75. t.c_lflag &= ~ECHO;
  76. if(tcsetattr(tty_fd, TCSANOW, &t) < 0)
  77. {
  78. perror("skeud_getpass: error: tcsetattr(~ECHO)");
  79. goto getpass_end;
  80. }
  81. errno = 0;
  82. got = getline(password, &len, tty);
  83. if(got < 0)
  84. {
  85. if(errno != 0) perror("skeud_getpass: error: getline");
  86. goto getpass_clean;
  87. }
  88. fputs("\n", tty);
  89. (*password)[got] = 0;
  90. (*password)[got - 1] = 0;
  91. got--;
  92. getpass_clean:
  93. t.c_lflag ^= ECHO;
  94. if(tcsetattr(tty_fd, TCSANOW, &t) < 0)
  95. {
  96. perror("skeud_getpass: error: tcsetattr(ECHO)");
  97. explicit_bzero(password, got);
  98. got = -1;
  99. goto getpass_end;
  100. }
  101. getpass_end:
  102. fclose(tty);
  103. return got;
  104. }