Account.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Base64.h>
  7. #include <AK/Random.h>
  8. #include <AK/ScopeGuard.h>
  9. #include <LibCore/Account.h>
  10. #include <LibCore/File.h>
  11. #ifndef AK_OS_MACOS
  12. # include <crypt.h>
  13. #endif
  14. #include <errno.h>
  15. #include <grp.h>
  16. #include <pwd.h>
  17. #ifndef AK_OS_MACOS
  18. # include <shadow.h>
  19. #else
  20. # include <LibC/shadow.h>
  21. #endif
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <sys/stat.h>
  25. #include <unistd.h>
  26. namespace Core {
  27. static String get_salt()
  28. {
  29. char random_data[12];
  30. fill_with_random(random_data, sizeof(random_data));
  31. StringBuilder builder;
  32. builder.append("$5$");
  33. builder.append(encode_base64(ReadonlyBytes(random_data, sizeof(random_data))));
  34. return builder.build();
  35. }
  36. static Vector<gid_t> get_extra_gids(const passwd& pwd)
  37. {
  38. StringView username { pwd.pw_name };
  39. Vector<gid_t> extra_gids;
  40. setgrent();
  41. for (auto* group = getgrent(); group; group = getgrent()) {
  42. if (group->gr_gid == pwd.pw_gid)
  43. continue;
  44. for (size_t i = 0; group->gr_mem[i]; ++i) {
  45. if (username == group->gr_mem[i]) {
  46. extra_gids.append(group->gr_gid);
  47. break;
  48. }
  49. }
  50. }
  51. endgrent();
  52. return extra_gids;
  53. }
  54. Result<Account, String> Account::from_passwd(const passwd& pwd, const spwd& spwd)
  55. {
  56. Account account(pwd, spwd, get_extra_gids(pwd));
  57. endpwent();
  58. #ifndef AK_OS_MACOS
  59. endspent();
  60. #endif
  61. return account;
  62. }
  63. Result<Account, String> Account::from_name(const char* username)
  64. {
  65. errno = 0;
  66. auto* pwd = getpwnam(username);
  67. if (!pwd) {
  68. if (errno == 0)
  69. return String("No such user");
  70. return String(strerror(errno));
  71. }
  72. spwd spwd_dummy = {};
  73. spwd_dummy.sp_namp = const_cast<char*>(username);
  74. spwd_dummy.sp_pwdp = const_cast<char*>("");
  75. #ifndef AK_OS_MACOS
  76. auto* spwd = getspnam(username);
  77. if (!spwd)
  78. spwd = &spwd_dummy;
  79. #else
  80. auto* spwd = &spwd_dummy;
  81. #endif
  82. return from_passwd(*pwd, *spwd);
  83. }
  84. Result<Account, String> Account::from_uid(uid_t uid)
  85. {
  86. errno = 0;
  87. auto* pwd = getpwuid(uid);
  88. if (!pwd) {
  89. if (errno == 0)
  90. return String("No such user");
  91. return String(strerror(errno));
  92. }
  93. spwd spwd_dummy = {};
  94. spwd_dummy.sp_namp = pwd->pw_name;
  95. spwd_dummy.sp_pwdp = const_cast<char*>("");
  96. #ifndef AK_OS_MACOS
  97. auto* spwd = getspnam(pwd->pw_name);
  98. if (!spwd)
  99. spwd = &spwd_dummy;
  100. #else
  101. auto* spwd = &spwd_dummy;
  102. #endif
  103. return from_passwd(*pwd, *spwd);
  104. }
  105. bool Account::authenticate(const char* password) const
  106. {
  107. // If there was no shadow entry for this account, authentication always fails.
  108. if (m_password_hash.is_null())
  109. return false;
  110. // An empty passwd field indicates that no password is required to log in.
  111. if (m_password_hash.is_empty())
  112. return true;
  113. // FIXME: Use crypt_r if it can be built in lagom.
  114. char* hash = crypt(password, m_password_hash.characters());
  115. return hash != nullptr && strcmp(hash, m_password_hash.characters()) == 0;
  116. }
  117. bool Account::login() const
  118. {
  119. if (setgroups(m_extra_gids.size(), m_extra_gids.data()) < 0)
  120. return false;
  121. if (setgid(m_gid) < 0)
  122. return false;
  123. if (setuid(m_uid) < 0)
  124. return false;
  125. return true;
  126. }
  127. void Account::set_password(const char* password)
  128. {
  129. m_password_hash = crypt(password, get_salt().characters());
  130. }
  131. void Account::set_password_enabled(bool enabled)
  132. {
  133. if (enabled && m_password_hash != "" && m_password_hash[0] == '!') {
  134. m_password_hash = m_password_hash.substring(1, m_password_hash.length() - 1);
  135. } else if (!enabled && (m_password_hash == "" || m_password_hash[0] != '!')) {
  136. StringBuilder builder;
  137. builder.append('!');
  138. builder.append(m_password_hash);
  139. m_password_hash = builder.build();
  140. }
  141. }
  142. void Account::delete_password()
  143. {
  144. m_password_hash = "";
  145. }
  146. Account::Account(const passwd& pwd, const spwd& spwd, Vector<gid_t> extra_gids)
  147. : m_username(pwd.pw_name)
  148. , m_password_hash(spwd.sp_pwdp)
  149. , m_uid(pwd.pw_uid)
  150. , m_gid(pwd.pw_gid)
  151. , m_gecos(pwd.pw_gecos)
  152. , m_home_directory(pwd.pw_dir)
  153. , m_shell(pwd.pw_shell)
  154. , m_extra_gids(move(extra_gids))
  155. {
  156. }
  157. String Account::generate_passwd_file() const
  158. {
  159. StringBuilder builder;
  160. setpwent();
  161. struct passwd* p;
  162. errno = 0;
  163. while ((p = getpwent())) {
  164. if (p->pw_uid == m_uid) {
  165. builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
  166. m_username,
  167. m_uid, m_gid,
  168. m_gecos,
  169. m_home_directory,
  170. m_shell);
  171. } else {
  172. builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
  173. p->pw_name, p->pw_uid,
  174. p->pw_gid, p->pw_gecos, p->pw_dir,
  175. p->pw_shell);
  176. }
  177. }
  178. endpwent();
  179. if (errno) {
  180. dbgln("errno was non-zero after generating new passwd file.");
  181. return {};
  182. }
  183. return builder.to_string();
  184. }
  185. #ifndef AK_OS_MACOS
  186. String Account::generate_shadow_file() const
  187. {
  188. StringBuilder builder;
  189. setspent();
  190. struct spwd* p;
  191. errno = 0;
  192. while ((p = getspent())) {
  193. if (p->sp_namp == m_username) {
  194. builder.appendff("{}:{}:{}:{}:{}:{}:{}:{}:{}\n",
  195. m_username, m_password_hash,
  196. p->sp_lstchg, p->sp_min,
  197. p->sp_max, p->sp_warn,
  198. p->sp_inact, p->sp_expire,
  199. p->sp_flag);
  200. } else {
  201. builder.appendff("{}:{}:{}:{}:{}:{}:{}:{}:{}\n",
  202. p->sp_namp, p->sp_pwdp,
  203. p->sp_lstchg, p->sp_min,
  204. p->sp_max, p->sp_warn,
  205. p->sp_inact, p->sp_expire,
  206. p->sp_flag);
  207. }
  208. }
  209. endspent();
  210. if (errno) {
  211. dbgln("errno was non-zero after generating new passwd file.");
  212. return {};
  213. }
  214. return builder.to_string();
  215. }
  216. #endif
  217. bool Account::sync()
  218. {
  219. auto new_passwd_file_content = generate_passwd_file();
  220. VERIFY(!new_passwd_file_content.is_null());
  221. #ifndef AK_OS_MACOS
  222. auto new_shadow_file_content = generate_shadow_file();
  223. VERIFY(!new_shadow_file_content.is_null());
  224. #endif
  225. char new_passwd_name[] = "/etc/passwd.XXXXXX";
  226. #ifndef AK_OS_MACOS
  227. char new_shadow_name[] = "/etc/shadow.XXXXXX";
  228. #endif
  229. {
  230. auto new_passwd_fd = mkstemp(new_passwd_name);
  231. if (new_passwd_fd < 0) {
  232. perror("mkstemp");
  233. VERIFY_NOT_REACHED();
  234. }
  235. ScopeGuard new_passwd_fd_guard = [new_passwd_fd] { close(new_passwd_fd); };
  236. #ifndef AK_OS_MACOS
  237. auto new_shadow_fd = mkstemp(new_shadow_name);
  238. if (new_shadow_fd < 0) {
  239. perror("mkstemp");
  240. VERIFY_NOT_REACHED();
  241. }
  242. ScopeGuard new_shadow_fd_guard = [new_shadow_fd] { close(new_shadow_fd); };
  243. #endif
  244. if (fchmod(new_passwd_fd, 0644) < 0) {
  245. perror("fchmod");
  246. VERIFY_NOT_REACHED();
  247. }
  248. auto nwritten = write(new_passwd_fd, new_passwd_file_content.characters(), new_passwd_file_content.length());
  249. if (nwritten < 0) {
  250. perror("write");
  251. VERIFY_NOT_REACHED();
  252. }
  253. VERIFY(static_cast<size_t>(nwritten) == new_passwd_file_content.length());
  254. #ifndef AK_OS_MACOS
  255. nwritten = write(new_shadow_fd, new_shadow_file_content.characters(), new_shadow_file_content.length());
  256. if (nwritten < 0) {
  257. perror("write");
  258. VERIFY_NOT_REACHED();
  259. }
  260. VERIFY(static_cast<size_t>(nwritten) == new_shadow_file_content.length());
  261. #endif
  262. }
  263. if (rename(new_passwd_name, "/etc/passwd") < 0) {
  264. perror("Failed to install new /etc/passwd");
  265. return false;
  266. }
  267. #ifndef AK_OS_MACOS
  268. if (rename(new_shadow_name, "/etc/shadow") < 0) {
  269. perror("Failed to install new /etc/shadow");
  270. return false;
  271. }
  272. #endif
  273. return true;
  274. // FIXME: Sync extra groups.
  275. }
  276. }