The theoretical right way to check if an account is in a Unix group

August 24, 2012

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.)


Comments on this page:

From 166.137.88.155 at 2012-09-26 02:09:00:

If you're going to do caching, why not just cache the results as you need them?

By cks at 2012-09-27 12:29:47:

High-level caching only helps if you repeatedly check to see whether a single person is in a group. In the case I ran into, the code was doing a group membership check on almost all logins. It could (and should) have cached the low-level results of scanning all groups, but didn't (possibly because the programmers didn't want to deal with all of the issues that caching brings up).

Written on 24 August 2012.
« Illustrating the Ubuntu clown car, AccountsService edition
Some odd behavior from blog comment spammers »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Fri Aug 24 01:22:06 2012
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.