logo

utils-std

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

id.c (6386B)


  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 "../lib/getopt_nolong.h"
  6. #include <grp.h> // getgrgid, getgroups, getgrouplist(glibc)
  7. #include <pwd.h> // getpwuid
  8. #include <stdbool.h> // bool
  9. #include <stdio.h> // printf, perror
  10. #include <stdlib.h> // calloc, free
  11. #include <sys/types.h> // uid_t
  12. #include <unistd.h> // getuid, getgid, getopt, opt*, getgrouplist(FreeBSD, NetBSD)
  13. const char *argv0 = "id";
  14. bool name_flag = false;
  15. static int
  16. simple_list_groups(struct passwd *pw, int ngroups, gid_t *groups)
  17. {
  18. bool first = true;
  19. // Don't print basegid twice when present in pw and grouplist
  20. bool basegid_done = false;
  21. for(int i = 0; i < ngroups; i++)
  22. {
  23. if(groups[i] == pw->pw_gid)
  24. {
  25. if(basegid_done)
  26. continue;
  27. else
  28. basegid_done = true;
  29. }
  30. if(name_flag)
  31. {
  32. struct group *lgr = getgrgid(groups[i]);
  33. if(lgr == NULL) return 1;
  34. if(!first) putchar(' ');
  35. printf("%s", lgr->gr_name);
  36. }
  37. else
  38. {
  39. if(!first) putchar(' ');
  40. printf("%u", groups[i]);
  41. }
  42. first = false;
  43. }
  44. putchar('\n');
  45. return 0;
  46. }
  47. static int
  48. list_groups(struct passwd *pw, int ngroups, gid_t *groups)
  49. {
  50. bool first = true;
  51. // Don't print basegid twice when present in pw and grouplist
  52. bool basegid_done = false;
  53. printf(" groups=");
  54. for(int i = 0; i < ngroups; i++)
  55. {
  56. struct group *lgr = getgrgid(groups[i]);
  57. if(groups[i] == pw->pw_gid)
  58. {
  59. if(basegid_done)
  60. continue;
  61. else
  62. basegid_done = true;
  63. }
  64. if(name_flag)
  65. {
  66. if(lgr == NULL) return 1;
  67. if(!first) putchar(' ');
  68. printf("%s", lgr->gr_name);
  69. }
  70. else
  71. {
  72. if(!first) putchar(',');
  73. printf("%u", groups[i]);
  74. if(lgr != NULL) printf("(%s)", lgr->gr_name);
  75. }
  76. first = false;
  77. }
  78. putchar('\n');
  79. return 0;
  80. }
  81. static int
  82. print_gid(const char *field, struct group *gr, gid_t gid)
  83. {
  84. if(gr && gr->gr_name)
  85. {
  86. if(name_flag) return printf("%s=%s", field, gr->gr_name);
  87. return printf("%s=%u(%s)", field, gid, gr->gr_name);
  88. }
  89. else
  90. {
  91. if(name_flag) return -1;
  92. return printf("%s=%u", field, gid);
  93. }
  94. }
  95. static int
  96. print_uid(const char *field, struct passwd *pw, uid_t uid)
  97. {
  98. if(pw && pw->pw_name)
  99. {
  100. if(name_flag) return printf("%s=%s", field, pw->pw_name);
  101. return printf("%s=%u(%s)", field, uid, pw->pw_name);
  102. }
  103. else
  104. {
  105. if(name_flag) return -1;
  106. return printf("%s=%u", field, uid);
  107. }
  108. }
  109. static void
  110. safe_getpwuid(uid_t uid, struct passwd *res)
  111. {
  112. struct passwd *pw = getpwuid(uid);
  113. if(pw != NULL) *res = *pw;
  114. }
  115. enum id_modes
  116. {
  117. ID_NORMAL,
  118. ID_GROUPS,
  119. ID_GID,
  120. ID_UID,
  121. };
  122. static void
  123. usage(void)
  124. {
  125. fprintf(stderr, "Usage: id [-Ggu] [-nr] [user]\n");
  126. }
  127. int
  128. main(int argc, char *argv[])
  129. {
  130. int ret = 0;
  131. enum id_modes mode = ID_NORMAL;
  132. bool real_flag = false;
  133. int ngroups = 0;
  134. int ngroups_max = (int)sysconf(_SC_NGROUPS_MAX) + 1;
  135. if(ngroups_max <= 0)
  136. {
  137. perror("id: error: sysconf(_SC_NGROUPS_MAX)");
  138. return 1;
  139. }
  140. gid_t *groups = NULL;
  141. groups = calloc(ngroups_max, sizeof(*groups));
  142. if(groups == NULL)
  143. {
  144. perror("id: error: calloc(groups)");
  145. return 1;
  146. }
  147. // geteuid, getuid, getegid, getgid shall always be successful
  148. uid_t uid = getuid();
  149. uid_t euid = geteuid();
  150. gid_t gid = getgid();
  151. gid_t egid = getegid();
  152. struct passwd pw = {.pw_uid = uid, .pw_gid = gid};
  153. struct passwd epw = {.pw_uid = euid, .pw_gid = egid};
  154. for(int c = -1; (c = getopt_nolong(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. if(!got_long_opt) 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. }