2012-08-24
The theoretical right way to check if an account is in a Unix group
If you are checking once to see if an account is in a group, there is a simple and obvious approach (omitting some details):
grp = getgrnam(GRPNAME); getgrouplist(pwent->pw_name, pwent->pw_gid, &groups, &ngroups); for (i = 0; i < ngroups; i++) { if (groups[i] == grp->gr_gid) return 1; }
There is just one problem
with this: on many systems, getgrouplist()
will re-scan and
re-parse /etc/group
every time you call it. If you call it only
once or twice this usually doesn't matter. If you call it a lot,
this matters a lot (especially if /etc/group
is big).
In theory, the better check is simple. Instead of getting the login's
group list and seeing if the group's GID is in there, you work the other
way around: you get the group's membership (as part of getting the group
entry itself) and then see if the login either has the group's GID as
it's primary group or appears on the list of group members. This avoids
(repeatedly) parsing all of /etc/group
, especially if you cache the
group entry for the group you care about.
However, these two checks are not equivalent and now you have to decide what you care about. The first version checks to see if a login has the group ID of a particular group. The second version checks to see if a login has the group name of a particular group. To see the difference, consider the following group entries:
wheel:x:10:jane cog:x:10:fred
Here is the question: is fred
in group wheel
? If we go by GID
the answer is clearly yes; fred
will have GID 10 as one of his
supplemental groups when he logs in and be able to access files that are
only accessible to GID 10. But if we go by group name, the answer is no;
fred
is in cog
, not wheel
, although they have the same GID. Which
version the software you use cares about is something that you may have
to investigate.
(If you are designing the software, you can decide to make it whichever is more convenient and useful to you.)
The corollary is that if you really do need the GID version and you want
to be fast for a large number of checks, in theory you need to build
some sort of full in-memory index of /etc/group
. In practice, however,
duplicate GIDs are extremely rare and usually not intended so you may be
to be able to ignore them. If not, you can at least scan /etc/group
once to see if the group you care about has duplicated GIDs.
(The fully general version is to scan /etc/group
and accumulate all
of the group entries for groups with the same GID as the group you care
about. Then you check all of their group membership lists. This is going
to require caching in order to make it fast for a large number of logins
and groups.)