usermod.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /*
  2. * Copyright (c) 2021, Brandon Pruitt <brapru@pm.me>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibCore/Account.h>
  7. #include <LibCore/ArgsParser.h>
  8. #include <LibCore/File.h>
  9. #include <pwd.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. int main(int argc, char** argv)
  14. {
  15. if (geteuid() != 0) {
  16. warnln("Not running as root :^(");
  17. return 1;
  18. }
  19. if (setegid(0) < 0) {
  20. perror("setegid");
  21. return 1;
  22. }
  23. if (pledge("stdio wpath rpath cpath fattr tty", nullptr) < 0) {
  24. perror("pledge");
  25. return 1;
  26. }
  27. if (unveil("/etc", "rwc") < 0) {
  28. perror("unveil");
  29. return 1;
  30. }
  31. int uid = 0;
  32. int gid = 0;
  33. bool lock = false;
  34. bool unlock = false;
  35. const char* new_home_directory = nullptr;
  36. bool move_home = false;
  37. const char* shell = nullptr;
  38. const char* gecos = nullptr;
  39. const char* username = nullptr;
  40. auto args_parser = Core::ArgsParser();
  41. args_parser.set_general_help("Modify a user account");
  42. args_parser.add_option(uid, "The new numerical value of the user's ID", "uid", 'u', "uid");
  43. args_parser.add_option(gid, "The group number of the user's new initial login group", "gid", 'g', "gid");
  44. args_parser.add_option(lock, "Lock password", "lock", 'L');
  45. args_parser.add_option(unlock, "Unlock password", "unlock", 'U');
  46. args_parser.add_option(new_home_directory, "The user's new login directory", "home", 'd', "new-home");
  47. args_parser.add_option(move_home, "Move the content of the user's home directory to the new location", "move", 'm');
  48. args_parser.add_option(shell, "The name of the user's new login shell", "shell", 's', "path-to-shell");
  49. args_parser.add_option(gecos, "Change the GECOS field of the user", "gecos", 'n', "general-info");
  50. args_parser.add_positional_argument(username, "Username of the account to modify", "username");
  51. args_parser.parse(argc, argv);
  52. auto account_or_error = Core::Account::from_name(username);
  53. if (account_or_error.is_error()) {
  54. warnln("Core::Account::from_name: {}", account_or_error.error());
  55. return 1;
  56. }
  57. // target_account is the account we are modifying.
  58. auto& target_account = account_or_error.value();
  59. if (move_home) {
  60. if (unveil(target_account.home_directory().characters(), "c") < 0) {
  61. perror("unveil");
  62. return 1;
  63. }
  64. if (unveil(new_home_directory, "wc") < 0) {
  65. perror("unveil");
  66. return 1;
  67. }
  68. }
  69. unveil(nullptr, nullptr);
  70. if (uid) {
  71. if (uid < 0) {
  72. warnln("invalid uid {}", uid);
  73. return 1;
  74. }
  75. if (getpwuid(static_cast<uid_t>(uid))) {
  76. warnln("uid {} already exists", uid);
  77. return 1;
  78. }
  79. target_account.set_uid(uid);
  80. }
  81. if (gid) {
  82. if (gid < 0) {
  83. warnln("invalid gid {}", gid);
  84. return 1;
  85. }
  86. target_account.set_gid(gid);
  87. }
  88. if (lock) {
  89. target_account.set_password_enabled(false);
  90. }
  91. if (unlock) {
  92. target_account.set_password_enabled(true);
  93. }
  94. if (new_home_directory) {
  95. if (move_home) {
  96. int rc = rename(target_account.home_directory().characters(), new_home_directory);
  97. if (rc < 0) {
  98. if (errno == EXDEV) {
  99. auto result = Core::File::copy_file_or_directory(
  100. new_home_directory, target_account.home_directory().characters(),
  101. Core::File::RecursionMode::Allowed,
  102. Core::File::LinkMode::Disallowed,
  103. Core::File::AddDuplicateFileMarker::No);
  104. if (result.is_error()) {
  105. warnln("usermod: could not move directory {} : {}", target_account.home_directory().characters(), static_cast<Error const&>(result.error()));
  106. return 1;
  107. }
  108. rc = unlink(target_account.home_directory().characters());
  109. if (rc < 0)
  110. warnln("usermod: unlink {} : {}", target_account.home_directory().characters(), strerror(errno));
  111. } else {
  112. warnln("usermod: could not move directory {} : {}", target_account.home_directory().characters(), strerror(errno));
  113. }
  114. }
  115. }
  116. target_account.set_home_directory(new_home_directory);
  117. }
  118. if (shell) {
  119. target_account.set_shell(shell);
  120. }
  121. if (gecos) {
  122. target_account.set_gecos(gecos);
  123. }
  124. if (pledge("stdio wpath rpath cpath fattr", nullptr) < 0) {
  125. perror("pledge");
  126. return 1;
  127. }
  128. if (!target_account.sync()) {
  129. perror("Core::Account::Sync");
  130. return 1;
  131. }
  132. return 0;
  133. }