From cf677da22c8e64c6651d2a460eab12ea4fa709ea Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 2 Oct 2023 11:04:47 +0100 Subject: [PATCH] Add getgrouplist() support to os_calls On enterprise systems, using getgrouplist() (if available) is more efficient than iterating over the members of the group, and is also more likely to work --- common/os_calls.c | 87 ++++++++++++++++++++++++++++++++++++++--------- common/os_calls.h | 9 +++++ 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/common/os_calls.c b/common/os_calls.c index a58051a5..1afeb570 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -3573,42 +3573,95 @@ g_getgroup_info(const char *groupname, int *gid) } /*****************************************************************************/ -/* returns error */ -/* if zero is returned, then ok is set */ -/* does not work in win32 */ +#ifdef HAVE_GETGROUPLIST +int +g_check_user_in_group(const char *username, int gid, int *ok) +{ + int rv = 1; + struct passwd *pwd_1 = getpwnam(username); + if (pwd_1 != NULL) + { + // Get number of groups for user + // + // Some implementations of getgrouplist() (i.e. muslc) don't + // allow ngroups to be <1 on entry + int ngroups = 1; + GETGROUPS_T dummy; + getgrouplist(username, pwd_1->pw_gid, &dummy, &ngroups); + + if (ngroups > 0) // Should always be true + { + GETGROUPS_T *grouplist; + grouplist = (GETGROUPS_T *)malloc(ngroups * sizeof(grouplist[0])); + if (grouplist != NULL) + { + // Now get the actual groups. The number of groups returned + // by this call is not necessarily the same as the number + // returned by the first call. + int allocgroups = ngroups; + getgrouplist(username, pwd_1->pw_gid, grouplist, &ngroups); + ngroups = MIN(ngroups, allocgroups); + + rv = 0; + *ok = 0; + + int i; + for (i = 0 ; i < ngroups; ++i) + { + if (grouplist[i] == (GETGROUPS_T)gid) + { + *ok = 1; + break; + } + } + free(grouplist); + } + } + } + return rv; +} +/*****************************************************************************/ +#else // HAVE_GETGROUPLIST int g_check_user_in_group(const char *username, int gid, int *ok) { #if defined(_WIN32) return 1; #else - struct group *groups; int i; - groups = getgrgid(gid); - - if (groups == 0) + struct passwd *pwd_1 = getpwnam(username); + struct group *groups = getgrgid(gid); + if (pwd_1 == NULL || groups == NULL) { return 1; } - *ok = 0; - i = 0; - - while (0 != groups->gr_mem[i]) + if (pwd_1->pw_gid == gid) { - if (0 == g_strcmp(groups->gr_mem[i], username)) - { - *ok = 1; - break; - } + *ok = 1; + } + else + { + *ok = 0; + i = 0; - i++; + while (0 != groups->gr_mem[i]) + { + if (0 == g_strcmp(groups->gr_mem[i], username)) + { + *ok = 1; + break; + } + + i++; + } } return 0; #endif } +#endif // HAVE_GETGROUPLIST /*****************************************************************************/ /* returns the time since the Epoch (00:00:00 UTC, January 1, 1970), diff --git a/common/os_calls.h b/common/os_calls.h index 2374df3e..2d5c18e6 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -323,6 +323,15 @@ int g_getuser_info_by_name(const char *username, int *uid, int *gid, int g_getuser_info_by_uid(int uid, char **username, int *gid, char **shell, char **dir, char **gecos); int g_getgroup_info(const char *groupname, int *gid); +/** + * Checks whether a user is in the specified group + * @param username Name of user + * @param gid GID of group + * @param[out] ok Whether user is in group + * @return Non-zero if a system error occurred. In this instance OK is not set + * + * Primary group of username is also checked + */ int g_check_user_in_group(const char *username, int gid, int *ok); int g_time1(void); int g_time2(void);