logo

skeud

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

common.c (2729B)


  1. // SPDX-FileCopyrightText: 2022 Haelwenn (lanodan) Monnier <contact+skeud@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _POSIX_C_SOURCE 202405L
  4. // for explicit_bzero
  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. #undef MIN
  13. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  14. // doesn't exits to make sure cleanup is done
  15. int
  16. skeud_err(const char *errmsg, int err)
  17. {
  18. fputs(argv0, stderr);
  19. fputs(": error: ", stderr);
  20. fputs(errmsg, stderr);
  21. if(err != 0)
  22. {
  23. fputs(": ", stderr);
  24. fputs(strerror(err), stderr);
  25. }
  26. fputs("\n", stderr);
  27. return fflush(stderr);
  28. }
  29. // Needs to be constant-time
  30. bool
  31. hash_match(const char *a, size_t alen, const char *b, size_t blen)
  32. {
  33. size_t n = MIN(alen, blen);
  34. bool ret = true;
  35. for(size_t i = 0; i < n; i++)
  36. if(a[i] != b[i]) ret = false;
  37. if(alen != blen)
  38. return false;
  39. else
  40. return ret;
  41. }
  42. bool
  43. skeud_crypt_check(const char *hash, const char *password)
  44. {
  45. size_t hashlen = strlen0(hash);
  46. if(hashlen == 0) return false;
  47. /* flawfinder: ignore CWE-327, crypt is the only relevant function */
  48. char *chk_hash = crypt(password, hash);
  49. if(chk_hash == NULL)
  50. {
  51. skeud_err("Failed hashing password", errno);
  52. return false;
  53. }
  54. bool match = hash_match(hash, hashlen, chk_hash, strlen(chk_hash));
  55. // cleanup
  56. chk_hash = crypt("", hash);
  57. return match;
  58. }
  59. ssize_t
  60. skeud_getpass(char **password)
  61. {
  62. struct termios t;
  63. size_t len = 0;
  64. ssize_t got = -1;
  65. /* flawfinder: ignore CWE-362 */
  66. FILE *tty = fopen("/dev/tty", "rb+");
  67. if(tty == NULL)
  68. {
  69. skeud_err("Failed opening '/dev/tty'", errno);
  70. return got;
  71. }
  72. int tty_fd = fileno(tty);
  73. if(tty_fd < 0)
  74. {
  75. skeud_err("Failed getting fd from '/dev/tty' stream", errno);
  76. goto getpass_end;
  77. }
  78. if(tcgetattr(tty_fd, &t) < 0)
  79. {
  80. skeud_err("Failed getting terminal attributes", errno);
  81. goto getpass_end;
  82. }
  83. const char *prompt = "Password: ";
  84. write(tty_fd, prompt, strlen(prompt));
  85. t.c_lflag &= ~ECHO;
  86. if(tcsetattr(tty_fd, TCSANOW, &t) < 0)
  87. {
  88. skeud_err("Failed disabling terminal echo", errno);
  89. goto getpass_end;
  90. }
  91. errno = 0;
  92. got = getline(password, &len, tty);
  93. if(got < 0)
  94. {
  95. if(errno != 0) skeud_err("Failed reading line", errno);
  96. goto getpass_clean;
  97. }
  98. fputs("\n", tty);
  99. (*password)[got] = 0;
  100. (*password)[got - 1] = 0;
  101. got--;
  102. getpass_clean:
  103. t.c_lflag ^= ECHO;
  104. if(tcsetattr(tty_fd, TCSANOW, &t) < 0)
  105. {
  106. skeud_err("Failed re-enabling terminal echo", errno);
  107. explicit_bzero(password, got);
  108. got = -1;
  109. goto getpass_end;
  110. }
  111. getpass_end:
  112. fclose(tty);
  113. return got;
  114. }