If I modify group membership and admins using the gpasswd command and add a user as a group admin Linux will allow that user to perform tasks otherwise reserved by root.


[test1@centos ~]$ gpasswd -a test6 test
Adding user test6 to group test

If I try to perform the same task as a user that has not been added as an admin it is not allowed:

[test2@centos ~]$ gpasswd -a test7 test
gpasswd: Permission denied.

This has to be tracked somewhere. The gpasswd command has the SUID bit set but no indication as to who that is restricted to.

Where is this tracked? How does the gpasswd command know who has been granted elevated permissions?


Where is this tracked? How does the gpasswd command know who has been granted elevated permissions?

It's not really "tracked", per se. gpasswd has rules for which users have permission to use the command in various ways, and those rules/permissions are tested on every run of the command. The results of those tests are not cached or stored.

In general, to run gpasswd you must either be root, or you must be defined as an administrative member of the group you are targeting, or you must be the first member of the group you are targeting.

If you'd like to get into specifics, we can examine the source of gpasswd and other parts of the shadow-utils project.

According to the source for gpasswd (GitHub mirror), depending on the results of #ifdef SHADOWGRP and #ifdef FIRST_MEMBER_IS_ADMIN, one of three checks is run in the check_perms function for a call to gpasswd that does not use the -A or -M flags (comments below are mine):

/* Check 1 */
if (!amroot && !is_on_list (sg->sg_adm, myname)) {
        failure ();

/* Check 2 */
if (!amroot) {
        if (gr->gr_mem[0] == (char *) 0) {
            failure ();

        if (strcmp (gr->gr_mem[0], myname) != 0) {
            failure ();

/* Check 3 */
if (!amroot) {
        failure ();

amroot is a boolean that is true if the root user has called gpasswd. It is defined here:

/* The UID of the caller */
static uid_t bywho;
/* Indicate if gpasswd was called by root */
#define amroot  (0 == bywho)

Being root will "pass" (skip) Check 1, Check 2, and Check 3 (amroot will be true, which means !amroot will be false, so the if statement will be skipped).

A non-root user may pass Check 1 if they are an administrative member of the targeted group. This is tested by passing a list of the targeted group's admin members (sg->sg_adm) and the username of the user who called gpasswd (myname) to the function is_on_list, defined here. If the calling user is in the group admin list, is_on_list returns true, which gets inverted to false, which allows the user to pass Check 1.

A non-root user may pass Check 2 if they are the first listed member of the group they are trying to change. This is tested by verifying that the first user in the targeted group is equal to the user who called gpasswd.

A non-root user cannot pass Check 3.

Note: All the source links that I provided are linked to the "debian/4.2-3" tag in the source repository. This is to provide consistent references.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.