linux进程凭证(权限)

每个进程都有一套用数字表示的用户id和组id,这些id决定了进程执行时具体的权限。

实际用户id和实际组id

这2个id决定了进程所属的用户和组。假设我们用root用户登录,那么我们在shell中创建的新进程都会从父进程shell中继承这些id。

有效用户id和有效组id

进程将结合有效用户id、有效组id、辅助组id来决定进程的权限。比如访问一些资源,能否给另一个进程发送信号。有效用户id为0的特权进程拥有所有权限。有些系统调用只能由特权进程执行。

通常,有效用户id及有效组id与实际用户id,实际组id相等,有两种方法可以改变。
1.下面讲到的系统调用
2.执行set-user-ID和set-group-ID的程序

设置用户id(set-user-ID)和设置组id(set-group-ID)程序

可执行文件的用户id和组id决定了该文件的所有权。另外,可执行文件还拥有两个特别的权限位,set-user-id位和set-group-id位。可使用chmod命令来设置这些权限位。非特权用户可以对自己拥有的文件进行设置,特权用户能够对任何文件设置。

[root@izj6cfw9yi1iqoik31tqbgz c]# ll a.out
-rwxr-xr-x 1 root root 13336 May 21 11:32 a.out

[root@izj6cfw9yi1iqoik31tqbgz c]# chmod u+s a.out
[root@izj6cfw9yi1iqoik31tqbgz c]# chmod g+s a.out
[root@izj6cfw9yi1iqoik31tqbgz c]# ll a.out
-rwsr-sr-x 1 root root 13336 May 21 11:32 a.out

从上例可以看出,当设置了set-user-id和set-group-id权限位时,那么可执行位x标识被替换成s。

当运行set-user-id程序时,内核会将进程的有效用户id设置为该文件的用户id。set-group-id程序一样。通过这种方法可以修改进程的有效用户id或者有效组id。

我们知道/etc/shadow文件记录了密码信息,只有root用户才能访问,但是普通用户却可以用passwd命令来修改自己的密码,要使修改后的密码存入/etc/shadow文件,那么普通用户必须要能对文件/etc/shadow有操作权限,所以可以得出,passwd命令肯定设置了set-user-id位。

保存设置用户id和保存设置组id

当用户执行程序时,将会发生如下事件

1.若可执行文件设置了设置用户id(设置组id),则进程的有效用户(组)id置为可执行文件的拥有者,若未设置,则保持不变。
2.此时,保存设置用户id,和保存设置组id值由对应的有效用户(组)id复制过来,不管有没设置set-user-id或set-group-id权限位。

文件系统用户id和组id

在linux系统中,要进行诸如打开文件,改变文件属主,修改文件权限之类的操作,决定的是文件系统用户id和组id(结合辅助组id),而非有效用户id和组id。

通常,只要有效用户或有效组id发生了变化,则相应的文件系统id或组id也将随之改变为同一值。之所以存在这两个id,是有历史因素在。目前,从严格意义来讲,这2种id保留已无必要。所以不再累述。

辅助组id

辅助组id用户标识进程所属的若干附加的组。将这些id和有效id相结合就能决定进程的权限。


获取和修改进程凭证

#include <unistd.h>

uid_t getuid(void);    //获取实际用户id
uid_t geteuid(void);   //获取有效用户id

gid_t getgid(void);    //获取实际组id
gid_t getegid(void);   //获取有效组id

int setuid(uid_t uid);  //设置实际用户id,有效用户id,保存用户id
int setgid(gid_t gid);  //设置实际组id,实际有效组id,实际保存组id

1.当非特权进程调用setuid()时,只能修改进程的有效用户id,而且只能将有效用户id改成相应的实际用户id。
2.特权用户调用setuid()设置uid为非0时,会同时设置实际用户id,有效用户id,和保存设置用户id,且为单向的,因为设置了之后进程就失去了特权。

setgid原理跟setuid一样,只是它在特权用户下可以随便设置。

#include <unistd.h>

int seteuid(uid_t euid);    //设置有效用户id
int setegid(gid_t egid);    //设置有效组id
//返回-1代表失败

特权用户可以将其有效id修改为任意值,但是修改为非0后将失去特权,但是可以恢复

下面是普通用户运行set-user-id程序的例子

euid = geteuid();    //保存有效用户id

seteuid(getuid());   //失去特权
seteuid(euid);       //恢复特权


修改实际id和有效id

#include <unistd.h>

int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);

//返回-1代表失败


linux独有的获取实际、有效和保存设置id

#define _GNU_SOURCE
#include <unistd.h>

int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);

//返回-1代表失败,成功返回0


linux独有的设置实际、有效和保存设置id

#define _GNU_SOURCE
#include <unistd.h>

int setresuid(uid_t ruid, uid_t euid, uid_t suid);
int setresgid(gid_t rgid, gid_t egid, gid_t sgid);

//返回-1代表失败,成功返回0


linux特有的修改文件系统id

#include <sys/fsuid.h>
int setfsuid(uid_t fsuid);
int setfsgid(gid_t fsgid);

//返回之前的文件系统id


获取和修改辅助组id

#include <unistd.h>

int getgroups(int gidsetsize, gid_t grouplist[]);

//返回grouplist里的元素个数,错误返回-1
//如果gidsetsize=0则返回进程属组个数

#include <limits.h>
gid_t grouplist[NGROUPS_MAX + 1]
//考虑到移植性,数组中可能包含有效组id,所以要+1

grouplist内存自己手动分配,gidsetsize代表组id个数。


修改辅助组id

#define _BSD_SOURCE
#include <grp.h>

int setgroups(size_t gidsetsize, const gid_t *grouplist);
int initgroups(const char *user, gid_t group);
//返回0代表成功,返回-1代表失败

setgroups()系统调用用grouplist里集合替换进程的辅助组id,gidsetsize代表个数

initgroups()函数将扫描/etc/group文件为user创建属组列表,也会把group追加到进程辅助组id,主要是用户创建登录会话程序-login(1);


例子

以下为nobody用户调用root用户的程序a.out(此程序为设置用户id程序),nobody用户id为99
ll a.out
-rwsr-xr-x 1 root root 8728 May 21 17:39 a.out
#define _BSD_SOURCE     /* Get getpass() declaration from <unistd.h> */
#define _XOPEN_SOURCE   /* Get crypt() declaration from <unistd.h> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define _GNU_SOURCE
#include <unistd.h>
#include <limits.h>


int main(int argc, char *argv[]){
    uid_t ruid;
    uid_t euid;
    uid_t suid;
    
    getresuid(&ruid, &euid, &suid);
    
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    //失去特权
    seteuid(2);
    getresuid(&ruid, &euid, &suid);
    printf("特权调用 seteuid(2);\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    setuid(4);
    getresuid(&ruid, &euid, &suid);
    printf("非特权调用 setuid(4);\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    setuid(99);
    getresuid(&ruid, &euid, &suid);
    printf("非特权调用 setuid(99);\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    //恢复特权
    seteuid(0);
    //setuid(0);
    getresuid(&ruid, &euid, &suid);
    printf("非特权调用 seteuid(0);---恢复特权\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    setuid(0);
    getresuid(&ruid, &euid, &suid);
    printf("特权调用 setuid(0);\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    //不可逆失去特权
    setuid(2);
    getresuid(&ruid, &euid, &suid);
    printf("特权调用 setuid(2);---永久失去特权\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
    
    setuid(0);
    getresuid(&ruid, &euid, &suid);
    printf("非特权调用 setuid(0);\n");
    printf("real id:%d  effective id:%d  saved id:%d\n\n", ruid, euid, suid);
}

/*
real id:99  effective id:0  saved id:0

特权调用 seteuid(2);
real id:99  effective id:2  saved id:0

非特权调用 setuid(4);
real id:99  effective id:2  saved id:0

非特权调用 setuid(99);
real id:99  effective id:99  saved id:0

非特权调用 seteuid(0);---恢复特权
real id:99  effective id:0  saved id:0

特权调用 setuid(0);
real id:0  effective id:0  saved id:0

特权调用 setuid(2);---永久失去特权
real id:2  effective id:2  saved id:2

非特权调用 setuid(0);
real id:2  effective id:2  saved id:2
*/


上一篇: linux用户和组
下一篇: linux c时间
作者邮箱: 203328517@qq.com