logo

skeud

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

suc.c (4818B)


  1. // SPDX-FileCopyrightText: 2022-2023 Haelwenn (lanodan) Monnier <contact+skeud@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _POSIX_C_SOURCE 202405L
  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> // initgroups
  14. #include <limits.h> // NAME_MAX
  15. #include <pwd.h> // getpwnam
  16. #include <stdbool.h> // bool
  17. #include <stdio.h> // fprintf, perror
  18. #include <stdlib.h> // abort, setenv
  19. #include <string.h> // strcmp, explicit_bzero
  20. #include <unistd.h> // getuid, getopt, opt*, chdir, setuid, setgid
  21. extern char **environ;
  22. char *envclear[] = {NULL};
  23. const char *argv0 = "suc";
  24. int
  25. main(int argc, char *argv[])
  26. {
  27. bool opt_l = false;
  28. bool opt_p = false;
  29. const char *username = "root";
  30. struct passwd *pwent = NULL;
  31. char *shell = NULL;
  32. if(geteuid() != 0)
  33. {
  34. fputs("suc: error: Not effectively super-user. Missing setuid?\n", stderr);
  35. return 1;
  36. }
  37. int c = EOF;
  38. /* flawfinder: ignore CWE-120, CWE-20 */
  39. while((c = getopt(argc, argv, ":ls:pu:")) != EOF)
  40. {
  41. switch(c)
  42. {
  43. case 'l': // login-mode
  44. opt_l = true;
  45. break;
  46. case 's': // shell
  47. if(getuid() != 0)
  48. {
  49. fputs("suc: error: Only the super-user can override the target shell\n", stderr);
  50. return 1;
  51. }
  52. shell = optarg;
  53. break;
  54. case 'p': // preserve environment
  55. if(getuid() != 0)
  56. {
  57. fputs("suc: error: Only the super-user can preserve the environment\n", stderr);
  58. return 1;
  59. }
  60. opt_p = true;
  61. break;
  62. case 'u': // username
  63. username = optarg;
  64. break;
  65. case ':':
  66. fprintf(stderr, "suc: error: Option -%c requires an operand\n", optopt);
  67. return 1;
  68. case '?':
  69. fprintf(stderr, "suc: error: Unrecognized option: '-%c'\n", optopt);
  70. return 1;
  71. default:
  72. fputs("suc: error: Unknown getopt state, aborting\n", stderr);
  73. abort();
  74. }
  75. }
  76. argc -= optind;
  77. argv += optind;
  78. errno = 0;
  79. pwent = getpwnam(username);
  80. if(pwent == NULL)
  81. {
  82. if(errno != 0)
  83. {
  84. perror("suc: error: Failed getting passwd entry");
  85. }
  86. else
  87. {
  88. fprintf(stderr, "suc: error: getpwnam: No entry found for user %s\n", username);
  89. }
  90. return 1;
  91. }
  92. if(shell == NULL)
  93. {
  94. if(pwent->pw_shell)
  95. {
  96. shell = pwent->pw_shell;
  97. }
  98. else
  99. {
  100. fprintf(stderr, "suc: error: No shell entry for user %s\n", username);
  101. return 1;
  102. }
  103. }
  104. fprintf(stderr, "suc: info: Authenticating as %s\n", username);
  105. if(getuid() != 0)
  106. {
  107. char *pw_hash = NULL;
  108. if(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("suc: warning: getspnam");
  119. }
  120. else
  121. {
  122. if(pw_hash && pw_hash[0] == 'x' && pw_hash[1] == 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(password);
  135. return 1;
  136. }
  137. bool valid_p = skeud_crypt_check(pw_hash, password);
  138. explicit_bzero(password, got);
  139. free(password);
  140. if(pw_hash) explicit_bzero(pw_hash, sizeof(pw_hash));
  141. if(!valid_p)
  142. {
  143. sleep(2);
  144. fprintf(stderr, "suc: error: Invalid username or password\n");
  145. return 1;
  146. }
  147. }
  148. if(!opt_p)
  149. {
  150. char *term = getenv("TERM");
  151. environ = envclear;
  152. if(term)
  153. {
  154. setenv("TERM", term, 1);
  155. }
  156. }
  157. if(setgid(pwent->pw_gid) < 0)
  158. {
  159. perror("suc: error: setgid");
  160. return 1;
  161. }
  162. if(initgroups(username, pwent->pw_gid) < 0)
  163. {
  164. perror("suc: error: initgroups");
  165. return 1;
  166. }
  167. if(setuid(pwent->pw_uid) < 0)
  168. {
  169. perror("suc: error: setuid");
  170. return 1;
  171. }
  172. const char *home = pwent->pw_dir ? pwent->pw_dir : "/";
  173. setenv("HOME", home, 1);
  174. static char shell0[NAME_MAX] = "sh";
  175. if(opt_l)
  176. {
  177. const char *sh_basename = strrchr(shell, '/');
  178. sh_basename = sh_basename ? sh_basename + 1 : shell;
  179. strlcpy(shell0, "-sh", NAME_MAX - 1);
  180. strlcpy(shell0 + 1, sh_basename, NAME_MAX - 1);
  181. if(chdir(home) != 0) perror("suc: chdir");
  182. }
  183. explicit_bzero(pwent, sizeof(pwent));
  184. pwent = NULL;
  185. setenv("USER", username, 1);
  186. setenv("LOGNAME", username, 1);
  187. setenv("SHELL", shell, 1);
  188. setenv("IFS", " \t\n", 1);
  189. static char *args_shell0[] = {shell0, NULL};
  190. char **args = argc > 0 ? argv : args_shell0;
  191. char *arg0 = argc > 0 ? argv[0] : shell;
  192. errno = 0;
  193. /* flawfinder: ignore CWE-78 */
  194. int ret = execvp(arg0, args);
  195. if(ret < 0)
  196. {
  197. if(errno == ENOENT)
  198. {
  199. perror("suc: execve");
  200. return 127;
  201. }
  202. else
  203. {
  204. perror("suc: execve");
  205. return 126;
  206. }
  207. }
  208. }