用户ID的基本概念
在Unix系统中,很多操作涉及到权限问题,这些权限涉及到用户ID和组ID的概念。
组ID和用户ID的原理和相关内容是类似的。下面介绍用户ID。
我们常见见到三种关于用户ID的概念。
实际用户ID:real user ID (RUID)
有效用户ID:effective user ID (EUID)
设置用户ID:saved set-user ID (SUID)
这三个ID实际上都是针对进程而言的。
实际用户ID
标识进程的执行者。可以看做是我们登录的ID,由login程序在登录时进行设置,一般不改变。只有超级用户可以改变RUID
有效用户ID
标识进程执行时文件的访问权限,一般情况下与实际用户ID相同,由exec函数进行设置,只有在set-user ID被设置时才可能发生变化。也有不一样的情况,比如我们修改自己密码时需要访问passwd文件,这是需要改变有效用户ID为root才能访问。
设置用户ID
设置用户ID其实是一种机制,有时我们访问一个文件的时候,需要获取这个文件的所有者的权限。如果该文件的设置用户ID位已经被设置,那么程序执行时就可以吧EUID设置为文件所有者的ID,然后save set-user ID用于保存EUID的一个副本。
下面通过man命令来说明三种用户ID
首先我们执行man,因为man程序文件归用户man所有,且SUID位被设置,因此用户id如下:
RUID 我们的id(后面用windeal表示)
EUID man 运行man时通过setuid(getuid())设置
SUID man(从EUID拷贝来)
OK,先在我们开始执行man程序了,接下来我们要做的第一件事就是降低执行的权限,将EUID改回原来权限较小的windeal,而不是一直使用man这个用户id:
RUID windeal(不变)
EUID windeal
SUID man(不变)
因为man程序执行过程中可能需要访问一些手册、配置文件,而这些文件又需要man用户权限。因此,在执行man的过程中,每当需要访问这些文件的时候,我们又把EUID改为man。
RUID windeal(不变)
EUID man
SUID man(不变)
也就是说,在man执行中,EUID会一直切换,需要man权限时,就把EUID改为man,否则就使用windeal一直执行。
关于用户id的几组函数
setuid和setgid
#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
Both return: 0 if OK,−1 on error
如果进程用户具有超级用户权限,则
setuid(
uid)将RUID、EUID、SUID全部设为uid。
如果进程用户没有超级用户权限,且uid等于RUID或者SUID,则
setuid(uid_t
uid)
将EUID设置为uid
如果前面两点都不满足,则errno设置为EPERM,返回-1
setreuid和setregid
#include <unistd.h>
int setreuid(uid_t ruid,uid_t euid);
int setregid(gid_t rgid,gid_t egid);
Both return: 0 if OK,−1 on error
设置调用进程(calling process)的RUID和EUID。 参数ruid和euid任何一个如果设置为-1,则表示该参数对应的RUID或EUID不改变。
非特权进程只能将EUID设置为 RUID、EUID或者SUID
非特权进程只能将RUID设置为RUID或EUID
如果RUID被设置,或者EUID被设置为跟以前的RUID不等的值,则SUID会被设置为新的EUID
seteuid和setegid
#include <unistd.h>
int seteuid(uid_tuid);
int setegid(gid_tgid);
Both return: 0 if OK,−1 on error
这两个函数类似于setuid和setgid,只不过前者只对EUID进行设置。