logo

skeud

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

login.c (5104B)


  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, initgroups
  5. #define _DEFAULT_SOURCE
  6. #ifdef __linux__
  7. // I love linux extensions (no)
  8. #include <shadow.h> /* getspnam */
  9. #endif
  10. #include "common.h" // skeud_getpass, skeud_crypt_check
  11. #include <assert.h> // assert
  12. #include <errno.h> // errno
  13. #include <grp.h> // getgrnam, initgroups
  14. #include <pwd.h> // getpwnam
  15. #include <stdbool.h> // bool
  16. #include <stdio.h> // fprintf, perror
  17. #include <stdlib.h> // abort, setenv
  18. #include <string.h> // strcmp, explicit_bzero
  19. #include <sys/stat.h> // fchmod
  20. #include <unistd.h> // getuid, getopt, opt*, chdir, setuid, setgid, fchown
  21. #define TTY_GROUP "tty"
  22. #define TTY_PERMS 0600
  23. extern char **environ;
  24. char *envclear[] = {NULL};
  25. int
  26. main(int argc, char *argv[])
  27. {
  28. bool opt_f = false;
  29. bool opt_p = false;
  30. int c = EOF;
  31. char *username = NULL;
  32. struct passwd *pwent = NULL;
  33. const char *shell = "/bin/sh";
  34. if(getuid() != 0)
  35. {
  36. fprintf(stderr, "login: error: Not super-user\n");
  37. return 1;
  38. }
  39. /* flawfinder: ignore CWE-120, CWE-20 */
  40. while((c = getopt(argc, argv, ":f:p")) != EOF)
  41. {
  42. switch(c)
  43. {
  44. case 'f': // user already authenticated
  45. opt_f = true;
  46. username = optarg;
  47. break;
  48. case 'p': // preserve environment
  49. opt_p = true;
  50. break;
  51. case ':':
  52. fprintf(stderr, "login: error: Option -%c requires an operand\n", optopt);
  53. return 1;
  54. case '?':
  55. fprintf(stderr, "login: error: Unrecognized option: '-%c'\n", optopt);
  56. return 1;
  57. default:
  58. fprintf(stderr, "login: error: Unknown getopt state, aborting\n");
  59. abort();
  60. }
  61. }
  62. argc -= optind;
  63. argv += optind;
  64. if(!opt_f)
  65. {
  66. if(argc == 1)
  67. {
  68. username = argv[0];
  69. }
  70. else if(argc > 1)
  71. {
  72. fprintf(stderr, "login: error: Got %d arguments, expected <= 1\n", argc);
  73. return 1;
  74. }
  75. }
  76. else
  77. {
  78. if(argc > 0)
  79. {
  80. fprintf(stderr, "login: error: Got %d arguments, expected <= 0\n", argc);
  81. return 1;
  82. }
  83. }
  84. if(username == NULL)
  85. {
  86. size_t len = 0;
  87. printf("username: ");
  88. ssize_t got = getline(&username, &len, stdin);
  89. if(got < 0)
  90. {
  91. if(errno != 0) perror("login: error: getline");
  92. free(username);
  93. return 1;
  94. }
  95. username[got] = 0;
  96. username[got - 1] = 0;
  97. }
  98. assert(username != NULL);
  99. errno = 0;
  100. pwent = getpwnam(username);
  101. if(errno != 0)
  102. {
  103. perror("login: warning: getpwnam");
  104. }
  105. if(!opt_f)
  106. {
  107. char *pw_hash = NULL;
  108. if(pwent && pwent->pw_passwd)
  109. {
  110. pw_hash = pwent->pw_passwd;
  111. }
  112. #ifdef __linux__
  113. // Always fetched to avoid potentially leaking passwd contents
  114. errno = 0;
  115. struct spwd *swent = getspnam(username);
  116. if(errno != 0)
  117. {
  118. perror("login: getspnam");
  119. }
  120. else
  121. {
  122. if(pw_hash && strcmp(pw_hash, "x") == 0)
  123. {
  124. pw_hash = swent->sp_pwdp;
  125. }
  126. explicit_bzero(swent, sizeof(swent));
  127. swent = NULL;
  128. }
  129. #endif /* __linux__ */
  130. char *password = NULL;
  131. ssize_t got = skeud_getpass(&password);
  132. if(got < 0)
  133. {
  134. free(username);
  135. free(password);
  136. return 1;
  137. }
  138. bool valid_p = skeud_crypt_check(pw_hash, password);
  139. explicit_bzero(password, got);
  140. free(password);
  141. if(pw_hash) explicit_bzero(pw_hash, sizeof(pw_hash));
  142. if(!valid_p)
  143. {
  144. free(username);
  145. sleep(2);
  146. fprintf(stderr, "login: error: Invalid username or password\n");
  147. return 1;
  148. }
  149. }
  150. if(!opt_p)
  151. {
  152. char *term = getenv("TERM");
  153. environ = envclear;
  154. if(term)
  155. {
  156. setenv("TERM", term, 1);
  157. }
  158. }
  159. if(pwent != NULL)
  160. {
  161. int tty_gid = pwent->pw_gid;
  162. struct group *tty_group = getgrnam(TTY_GROUP);
  163. if(tty_group == NULL)
  164. {
  165. perror("login: warning: getgrnam");
  166. }
  167. else
  168. {
  169. tty_gid = tty_group->gr_gid;
  170. }
  171. /* considers that STDIN_FILENO is close enough to the current tty */
  172. if(fchown(STDIN_FILENO, pwent->pw_uid, tty_gid) < 0)
  173. {
  174. perror("login: error: fchown");
  175. return 1;
  176. }
  177. if(fchmod(STDIN_FILENO, TTY_PERMS))
  178. {
  179. perror("login: error: fchmod");
  180. return 1;
  181. }
  182. if(setgid(pwent->pw_gid) < 0)
  183. {
  184. perror("login: error: setgid");
  185. return 1;
  186. }
  187. if(initgroups(pwent->pw_name, pwent->pw_gid) < 0)
  188. {
  189. perror("login: error: initgroups");
  190. return 1;
  191. }
  192. if(setuid(pwent->pw_uid) < 0)
  193. {
  194. perror("login: error: setuid");
  195. return 1;
  196. }
  197. if(pwent->pw_shell != NULL)
  198. {
  199. shell = pwent->pw_shell;
  200. }
  201. if(pwent->pw_dir != NULL)
  202. {
  203. setenv("HOME", pwent->pw_dir, 1);
  204. if(chdir(pwent->pw_dir) != 0)
  205. {
  206. fprintf(
  207. stderr, "login: warning: Failed to change current directory to: %s\n", pwent->pw_dir);
  208. }
  209. }
  210. explicit_bzero(pwent, sizeof(pwent));
  211. pwent = NULL;
  212. }
  213. setenv("USER", username, 1);
  214. setenv("LOGNAME", username, 1);
  215. setenv("SHELL", shell, 1);
  216. setenv("IFS", " \t\n", 1);
  217. free(username);
  218. errno = 0;
  219. /* flawfinder: ignore CWE-78 */
  220. if(execl(shell, shell, "-l", NULL) < 0)
  221. {
  222. if(errno == ENOENT)
  223. {
  224. perror("login: error: execve");
  225. return 127;
  226. }
  227. else
  228. {
  229. perror("login: error: execve");
  230. return 126;
  231. }
  232. }
  233. }