logo

utils-std

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

id.c (6335B)


  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. int c = -1;
  154. while((c = getopt(argc, argv, ":Ggunr")) != -1)
  155. {
  156. switch(c)
  157. {
  158. case 'G':
  159. mode = ID_GROUPS;
  160. break;
  161. case 'u':
  162. mode = ID_UID;
  163. break;
  164. case 'g':
  165. mode = ID_GID;
  166. break;
  167. case 'n':
  168. name_flag = true;
  169. break;
  170. case 'r':
  171. real_flag = true;
  172. break;
  173. case '?':
  174. fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
  175. usage();
  176. free(groups);
  177. return 1;
  178. default:
  179. fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
  180. usage();
  181. free(groups);
  182. return 1;
  183. }
  184. }
  185. argc -= optind;
  186. argv += optind;
  187. if(argc == 0)
  188. {
  189. safe_getpwuid(uid, &pw);
  190. safe_getpwuid(euid, &epw);
  191. // Get groups from currently running process instead of configuration
  192. ngroups = getgroups(ngroups_max, groups);
  193. if(ngroups < 0)
  194. {
  195. perror("id: error: getgroups");
  196. goto failure;
  197. }
  198. }
  199. else if(argc == 1)
  200. {
  201. struct passwd *pw_n = getpwnam(argv[0]);
  202. if(pw_n == NULL)
  203. {
  204. goto failure;
  205. }
  206. pw = *pw_n;
  207. epw = *pw_n;
  208. uid = pw.pw_uid;
  209. euid = epw.pw_uid;
  210. gid = pw.pw_gid;
  211. egid = epw.pw_gid;
  212. // Can only get groups from configuration
  213. if(getgrouplist(pw.pw_name, pw.pw_gid, groups, &ngroups_max) < 0)
  214. {
  215. perror("id: error: getgrouplist");
  216. goto failure;
  217. }
  218. ngroups = ngroups_max;
  219. }
  220. else
  221. {
  222. usage();
  223. }
  224. struct group *gr = getgrgid(gid);
  225. struct group *egr = getgrgid(egid);
  226. if(mode == ID_GID)
  227. {
  228. if(!real_flag) gid = egid;
  229. if(!name_flag)
  230. {
  231. ret = printf("%u\n", gid);
  232. }
  233. else
  234. {
  235. if(gr == NULL || gr->gr_name == NULL)
  236. {
  237. ret--;
  238. fprintf(stderr, "%s: error: Cannot find name for group ID %u\n", argv0, gid);
  239. printf("%u\n", gid);
  240. }
  241. else
  242. {
  243. ret = printf("%s\n", gr->gr_name);
  244. }
  245. }
  246. if(ret < 0) goto failure;
  247. goto done;
  248. }
  249. if(mode == ID_UID)
  250. {
  251. if(!real_flag) uid = euid;
  252. if(!name_flag)
  253. {
  254. ret = printf("%u\n", uid);
  255. }
  256. else
  257. {
  258. if(pw.pw_name == NULL)
  259. {
  260. ret--;
  261. fprintf(stderr, "%s: error: Cannot find name for user ID %u\n", argv0, uid);
  262. printf("%u\n", uid);
  263. }
  264. else
  265. {
  266. ret = printf("%s\n", pw.pw_name);
  267. }
  268. }
  269. if(ret < 0) goto failure;
  270. goto done;
  271. }
  272. if(mode == ID_GROUPS)
  273. {
  274. if(real_flag)
  275. {
  276. ret = simple_list_groups(&pw, ngroups, groups);
  277. }
  278. else
  279. {
  280. ret = simple_list_groups(&epw, ngroups, groups);
  281. }
  282. if(ret != 0) goto failure;
  283. goto done;
  284. }
  285. ret = print_uid("uid", &pw, uid);
  286. if(ret < 0) goto failure;
  287. if(euid != uid)
  288. {
  289. ret = print_uid(" euid", &epw, euid);
  290. if(ret < 0) goto failure;
  291. }
  292. ret = print_gid(" gid", gr, gid);
  293. if(ret < 0) goto failure;
  294. if(egid != gid)
  295. {
  296. ret = print_gid(" egid", egr, egid);
  297. if(ret < 0) goto failure;
  298. }
  299. if(list_groups(&pw, ngroups, groups) != 0) goto failure;
  300. ret = printf("\n");
  301. if(ret < 0) goto failure;
  302. done:
  303. free(groups);
  304. return 0;
  305. failure:
  306. free(groups);
  307. return 1;
  308. }