Linux 用户信息、切换用户

UID、EUID、GID、EGID

几个常见用户信息概念:UID、EUID、GID、EGID

UID:当前进程的真实用户ID;
EUID:有效用户ID;
真实组ID:GID;
有效组ID:EGID;

可以通过下面这组函数获取和设置当前进程的用户信息:

#include <sys/types.h>
#include <unistd.h>

uid_t getuid();          /* 获取真实用户ID */
uid_t geteuid();         /* 获取有效用户ID */

gid_t getgid();                      /* 获取真实组ID */
gid_t getegid();         /* 获取有效组ID */

int setuid(uid_t uid);   /* 设置真实用户ID */
int seteuid(uid_t uid);  /* 设置有效用户ID */

int setgid(gid_t gid);   /* 设置真实组ID */  
int setegid(gid_t gid);  /* 获取有效组ID */

一个进程有两个用户ID:UID和EUID。有什么区别呢?
EUID存在的目的是方便资源访问,使得运行程序的用户拥有该程序的有效用户的权限,比如su程序,任何用户都可以用它来修改自己的账户信息,但修改账户时,su程序不得不访问/etc/passwd文件,而访问该文件是需要root权限的。

以普通用户身份启动的su程序,如何能访问/etc/passwd文件呢?
诀窍在于EUID。ls命令可以查看到,su程序所有者root,且被设置了set-user-id标志。这个标志表示,任何普通用户运行su程序时,其有效用户就是该程序的所有者root。根据有效用户的含义,任何运行su程序的普通用户都能访问/etc/passwd文件。有效用户为root的进程,称为特权进程(privileged processes)。
EGID含义与EUID类似,给运行目标程序的组用户提供有效组的权限。

比如,ls命令查看uid_test程序权限位,其中"s"表示set-user-id标志。

$ ls -al uid_test
-rwsrwsr-x 1 root root 10391 May  1 14:39 uid_test

下面程序测试进程的UID和EUID的区别

#include <unistd.h>
#include <stdio.h>

int main()
{
    uid_t uid = getuid(); // Get the real user ID
    uid_t euid = geteuid(); // Get the effective user ID
    printf("real userid is %d, effective userid is %d\n", uid, euid);
    return 0;
}

编译完成后,需要将生成的可执行文件(uid_test)所有者设置为root,并设置该文件的set-user-id标志,然后运行该程序查看UID和EUID。具体操作:

$ sudo chown root:root uid_test # 修改目标文件的所有者为root
$ ls -l uid_test
-rwxrwxr-x 1 root root 10391 May  1 14:39 uid_test

$ sudo chmod +s uid_test # 设置目标文件的set-user-id标志
$ ls -al uid_test
-rwsrwsr-x 1 root root 10391 May  1 14:39 uid_test

$ ./uid_test # 运行程序
real userid is 1000, effective userid is 0

可以看到,UID是启动程序的用户的ID,EUID是root账户(文件所有者)的ID。

切换用户

例程:从root用户切换到普通用户。
要求程序以root身份启动,而我的登录用户名为martin的用户uid和gid分别为1000,1000。为简化程序设计,直接将其作为要切换的普通目标用户。

#include <stdio.h>
#include <unistd.h>

/**
* 从root用户切换到目标用户(user_id, gp_id)
* root 用户uid = 0, gid = 0
*/
static bool switch_to_user(uid_t user_id, gid_t gp_id)
{
    /* 先确保目标用户不是root */
    if ((user_id == 0) && (gp_id == 0)) {
        return false;
    }
    /* 确保当前用户是合法用户: root或者目标用户 */
    gid_t gid = getgid();
    uid_t uid = getuid();
    if (((gid != 0) || (uid != 0)) && ((gid != gp_id) || (uid != user_id))) {
        return false;
    }
    /* 如果不是root, 则已经是目标用户 */
    if (uid != 0) {
        return true;
    }

    /* 切换到目标用户 */
    if ((setgid(gp_id) < 0) || (setuid(user_id) < 0)) {
        return false;
    }
    return true;
}

/**
* 要求以root身份启动程序
* user_id = 1000, gp_id = 1000 是我的登录用户 martin
*/
int main()
{
    printf("current uid = %d, euid = %d\n", getuid(), geteuid());
    switch_to_user(1000, 1000);
    printf("current uid = %d, euid = %d\n", getuid(), geteuid());
    return 0;
}

参考

《Linux高性能服务器编程》

posted @ 2022-05-01 18:56  明明1109  阅读(442)  评论(0编辑  收藏  举报