logo

skeud

Simple and portable utilities to deal with user accounts (su, login)

common.c (2444B)


  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. size_t
  13. smin(size_t a, size_t b)
  14. {
  15. return a < b ? a : b;
  16. }
  17. // Needs to be constant-time so the hash can't be guessed via using a rainbow-table
  18. bool
  19. hash_match(char *a, char *b)
  20. {
  21. /* flawfinder: ignore CWE-126 */
  22. size_t len_a = strlen(a);
  23. /* flawfinder: ignore CWE-126 */
  24. size_t len_b = strlen(b);
  25. size_t n = smin(len_a, len_b);
  26. bool ret = true;
  27. for(size_t i = 0; i < n; i++)
  28. if(a[i] != b[i]) ret = false;
  29. if(len_a != len_b)
  30. return false;
  31. else
  32. return ret;
  33. }
  34. bool
  35. skeud_crypt_check(char *hash, char *password)
  36. {
  37. if(!hash) return false;
  38. if(strcmp(hash, "") == 0) return false;
  39. /* flawfinder: ignore CWE-327, crypt is the only relevant function */
  40. char *chk_hash = crypt(password, hash);
  41. if(chk_hash == NULL)
  42. {
  43. perror("skeud_crypt_check: crypt");
  44. return false;
  45. }
  46. bool match = hash_match(hash, chk_hash);
  47. explicit_bzero(chk_hash, sizeof(chk_hash));
  48. return match;
  49. }
  50. ssize_t
  51. skeud_getpass(char **password)
  52. {
  53. struct termios t;
  54. size_t len = 0;
  55. ssize_t got = -1;
  56. /* flawfinder: ignore CWE-362 */
  57. FILE *tty = fopen("/dev/tty", "rb+");
  58. if(tty == NULL)
  59. {
  60. perror("skeud_getpass: open(\"/dev/tty\")");
  61. return got;
  62. }
  63. int tty_fd = fileno(tty);
  64. if(tty_fd < 0)
  65. {
  66. perror("skeud_getpass: fileno(tty)");
  67. goto getpass_end;
  68. }
  69. if(tcgetattr(tty_fd, &t) < 0)
  70. {
  71. perror("skeud_getpass: tcgetattr");
  72. goto getpass_end;
  73. }
  74. fprintf(tty, "Password: ");
  75. t.c_lflag &= ~ECHO;
  76. if(tcsetattr(tty_fd, TCSANOW, &t) < 0)
  77. {
  78. perror("skeud_getpass: tcsetattr(~ECHO)");
  79. goto getpass_end;
  80. }
  81. errno = 0;
  82. got = getline(password, &len, tty);
  83. fprintf(tty, "\n");
  84. if(got < 0)
  85. {
  86. if(errno != 0) perror("skeud_getpass: getline");
  87. goto getpass_clean;
  88. }
  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: 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. }