logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

id.c (6333B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _DEFAULT_SOURCE // getgrouplist (4.4BSD+)
  5. #include <grp.h> // getgrgid, getgroups, getgrouplist(glibc)
  6. #include <pwd.h> // getpwuid
  7. #include <stdbool.h> // bool
  8. #include <stdio.h> // printf, perror
  9. #include <stdlib.h> // calloc, free
  10. #include <sys/types.h> // uid_t
  11. #include <unistd.h> // getuid, getgid, getopt, opt*, getgrouplist(FreeBSD, NetBSD)
  12. const char *argv0 = "id";
  13. bool name_flag = false;
  14. static int
  15. simple_list_groups(struct passwd *pw, int ngroups, gid_t *groups)
  16. {
  17. bool first = true;
  18. // Don't print basegid twice when present in pw and grouplist
  19. bool basegid_done = false;
  20. for(int i = 0; i < ngroups; i++)
  21. {
  22. if(groups[i] == pw->pw_gid)
  23. {
  24. if(basegid_done)
  25. continue;
  26. else
  27. basegid_done = true;
  28. }
  29. if(name_flag)
  30. {
  31. struct group *lgr = getgrgid(groups[i]);
  32. if(lgr == NULL) return 1;
  33. if(!first) putchar(' ');
  34. printf("%s", lgr->gr_name);
  35. }
  36. else
  37. {
  38. if(!first) putchar(' ');
  39. printf("%u", groups[i]);
  40. }
  41. first = false;
  42. }
  43. putchar('\n');
  44. return 0;
  45. }
  46. static int
  47. list_groups(struct passwd *pw, int ngroups, gid_t *groups)
  48. {
  49. bool first = true;
  50. // Don't print basegid twice when present in pw and grouplist
  51. bool basegid_done = false;
  52. printf(" groups=");
  53. for(int i = 0; i < ngroups; i++)
  54. {
  55. struct group *lgr = getgrgid(groups[i]);
  56. if(groups[i] == pw->pw_gid)
  57. {
  58. if(basegid_done)
  59. continue;
  60. else
  61. basegid_done = true;
  62. }
  63. if(name_flag)
  64. {
  65. if(lgr == NULL) return 1;
  66. if(!first) putchar(' ');
  67. printf("%s", lgr->gr_name);
  68. }
  69. else
  70. {
  71. if(!first) putchar(',');
  72. printf("%u", groups[i]);
  73. if(lgr != NULL) printf("(%s)", lgr->gr_name);
  74. }
  75. first = false;
  76. }
  77. putchar('\n');
  78. return 0;
  79. }
  80. static int
  81. print_gid(const char *field, struct group *gr, gid_t gid)
  82. {
  83. if(gr && gr->gr_name)
  84. {
  85. if(name_flag) return printf("%s=%s", field, gr->gr_name);
  86. return printf("%s=%u(%s)", field, gid, gr->gr_name);
  87. }
  88. else
  89. {
  90. if(name_flag) return -1;
  91. return printf("%s=%u", field, gid);
  92. }
  93. }
  94. static int
  95. print_uid(const char *field, struct passwd *pw, uid_t uid)
  96. {
  97. if(pw && pw->pw_name)
  98. {
  99. if(name_flag) return printf("%s=%s", field, pw->pw_name);
  100. return printf("%s=%u(%s)", field, uid, pw->pw_name);
  101. }
  102. else
  103. {
  104. if(name_flag) return -1;
  105. return printf("%s=%u", field, uid);
  106. }
  107. }
  108. static void
  109. safe_getpwuid(uid_t uid, struct passwd *res)
  110. {
  111. struct passwd *pw = getpwuid(uid);
  112. if(pw != NULL) *res = *pw;
  113. }
  114. enum id_modes
  115. {
  116. ID_NORMAL,
  117. ID_GROUPS,
  118. ID_GID,
  119. ID_UID,
  120. };
  121. static void
  122. usage(void)
  123. {
  124. fprintf(stderr, "Usage: id [-Ggu] [-nr] [user]\n");
  125. }
  126. int
  127. main(int argc, char *argv[])
  128. {
  129. int ret = 0;
  130. enum id_modes mode = ID_NORMAL;
  131. bool real_flag = false;
  132. int ngroups = 0;
  133. int ngroups_max = (int)sysconf(_SC_NGROUPS_MAX) + 1;
  134. if(ngroups_max <= 0)
  135. {
  136. perror("id: error: sysconf(_SC_NGROUPS_MAX)");
  137. return 1;
  138. }
  139. gid_t *groups = NULL;
  140. groups = calloc(ngroups_max, sizeof(*groups));
  141. if(groups == NULL)
  142. {
  143. perror("id: error: calloc(groups)");
  144. return 1;
  145. }
  146. // geteuid, getuid, getegid, getgid shall always be successful
  147. uid_t uid = getuid();
  148. uid_t euid = geteuid();
  149. gid_t gid = getgid();
  150. gid_t egid = getegid();
  151. struct passwd pw = {.pw_uid = uid, .pw_gid = gid};
  152. struct passwd epw = {.pw_uid = euid, .pw_gid = egid};
  153. for(int c = -1; (c = getopt(argc, argv, ":Ggunr")) != -1;)
  154. {
  155. switch(c)
  156. {
  157. case 'G':
  158. mode = ID_GROUPS;
  159. break;
  160. case 'u':
  161. mode = ID_UID;
  162. break;
  163. case 'g':
  164. mode = ID_GID;
  165. break;
  166. case 'n':
  167. name_flag = true;
  168. break;
  169. case 'r':
  170. real_flag = true;
  171. break;
  172. case '?':
  173. fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
  174. usage();
  175. free(groups);
  176. return 1;
  177. default:
  178. fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
  179. usage();
  180. free(groups);
  181. return 1;
  182. }
  183. }
  184. argc -= optind;
  185. argv += optind;
  186. if(argc == 0)
  187. {
  188. safe_getpwuid(uid, &pw);
  189. safe_getpwuid(euid, &epw);
  190. // Get groups from currently running process instead of configuration
  191. ngroups = getgroups(ngroups_max, groups);
  192. if(ngroups < 0)
  193. {
  194. perror("id: error: getgroups");
  195. goto failure;
  196. }
  197. }
  198. else if(argc == 1)
  199. {
  200. struct passwd *pw_n = getpwnam(argv[0]);
  201. if(pw_n == NULL)
  202. {
  203. goto failure;
  204. }
  205. pw = *pw_n;
  206. epw = *pw_n;
  207. uid = pw.pw_uid;
  208. euid = epw.pw_uid;
  209. gid = pw.pw_gid;
  210. egid = epw.pw_gid;
  211. // Can only get groups from configuration
  212. if(getgrouplist(pw.pw_name, pw.pw_gid, groups, &ngroups_max) < 0)
  213. {
  214. perror("id: error: getgrouplist");
  215. goto failure;
  216. }
  217. ngroups = ngroups_max;
  218. }
  219. else
  220. {
  221. usage();
  222. }
  223. struct group *gr = getgrgid(gid);
  224. struct group *egr = getgrgid(egid);
  225. if(mode == ID_GID)
  226. {
  227. if(!real_flag) gid = egid;
  228. if(!name_flag)
  229. {
  230. ret = printf("%u\n", gid);
  231. }
  232. else
  233. {
  234. if(gr == NULL || gr->gr_name == NULL)
  235. {
  236. ret--;
  237. fprintf(stderr, "%s: error: Cannot find name for group ID %u\n", argv0, gid);
  238. printf("%u\n", gid);
  239. }
  240. else
  241. {
  242. ret = printf("%s\n", gr->gr_name);
  243. }
  244. }
  245. if(ret < 0) goto failure;
  246. goto done;
  247. }
  248. if(mode == ID_UID)
  249. {
  250. if(!real_flag) uid = euid;
  251. if(!name_flag)
  252. {
  253. ret = printf("%u\n", uid);
  254. }
  255. else
  256. {
  257. if(pw.pw_name == NULL)
  258. {
  259. ret--;
  260. fprintf(stderr, "%s: error: Cannot find name for user ID %u\n", argv0, uid);
  261. printf("%u\n", uid);
  262. }
  263. else
  264. {
  265. ret = printf("%s\n", pw.pw_name);
  266. }
  267. }
  268. if(ret < 0) goto failure;
  269. goto done;
  270. }
  271. if(mode == ID_GROUPS)
  272. {
  273. if(real_flag)
  274. {
  275. ret = simple_list_groups(&pw, ngroups, groups);
  276. }
  277. else
  278. {
  279. ret = simple_list_groups(&epw, ngroups, groups);
  280. }
  281. if(ret != 0) goto failure;
  282. goto done;
  283. }
  284. ret = print_uid("uid", &pw, uid);
  285. if(ret < 0) goto failure;
  286. if(euid != uid)
  287. {
  288. ret = print_uid(" euid", &epw, euid);
  289. if(ret < 0) goto failure;
  290. }
  291. ret = print_gid(" gid", gr, gid);
  292. if(ret < 0) goto failure;
  293. if(egid != gid)
  294. {
  295. ret = print_gid(" egid", egr, egid);
  296. if(ret < 0) goto failure;
  297. }
  298. if(list_groups(&pw, ngroups, groups) != 0) goto failure;
  299. ret = printf("\n");
  300. if(ret < 0) goto failure;
  301. done:
  302. free(groups);
  303. return 0;
  304. failure:
  305. free(groups);
  306. return 1;
  307. }