logo

utils-std

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

id.c (6913B)


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