Account.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /*
  2. * Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
  3. * Copyright (c) 2021-2022, Brian Gianforcaro <bgianf@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Base64.h>
  8. #include <AK/Memory.h>
  9. #include <AK/Random.h>
  10. #include <AK/ScopeGuard.h>
  11. #include <LibCore/Account.h>
  12. #include <LibCore/Directory.h>
  13. #include <LibCore/System.h>
  14. #include <LibCore/UmaskScope.h>
  15. #include <errno.h>
  16. #include <grp.h>
  17. #include <pwd.h>
  18. #ifndef AK_OS_BSD_GENERIC
  19. # include <crypt.h>
  20. # include <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$"sv);
  33. builder.append(encode_base64(ReadonlyBytes(random_data, sizeof(random_data))));
  34. return builder.build();
  35. }
  36. static Vector<gid_t> get_extra_gids(passwd const& pwd)
  37. {
  38. StringView username { pwd.pw_name, strlen(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. ErrorOr<Account> Account::from_passwd(passwd const& pwd, spwd const& spwd)
  55. {
  56. Account account(pwd, spwd, get_extra_gids(pwd));
  57. endpwent();
  58. #ifndef AK_OS_BSD_GENERIC
  59. endspent();
  60. #endif
  61. return account;
  62. }
  63. String Account::parse_path_with_uid(StringView general_path, Optional<uid_t> uid)
  64. {
  65. if (general_path.contains("%uid"sv)) {
  66. auto const final_uid = uid.has_value() ? uid.value() : getuid();
  67. return general_path.replace("%uid"sv, String::number(final_uid), ReplaceMode::All);
  68. }
  69. return general_path;
  70. }
  71. ErrorOr<Account> Account::self([[maybe_unused]] Read options)
  72. {
  73. Vector<gid_t> extra_gids = TRY(Core::System::getgroups());
  74. auto pwd = TRY(Core::System::getpwuid(getuid()));
  75. if (!pwd.has_value())
  76. return Error::from_string_literal("No such user");
  77. spwd spwd = {};
  78. #ifndef AK_OS_BSD_GENERIC
  79. if (options != Read::PasswdOnly) {
  80. auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
  81. if (!maybe_spwd.has_value())
  82. return Error::from_string_literal("No shadow entry for user");
  83. spwd = maybe_spwd.release_value();
  84. }
  85. #endif
  86. return Account(*pwd, spwd, extra_gids);
  87. }
  88. ErrorOr<Account> Account::from_name(char const* username, [[maybe_unused]] Read options)
  89. {
  90. auto pwd = TRY(Core::System::getpwnam({ username, strlen(username) }));
  91. if (!pwd.has_value())
  92. return Error::from_string_literal("No such user");
  93. spwd spwd = {};
  94. #ifndef AK_OS_BSD_GENERIC
  95. if (options != Read::PasswdOnly) {
  96. auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
  97. if (!maybe_spwd.has_value())
  98. return Error::from_string_literal("No shadow entry for user");
  99. spwd = maybe_spwd.release_value();
  100. }
  101. #endif
  102. return from_passwd(*pwd, spwd);
  103. }
  104. ErrorOr<Account> Account::from_uid(uid_t uid, [[maybe_unused]] Read options)
  105. {
  106. auto pwd = TRY(Core::System::getpwuid(uid));
  107. if (!pwd.has_value())
  108. return Error::from_string_literal("No such user");
  109. spwd spwd = {};
  110. #ifndef AK_OS_BSD_GENERIC
  111. if (options != Read::PasswdOnly) {
  112. auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
  113. if (!maybe_spwd.has_value())
  114. return Error::from_string_literal("No shadow entry for user");
  115. spwd = maybe_spwd.release_value();
  116. }
  117. #endif
  118. return from_passwd(*pwd, spwd);
  119. }
  120. bool Account::authenticate(SecretString const& password) const
  121. {
  122. // If there was no shadow entry for this account, authentication always fails.
  123. if (m_password_hash.is_null())
  124. return false;
  125. // An empty passwd field indicates that no password is required to log in.
  126. if (m_password_hash.is_empty())
  127. return true;
  128. // FIXME: Use crypt_r if it can be built in lagom.
  129. char* hash = crypt(password.characters(), m_password_hash.characters());
  130. return hash != nullptr && AK::timing_safe_compare(hash, m_password_hash.characters(), m_password_hash.length());
  131. }
  132. ErrorOr<void> Account::create_user_temporary_directory_if_needed() const
  133. {
  134. auto const temporary_directory = String::formatted("/tmp/user/{}", m_uid);
  135. auto directory = TRY(Core::Directory::create(temporary_directory, Core::Directory::CreateDirectories::Yes));
  136. TRY(directory.chown(m_uid, m_gid));
  137. return {};
  138. }
  139. bool Account::login() const
  140. {
  141. if (setgroups(m_extra_gids.size(), m_extra_gids.data()) < 0)
  142. return false;
  143. if (setgid(m_gid) < 0)
  144. return false;
  145. if (setuid(m_uid) < 0)
  146. return false;
  147. return true;
  148. }
  149. void Account::set_password(SecretString const& password)
  150. {
  151. m_password_hash = crypt(password.characters(), get_salt().characters());
  152. }
  153. void Account::set_password_enabled(bool enabled)
  154. {
  155. if (enabled && m_password_hash != "" && m_password_hash[0] == '!') {
  156. m_password_hash = m_password_hash.substring(1, m_password_hash.length() - 1);
  157. } else if (!enabled && (m_password_hash == "" || m_password_hash[0] != '!')) {
  158. StringBuilder builder;
  159. builder.append('!');
  160. builder.append(m_password_hash);
  161. m_password_hash = builder.build();
  162. }
  163. }
  164. void Account::delete_password()
  165. {
  166. m_password_hash = "";
  167. }
  168. Account::Account(passwd const& pwd, spwd const& spwd, Vector<gid_t> extra_gids)
  169. : m_username(pwd.pw_name)
  170. , m_password_hash(spwd.sp_pwdp)
  171. , m_uid(pwd.pw_uid)
  172. , m_gid(pwd.pw_gid)
  173. , m_gecos(pwd.pw_gecos)
  174. , m_home_directory(pwd.pw_dir)
  175. , m_shell(pwd.pw_shell)
  176. , m_extra_gids(move(extra_gids))
  177. {
  178. }
  179. ErrorOr<String> Account::generate_passwd_file() const
  180. {
  181. StringBuilder builder;
  182. setpwent();
  183. struct passwd* p;
  184. errno = 0;
  185. while ((p = getpwent())) {
  186. if (p->pw_name == m_username) {
  187. builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
  188. m_username,
  189. m_uid, m_gid,
  190. m_gecos,
  191. m_home_directory,
  192. m_shell);
  193. } else {
  194. builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
  195. p->pw_name, p->pw_uid,
  196. p->pw_gid, p->pw_gecos, p->pw_dir,
  197. p->pw_shell);
  198. }
  199. }
  200. endpwent();
  201. if (errno)
  202. return Error::from_errno(errno);
  203. return builder.to_string();
  204. }
  205. #ifndef AK_OS_BSD_GENERIC
  206. ErrorOr<String> Account::generate_shadow_file() const
  207. {
  208. StringBuilder builder;
  209. setspent();
  210. struct spwd* p;
  211. errno = 0;
  212. while ((p = getspent())) {
  213. if (p->sp_namp == m_username) {
  214. builder.appendff("{}:{}:{}:{}:{}:{}:{}:{}:{}\n",
  215. m_username, m_password_hash,
  216. (p->sp_lstchg == -1) ? "" : String::formatted("{}", p->sp_lstchg),
  217. (p->sp_min == -1) ? "" : String::formatted("{}", p->sp_min),
  218. (p->sp_max == -1) ? "" : String::formatted("{}", p->sp_max),
  219. (p->sp_warn == -1) ? "" : String::formatted("{}", p->sp_warn),
  220. (p->sp_inact == -1) ? "" : String::formatted("{}", p->sp_inact),
  221. (p->sp_expire == -1) ? "" : String::formatted("{}", p->sp_expire),
  222. (p->sp_flag == 0) ? "" : String::formatted("{}", p->sp_flag));
  223. } else {
  224. builder.appendff("{}:{}:{}:{}:{}:{}:{}:{}:{}\n",
  225. p->sp_namp, p->sp_pwdp,
  226. (p->sp_lstchg == -1) ? "" : String::formatted("{}", p->sp_lstchg),
  227. (p->sp_min == -1) ? "" : String::formatted("{}", p->sp_min),
  228. (p->sp_max == -1) ? "" : String::formatted("{}", p->sp_max),
  229. (p->sp_warn == -1) ? "" : String::formatted("{}", p->sp_warn),
  230. (p->sp_inact == -1) ? "" : String::formatted("{}", p->sp_inact),
  231. (p->sp_expire == -1) ? "" : String::formatted("{}", p->sp_expire),
  232. (p->sp_flag == 0) ? "" : String::formatted("{}", p->sp_flag));
  233. }
  234. }
  235. endspent();
  236. if (errno)
  237. return Error::from_errno(errno);
  238. return builder.to_string();
  239. }
  240. #endif
  241. ErrorOr<void> Account::sync()
  242. {
  243. Core::UmaskScope umask_scope(0777);
  244. auto new_passwd_file_content = TRY(generate_passwd_file());
  245. #ifndef AK_OS_BSD_GENERIC
  246. auto new_shadow_file_content = TRY(generate_shadow_file());
  247. #endif
  248. // FIXME: mkstemp taking Span<char> makes this code entirely un-AKable.
  249. // Make this code less char-pointery.
  250. char new_passwd_name[] = "/etc/passwd.XXXXXX";
  251. size_t new_passwd_name_length = strlen(new_passwd_name);
  252. #ifndef AK_OS_BSD_GENERIC
  253. char new_shadow_name[] = "/etc/shadow.XXXXXX";
  254. size_t new_shadow_name_length = strlen(new_shadow_name);
  255. #endif
  256. {
  257. auto new_passwd_fd = TRY(Core::System::mkstemp({ new_passwd_name, new_passwd_name_length }));
  258. ScopeGuard new_passwd_fd_guard = [new_passwd_fd] { close(new_passwd_fd); };
  259. TRY(Core::System::fchmod(new_passwd_fd, 0644));
  260. #ifndef AK_OS_BSD_GENERIC
  261. auto new_shadow_fd = TRY(Core::System::mkstemp({ new_shadow_name, new_shadow_name_length }));
  262. ScopeGuard new_shadow_fd_guard = [new_shadow_fd] { close(new_shadow_fd); };
  263. TRY(Core::System::fchmod(new_shadow_fd, 0600));
  264. #endif
  265. auto nwritten = TRY(Core::System::write(new_passwd_fd, new_passwd_file_content.bytes()));
  266. VERIFY(static_cast<size_t>(nwritten) == new_passwd_file_content.length());
  267. #ifndef AK_OS_BSD_GENERIC
  268. nwritten = TRY(Core::System::write(new_shadow_fd, new_shadow_file_content.bytes()));
  269. VERIFY(static_cast<size_t>(nwritten) == new_shadow_file_content.length());
  270. #endif
  271. }
  272. TRY(Core::System::rename({ new_passwd_name, new_passwd_name_length }, "/etc/passwd"sv));
  273. #ifndef AK_OS_BSD_GENERIC
  274. TRY(Core::System::rename({ new_shadow_name, new_shadow_name_length }, "/etc/shadow"sv));
  275. #endif
  276. return {};
  277. // FIXME: Sync extra groups.
  278. }
  279. }