id.c (6335B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _DEFAULT_SOURCE // getgrouplist (4.4BSD+)
- #include <grp.h> // getgrgid, getgroups, getgrouplist(glibc)
- #include <pwd.h> // getpwuid
- #include <stdbool.h> // bool
- #include <stdio.h> // printf, perror
- #include <stdlib.h> // calloc, free
- #include <sys/types.h> // uid_t
- #include <unistd.h> // getuid, getgid, getopt, opt*, getgrouplist(FreeBSD, NetBSD)
- const char *argv0 = "id";
- bool name_flag = false;
- static int
- simple_list_groups(struct passwd *pw, int ngroups, gid_t *groups)
- {
- bool first = true;
- // Don't print basegid twice when present in pw and grouplist
- bool basegid_done = false;
- for(int i = 0; i < ngroups; i++)
- {
- if(groups[i] == pw->pw_gid)
- {
- if(basegid_done)
- continue;
- else
- basegid_done = true;
- }
- if(name_flag)
- {
- struct group *lgr = getgrgid(groups[i]);
- if(lgr == NULL) return 1;
- if(!first) putchar(' ');
- printf("%s", lgr->gr_name);
- }
- else
- {
- if(!first) putchar(' ');
- printf("%u", groups[i]);
- }
- first = false;
- }
- putchar('\n');
- return 0;
- }
- static int
- list_groups(struct passwd *pw, int ngroups, gid_t *groups)
- {
- bool first = true;
- // Don't print basegid twice when present in pw and grouplist
- bool basegid_done = false;
- printf(" groups=");
- for(int i = 0; i < ngroups; i++)
- {
- struct group *lgr = getgrgid(groups[i]);
- if(groups[i] == pw->pw_gid)
- {
- if(basegid_done)
- continue;
- else
- basegid_done = true;
- }
- if(name_flag)
- {
- if(lgr == NULL) return 1;
- if(!first) putchar(' ');
- printf("%s", lgr->gr_name);
- }
- else
- {
- if(!first) putchar(',');
- printf("%u", groups[i]);
- if(lgr != NULL) printf("(%s)", lgr->gr_name);
- }
- first = false;
- }
- putchar('\n');
- return 0;
- }
- static int
- print_gid(const char *field, struct group *gr, gid_t gid)
- {
- if(gr && gr->gr_name)
- {
- if(name_flag) return printf("%s=%s", field, gr->gr_name);
- return printf("%s=%u(%s)", field, gid, gr->gr_name);
- }
- else
- {
- if(name_flag) return -1;
- return printf("%s=%u", field, gid);
- }
- }
- static int
- print_uid(const char *field, struct passwd *pw, uid_t uid)
- {
- if(pw && pw->pw_name)
- {
- if(name_flag) return printf("%s=%s", field, pw->pw_name);
- return printf("%s=%u(%s)", field, uid, pw->pw_name);
- }
- else
- {
- if(name_flag) return -1;
- return printf("%s=%u", field, uid);
- }
- }
- static void
- safe_getpwuid(uid_t uid, struct passwd *res)
- {
- struct passwd *pw = getpwuid(uid);
- if(pw != NULL) *res = *pw;
- }
- enum id_modes
- {
- ID_NORMAL,
- ID_GROUPS,
- ID_GID,
- ID_UID,
- };
- static void
- usage(void)
- {
- fprintf(stderr, "Usage: id [-Ggu] [-nr] [user]\n");
- }
- int
- main(int argc, char *argv[])
- {
- int ret = 0;
- enum id_modes mode = ID_NORMAL;
- bool real_flag = false;
- int ngroups = 0;
- int ngroups_max = (int)sysconf(_SC_NGROUPS_MAX) + 1;
- if(ngroups_max <= 0)
- {
- perror("id: error: sysconf(_SC_NGROUPS_MAX)");
- return 1;
- }
- gid_t *groups = NULL;
- groups = calloc(ngroups_max, sizeof(*groups));
- if(groups == NULL)
- {
- perror("id: error: calloc(groups)");
- return 1;
- }
- // geteuid, getuid, getegid, getgid shall always be successful
- uid_t uid = getuid();
- uid_t euid = geteuid();
- gid_t gid = getgid();
- gid_t egid = getegid();
- struct passwd pw = {.pw_uid = uid, .pw_gid = gid};
- struct passwd epw = {.pw_uid = euid, .pw_gid = egid};
- int c = -1;
- while((c = getopt(argc, argv, ":Ggunr")) != -1)
- {
- switch(c)
- {
- case 'G':
- mode = ID_GROUPS;
- break;
- case 'u':
- mode = ID_UID;
- break;
- case 'g':
- mode = ID_GID;
- break;
- case 'n':
- name_flag = true;
- break;
- case 'r':
- real_flag = true;
- break;
- case '?':
- fprintf(stderr, "%s: error: Unhandled option '-%c'\n", argv0, optopt);
- usage();
- free(groups);
- return 1;
- default:
- fprintf(stderr, "%s: error: Unhandled getopt case '%c'\n", argv0, c);
- usage();
- free(groups);
- return 1;
- }
- }
- argc -= optind;
- argv += optind;
- if(argc == 0)
- {
- safe_getpwuid(uid, &pw);
- safe_getpwuid(euid, &epw);
- // Get groups from currently running process instead of configuration
- ngroups = getgroups(ngroups_max, groups);
- if(ngroups < 0)
- {
- perror("id: error: getgroups");
- goto failure;
- }
- }
- else if(argc == 1)
- {
- struct passwd *pw_n = getpwnam(argv[0]);
- if(pw_n == NULL)
- {
- goto failure;
- }
- pw = *pw_n;
- epw = *pw_n;
- uid = pw.pw_uid;
- euid = epw.pw_uid;
- gid = pw.pw_gid;
- egid = epw.pw_gid;
- // Can only get groups from configuration
- if(getgrouplist(pw.pw_name, pw.pw_gid, groups, &ngroups_max) < 0)
- {
- perror("id: error: getgrouplist");
- goto failure;
- }
- ngroups = ngroups_max;
- }
- else
- {
- usage();
- }
- struct group *gr = getgrgid(gid);
- struct group *egr = getgrgid(egid);
- if(mode == ID_GID)
- {
- if(!real_flag) gid = egid;
- if(!name_flag)
- {
- ret = printf("%u\n", gid);
- }
- else
- {
- if(gr == NULL || gr->gr_name == NULL)
- {
- ret--;
- fprintf(stderr, "%s: error: Cannot find name for group ID %u\n", argv0, gid);
- printf("%u\n", gid);
- }
- else
- {
- ret = printf("%s\n", gr->gr_name);
- }
- }
- if(ret < 0) goto failure;
- goto done;
- }
- if(mode == ID_UID)
- {
- if(!real_flag) uid = euid;
- if(!name_flag)
- {
- ret = printf("%u\n", uid);
- }
- else
- {
- if(pw.pw_name == NULL)
- {
- ret--;
- fprintf(stderr, "%s: error: Cannot find name for user ID %u\n", argv0, uid);
- printf("%u\n", uid);
- }
- else
- {
- ret = printf("%s\n", pw.pw_name);
- }
- }
- if(ret < 0) goto failure;
- goto done;
- }
- if(mode == ID_GROUPS)
- {
- if(real_flag)
- {
- ret = simple_list_groups(&pw, ngroups, groups);
- }
- else
- {
- ret = simple_list_groups(&epw, ngroups, groups);
- }
- if(ret != 0) goto failure;
- goto done;
- }
- ret = print_uid("uid", &pw, uid);
- if(ret < 0) goto failure;
- if(euid != uid)
- {
- ret = print_uid(" euid", &epw, euid);
- if(ret < 0) goto failure;
- }
- ret = print_gid(" gid", gr, gid);
- if(ret < 0) goto failure;
- if(egid != gid)
- {
- ret = print_gid(" egid", egr, egid);
- if(ret < 0) goto failure;
- }
- if(list_groups(&pw, ngroups, groups) != 0) goto failure;
- ret = printf("\n");
- if(ret < 0) goto failure;
- done:
- free(groups);
- return 0;
- failure:
- free(groups);
- return 1;
- }