setuid & setgid

There are three user IDs that the kernel maintains.

  • real user id, the one who owns the process
  • effective user id,the effective user id is what the operating system looks at to make a decision whether or not you are allowed to do something. When you log in, the login shell sets both the real and effective user id to the same value (your real user id) as supplied by the password file. when set-user-id bit be turn on, while exec the executable file, the effective user ID will be the file owner and the saved uid be the file owner also.
  • saved uid, storage effective user id, make sure previous effective user id can be restored after be modified.

Note that we can obtain only the current value of the real user ID and the effective user ID with the functions getuid and geteuid. We can't obtain the current value of the saved set-user-ID.


There are rules for who can change the IDs. Let’s consider only the user ID for now.(Everything we describe for the user ID also applies to the group ID.)

  1. Only a superuser process can change the real user ID. Normally, the real user ID is set by the login (1) program when we log in and never changes. Because login is a superuser process, it sets all three user IDs when it calls setuid .

  2. The effective user ID is set by the exec functions only if the set-user-ID bit is set for the program file. If the set-user-ID bit is not set, the exec functions leave the effective user ID as its current value. We can call setuid at any time to set the effective user ID to either the real user ID or the saved set-user-ID. Naturally, we can’t set the effective user ID to any random value.

  3. The saved set-user-ID is copied from the effective user ID by exec . If the file’s set-user-ID bit is set, this copy is saved after exec stores the effective user ID from the file’s user ID.

  4. If the process has superuser privileges, the setuid function sets the real user ID, effective user ID, and saved set-user-ID to uid .

  5. If the process does not have superuser privileges, but uid equals either the real user ID or the saved set-user-ID, setuid sets only the effective user ID to uid .The real user ID and the saved set-user-ID are not changed.

  6. If neither of these two conditions is true, errno is set to EPERM and − 1 is returned.

Summarizes the various ways these three user IDs can be changed.

https://hzzz.lengzzz.com/blog/1385/


The Nginx related code snippet makes sense for our learning the usage of setuid and setgid.

if (geteuid() == 0) {
        if (setgid(ccf->group) == -1) {
            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                          "setgid(%d) failed", ccf->group);
            /* fatal */
            exit(2);
        }

        if (initgroups(ccf->username, ccf->group) == -1) {
            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                          "initgroups(%s, %d) failed",
                          ccf->username, ccf->group);
        }

        if (setuid(ccf->user) == -1) {
            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                          "setuid(%d) failed", ccf->user);
            /* fatal */
            exit(2);
        }
    }

Reference from nginx/src/core/ngx_process_cycle.c, ngx_worker_process_init() function.

The nginx related code is not complicated, only permit root user to set user ID. And in this case, the real user id, effective user id and saved user id both are modified due to running in root.

How to fetch uid and gid with username and group name?

#include <pwd.h>
#include <grp.h>

char uname[] = "nobody";
char ugroup[] = "nobody";

int
main() {
    char            *group;
    struct passwd   *pwd;
    struct group    *grp;
    uid_t           uid;
    gid_t           gid;

    pwd = getpwnam((const char *)uname);
    if (pwd == NULL) {
        printf("invalid uname: %s\n", uname);
        exit(1);
    }

    grp = getgrnam(ugroup);
    if (grp == NULL) {
        printf("invalid group: %s\n", ugroup);
        exit(1);
    }

    uid = pwd->pw_uid;
    gid = grp->gr_gid;

    printf("uname %s:%zd, ugroup %s:%zd\n", uname, uid, ugroup, gid);



    if (setuid(uid) != 0) {
        printf("setuid failed\n");
    } else {
        printf("setuid success\n");
    }

    if (setgid(gid) != 0) {
        printf("setgid failed\n");
    } else {
        printf("setgid success\n");
    }

    printf("current uid:%zd, euid:%zd, current gid:%zd\n", getuid(), geteuid(), getgid());


    exit(0);

}

results matching ""

    No results matching ""