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高性能服务器编程》