userdel.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. userdel.c
  5. Abstract:
  6. This module implements the userdel command, which deletes a user account
  7. from the system.
  8. Author:
  9. Evan Green 11-Mar-2015
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <getopt.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "../swlib.h"
  24. #include "lutil.h"
  25. //
  26. // ---------------------------------------------------------------- Definitions
  27. //
  28. #define USERDEL_VERSION_MAJOR 1
  29. #define USERDEL_VERSION_MINOR 0
  30. #define USERDEL_USAGE \
  31. "usage: userdel [options] username\n" \
  32. "The userdel utility deletes a user from the system. Options are:\n" \
  33. " -f, --force -- Force the removal of the account, even if the user \n" \
  34. " is still logged in, or another user uses the same home directory.\n"\
  35. " -r, --remove -- Delete the home directory and its files.\n" \
  36. " -R, --root=dir -- Chroot into the given directory before operation.\n" \
  37. " --help -- Displays this help text and exits.\n" \
  38. " --version -- Displays the application version and exits.\n"
  39. #define USERDEL_OPTIONS_STRING "frR:HV"
  40. //
  41. // Define application options.
  42. //
  43. //
  44. // Set this option to force the removal.
  45. //
  46. #define USERDEL_OPTION_FORCE 0x00000001
  47. //
  48. // Set this option to remove the home directory and mail spool.
  49. //
  50. #define USERDEL_OPTION_REMOVE 0x00000002
  51. //
  52. // ------------------------------------------------------ Data Type Definitions
  53. //
  54. //
  55. // ----------------------------------------------- Internal Function Prototypes
  56. //
  57. //
  58. // -------------------------------------------------------------------- Globals
  59. //
  60. struct option UserdelLongOptions[] = {
  61. {"force", no_argument, 0, 'f'},
  62. {"remove", no_argument, 0, 'r'},
  63. {"root", required_argument, 0, 'R'},
  64. {"help", no_argument, 0, 'H'},
  65. {"version", no_argument, 0, 'V'},
  66. {NULL, 0, 0, 0},
  67. };
  68. //
  69. // ------------------------------------------------------------------ Functions
  70. //
  71. INT
  72. UserdelMain (
  73. INT ArgumentCount,
  74. CHAR **Arguments
  75. )
  76. /*++
  77. Routine Description:
  78. This routine is the main entry point for the userdel utility.
  79. Arguments:
  80. ArgumentCount - Supplies the number of command line arguments the program
  81. was invoked with.
  82. Arguments - Supplies a tokenized array of command line arguments.
  83. Return Value:
  84. Returns an integer exit code. 0 for success, nonzero otherwise.
  85. --*/
  86. {
  87. ULONG ArgumentIndex;
  88. struct group *Group;
  89. int GroupCount;
  90. UINTN GroupIndex;
  91. gid_t *Groups;
  92. PSTR Home;
  93. INT Option;
  94. ULONG Options;
  95. PSTR RootDirectory;
  96. struct spwd Shadow;
  97. int Status;
  98. int TotalStatus;
  99. struct passwd *User;
  100. struct passwd UserCopy;
  101. PSTR UserName;
  102. memset(&Shadow, 0, sizeof(Shadow));
  103. Groups = NULL;
  104. GroupCount = 0;
  105. Home = NULL;
  106. Options = 0;
  107. RootDirectory = NULL;
  108. TotalStatus = 0;
  109. UserName = NULL;
  110. //
  111. // Process the control arguments.
  112. //
  113. while (TRUE) {
  114. Option = getopt_long(ArgumentCount,
  115. Arguments,
  116. USERDEL_OPTIONS_STRING,
  117. UserdelLongOptions,
  118. NULL);
  119. if (Option == -1) {
  120. break;
  121. }
  122. if ((Option == '?') || (Option == ':')) {
  123. Status = 1;
  124. goto MainEnd;
  125. }
  126. switch (Option) {
  127. case 'f':
  128. Options |= USERDEL_OPTION_FORCE;
  129. break;
  130. case 'r':
  131. Options |= USERDEL_OPTION_REMOVE;
  132. break;
  133. case 'R':
  134. RootDirectory = optarg;
  135. break;
  136. case 'V':
  137. SwPrintVersion(USERDEL_VERSION_MAJOR, USERDEL_VERSION_MINOR);
  138. return 1;
  139. case 'H':
  140. printf(USERDEL_USAGE);
  141. return 1;
  142. default:
  143. assert(FALSE);
  144. Status = 1;
  145. goto MainEnd;
  146. }
  147. }
  148. ArgumentIndex = optind;
  149. if (ArgumentIndex > ArgumentCount) {
  150. ArgumentIndex = ArgumentCount;
  151. }
  152. if (ArgumentIndex >= ArgumentCount) {
  153. SwPrintError(0, NULL, "Argument expected. Try --help for usage");
  154. return 1;
  155. }
  156. //
  157. // Chroot if requested.
  158. //
  159. if (RootDirectory != NULL) {
  160. Status = chroot(RootDirectory);
  161. if (Status != 0) {
  162. Status = errno;
  163. SwPrintError(Status, RootDirectory, "Failed to chroot");
  164. goto MainEnd;
  165. }
  166. Status = chdir("/");
  167. if (Status != 0) {
  168. Status = errno;
  169. SwPrintError(Status, RootDirectory, "Failed to chdir");
  170. goto MainEnd;
  171. }
  172. }
  173. while (ArgumentIndex < ArgumentCount) {
  174. UserName = Arguments[ArgumentIndex];
  175. ArgumentIndex += 1;
  176. User = getpwnam(UserName);
  177. if (User == NULL) {
  178. SwPrintError(0, UserName, "No such user");
  179. TotalStatus = ENOENT;
  180. continue;
  181. }
  182. memcpy(&UserCopy, User, sizeof(struct passwd));
  183. UserCopy.pw_name = UserName;
  184. Home = strdup(User->pw_dir);
  185. //
  186. // Get all the groups this user belongs to.
  187. //
  188. Status = getgrouplist(UserName, UserCopy.pw_gid, Groups, &GroupCount);
  189. if (Status < 0) {
  190. //
  191. // Allocate or reallocate the buffer. Add some extras in case
  192. // things shift a bit in the meantime.
  193. //
  194. Groups = realloc(Groups, (GroupCount + 5) * sizeof(gid_t));
  195. if (Groups == NULL) {
  196. Status = ENOMEM;
  197. goto MainEnd;
  198. }
  199. Status = getgrouplist(UserName,
  200. UserCopy.pw_gid,
  201. Groups,
  202. &GroupCount);
  203. if (Status < 0) {
  204. SwPrintError(0, UserName, "Failed to get groups");
  205. Status = errno;
  206. goto MainEnd;
  207. }
  208. }
  209. if (Status > 0) {
  210. //
  211. // Remove the user from all supplementary groups.
  212. //
  213. for (GroupIndex = 0; GroupIndex < GroupCount; GroupIndex += 1) {
  214. Group = getgrgid(Groups[GroupIndex]);
  215. if ((Group != NULL) && (Group->gr_gid != UserCopy.pw_gid)) {
  216. Status = SwUpdatePasswordFile(
  217. GROUP_FILE_PATH,
  218. Group->gr_name,
  219. NULL,
  220. UserName,
  221. UpdatePasswordDeleteGroupMember);
  222. if (Status != 0) {
  223. SwPrintError(Status,
  224. NULL,
  225. "Failed to remove user %s from group %s",
  226. UserName,
  227. Group->gr_name);
  228. TotalStatus = Status;
  229. }
  230. }
  231. }
  232. }
  233. //
  234. // If there is a group with the same name as the user, delete that
  235. // group.
  236. //
  237. Group = getgrnam(UserName);
  238. if (Group != NULL) {
  239. Status = SwUpdatePasswordFile(GROUP_FILE_PATH,
  240. UserName,
  241. NULL,
  242. NULL,
  243. UpdatePasswordDeleteLine);
  244. if (Status != 0) {
  245. TotalStatus = Status;
  246. }
  247. }
  248. //
  249. // Remove the user itself.
  250. //
  251. UserCopy.pw_name = UserName;
  252. Shadow.sp_namp = UserName;
  253. Status = SwUpdatePasswordLine(&UserCopy,
  254. &Shadow,
  255. UpdatePasswordDeleteLine);
  256. if (Status != 0) {
  257. SwPrintError(Status, UserName, "Failed to remove user");
  258. goto MainEnd;
  259. }
  260. //
  261. // Remove the home directory.
  262. //
  263. if (((Options & USERDEL_OPTION_REMOVE) != 0) && (Home != NULL)) {
  264. Status = SwDelete(DELETE_OPTION_RECURSIVE | DELETE_OPTION_FORCE,
  265. Home);
  266. if (Status != 0) {
  267. SwPrintError(Status, Home, "Failed to delete home directory");
  268. TotalStatus = Status;
  269. }
  270. }
  271. if (Home != NULL) {
  272. free(Home);
  273. Home = NULL;
  274. }
  275. }
  276. Status = 0;
  277. MainEnd:
  278. if (Home != NULL) {
  279. free(Home);
  280. }
  281. if (Groups != NULL) {
  282. free(Groups);
  283. }
  284. if ((TotalStatus == 0) && (Status != 0)) {
  285. TotalStatus = Status;
  286. }
  287. return TotalStatus;
  288. }
  289. //
  290. // --------------------------------------------------------- Internal Functions
  291. //