实际用户ID、有效用户ID、保存的设置用户ID

简介

与进程相关的id简介查看方式如何设置
实际用户ID (Real User ID, RUID)进程被创建时的用户IDps、cat /proc/PID/status、getuid()一般不可更改
实际组ID (Real Group ID, RGID)进程被创建时的组IDps、cat /proc/PID/status、getgid()一般不可更改
有效用户ID (Effective User ID, EUID)进程访问文件或其他资源时使用的用户IDps、cat /proc/PID/status、geteuid()、getreuid()、getresuid()setuid()、seteuid()、setreuid()、setresuid()
有效组ID (Effective Group ID, EGID)进程访问文件或其他资源时使用的组IDps、cat /proc/PID/status、getegid()、getregid()、getresgid()setuid()、setegid()、setregid()、setresgid()
保存的设置用户ID (saved set-user-ID,saved SUID)程序文件特殊权限,运行时设置EUIDls、ps、cat /proc/PID/status、getresuid()chmod u+s、getresuid()
保存的设置组ID (saved set-group-ID,saved SGID)程序文件特殊权限,运行时设置EGIDls、ps、cat /proc/PID/status、getresgid()chmod g+s、getresgid()

Linux中添加“组”的概念是为了什么?

  1. 进行访问控制。通过设置文件或进程的组权限,可以控制哪些用户组有权访问它们。
  2. 实现资源共享。同一个组的用户可以共享文件或其他资源的访问权限。
  3. 简化权限管理。通过设置组,可以一次性地给多用户设置权限,简化管理。
  4. 兼容老系统。大多数老系统都有组的概念,Linux为了兼容也继承了这一设计。
  5. 隔离安全域。设置不同的用户组,可以将系统资源隔离到不同的安全域中。
  6. 账户跟踪(Accounting Tracking),按用户组记录和跟踪系统资源占用情况。

所以接下来的讲述中,用户ID与组的操作方法大都类似,就不分别展示了。

一、实际用户ID和实际组ID

用于标识进程所属的用户身份。RUID与RGID是进程创建时绑定,并且在进程生命周期内保持不变。

1、RUID是指登录当前会话的用户id吗?如何证明?

不完全正确。证明如下:

# 以用户root登录, 查看当前RUID
[root@localhost ~]# id
uid=0(root) gid=0(root)=0(root)

# 添加新用户newone, 并登录, 查看RUID
[root@localhost ~]# useradd newone
[root@localhost ~]# su newone
[newone@localhost root]$ id
uid=1000(newone) gid=1000(newone)=1000(newone)

# 使用newone启动一个后台进程, 并退出
[newone@localhost root]$ sleep 1000 &
[1] 168778
[newone@localhost root]$ exit

# 查看sleep进程的RUID为newone id
[root@localhost ~]# ps -p 168778 -eo pid,ruid,rgid,comm | grep -e "sleep" -e "PID"
    PID  RUID  RGID COMMAND
 168778  1000  1000 sleep

因此,一个没有终端的系统进程,它的RUID不与任何登录会话对应,会话结束后,原有进程的RUID也不会改变。

2、如何查看RUID、RGID

# 方法1
ps -eo ruid,rgid,euid,egid,suid,sgid,pid,comm
# 如需要指定进程id,则使用以下命令
ps -eo ruid,rgid,euid,egid,suid,sgid,pid,comm -q 3141
 RUID  RGID  EUID  EGID  SUID  SGID     PID COMMAND
    0     0     0     0     0     0    3141 SimuService

# 方法2
cat /proc/self/status | grep -e Uid -e Gid
# 如需要指定进程id,则使用以下命令
cat /proc/PID/status | grep -e Uid -e Gid
Uid:    0       0       0       0
Gid:    0       0       0       0

# Uid、Gid每列表示的信息为:
# Real, effective, saved set, and filesystem UIDs (GIDs).
# 其中filesystem UID(文件系统用户ID),是指分配给每个用户目录的用户ID。
# 每个用户目录都拥有一个唯一的用户ID和组ID。这个用户ID被称为文件系统用户ID。
# 当一个用户登录系统时,会将当前用户的EUID设置为文件系统用户ID。这样用户就可以访问自己家目录中的文件。
# 文件系统中的每个文件和目录都保存了用户ID和组ID。读取文件时会检查用户的有效ID是否匹配文件所有者ID,以确定访问权限。

# 方法3,系统调用getuid()/getgid(),下文中展示

二、有效用户ID和有效组ID

进程用EUID来决定我们对资源的访问权限。一般情况下,EUID等于RUIDEGID等于RGID但有效用户ID不是一成不变的,它是可以更改的,有效用户组ID也一样。

1、如何更改EUID?

查看EUID的方法前面已经展示了。更改EUID的方法有两种:第一种方法是通过系统调用修改;第二种方法则是通过修改保存设置用户ID,在第3节中讲述。

为什么要修改进程的有效用户ID? 就是想在某一时刻能够执行一些特权操作。

2、setuid()

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

int setuid(uid_t uid);

setuid()设置调用进程的有效用户ID。如果调用进程具有权限,更准确地说:如果该进程在其user namespace(用户命名空间,用于隔离用户环境)中具有CAP_SETUID功能,那么还将同时设置RUIDSUID

不是说RUID是不可以更改的吗?怎么还能通过系统调用去设置?
进程的RUID在进程生命周期内原则上是不可更改的。但是root可以修改RUID的值。举例:

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

#define PRINT_ID() {printf("RUID:%-4d\t EUID:%-4d\t RGID:%-4d\t EGID:%-4d\n", getuid(), geteuid(), getgid(), getegid());}

int main()
{
    PRINT_ID();
    setuid(300);
    PRINT_ID();
    return 0;
}

使用newone用户(属于root组,但无root权限),输出结果如下:

[newone@KL138 ~]$ ./a.out 
RUID:1000        EUID:1000       RGID:0          EGID:0
RUID:1000        EUID:1000       RGID:0          EGID:0

无法改变EUIDRUID

使用root用户,输出结果如下:

[root@KL138 newone]# ./a.out 
RUID:0           EUID:0          RGID:0          EGID:0   
RUID:0           EUID:0          RGID:0          EGID:0

成功改变了EUIDRUID

3、seteuid()

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

int seteuid(uid_t euid);
  • 若进程具有超级用户权限,则seteuid将RUIDEUIDSUID设置为euid

  • 若进程没有超级用户权限,但是euid等于RUIDSUID,则seteuid()只将EUID设置为euid

4、setreuid()

用户交换实际用户ID和有效用户ID。

#include <unistd.h>

int setresuid(uid_t ruid, uid_t euid, uid_t suid);

5、setresuid()

用来同时设置进程的RUIDEUIDSUID

#include <unistd.h>

int setresuid(uid_t ruid, uid_t euid, uid_t suid);

// 设置为root权限 
setresuid(0, 0, 0); 

// 恢复调用者权限
setresuid(-1, -1, -1);

6、getuid()、geteuid()、getresuid()、getgid()、getegid()、getresgid()

#include <unistd.h>
uid_t getuid(void);     // 返回:调用进程的实际用户ID
uid_t geteuid(void);    // 返回:调用进程的有效用户ID
uid_t getresuid(void);  // 返回:调用进程的设置用户ID
gid_t getgid(void);     // 返回:调用进程的实际组ID
gid_t getegid(void);    // 返回:调用进程的有效组ID
gid_t getresgid(void);  // 返回:调用进程的有效组ID

// 这些函数都没有出错返回

三、保存的设置用户ID和保存的设置组ID

saved set-user-ID,顾名思义,这两个id存在的价值就是保存,保存谁呢?保存EUIDEGID。当进程的RUIDEUID不同时(组id同理),SUID才有意义。因为这样就可以通过调用setuid()把EUID切换为与进程的RUIDSUID相同的值,不保存下来,我们就弄丢了。它的作用就是为了恢复EUID、EGID

文件属性中有一个特殊标志位s,当该标志设置时,执行该文件时的EUID就是文件的所有者ID。如下:
在这里插入图片描述

当文件的SUID位设置时,EUID等于文件的所有者UID,而不是RUID;同样,如果设置了SGID,则EGID等于文件所有者GID,而不是RGID

1、如何更改SUID、SGID

  • 方法一
chmod u+s a.out
chmod g+s a.out
  • 方法二
#include <unistd.h>

int setresuid(uid_t ruid, uid_t euid, uid_t suid); 
setresuid(0, 0, 0); 

2、举例

对于linux系统来说,用户的密码都存放在/etc/shadow文件下,假如我是一个普通的用户,显然我是可以修改我自己的密码的,通过passwd命令,无可厚非,自己修改自己的密码当然是允许的。

但是仔细想想有没有什么不对的地方,作为一个普通的用户登录后,我的所有的进程的RUID和EUID都应该是我自己(这个用户)的UID。从上面对/etc/shadow文件用户ID和所属的组ID看,我不具备修改这个文件的权限,那么执行passwd命令是怎么修改我的密码的呢?

根据上面所讲的知识,决定进程对文件的访问权限的是执行操作时的EUID,所以在执行passwd命令时,EUID一定被修改过了。还有一点,在执行passwd这个命令的时候,在磁盘上肯定有一个可执行的文件,设置了s权限,上面说过这个位的作用就是修改执行这个可执行文件的进程的EUID,那么我么来看看他是怎样修改EUID的。

首先看一下命令的执行的过程,当普通用户执行passwd命令时,shell会fork出一个子进程,此时该进程的EUID还是这个普通用户的ID,然后exec程序执行/usr/bin/passwd这个例程(可执行文件)。通过上面的表我们就能知道,exec发现/usr/bin/passwd这个可执行文件有S位,于是会把进程的EUID设置成可执行文件的用户ID,显示是root,此时这个进程就获得了root的权限,得到了读写/etc/shadow文件的权限,从而普通用户可以完成密码的修改。exec进程退出后,会恢复普通用户的EUIDRUID(也就是该普通用户的ID),这样就保证了不会是普通用户一直具有root权限。

exec时,修改进程的EUID为文件的RUID,还是恢复进程的EUID为进程的RUID,这些都是linux内核完成的,用户是不能干预这些步骤的。这样的过程既实现了**普通用户权限的暂时的提升**,不让普通用户长久的拥有root权限。

但是你可能疑问,为什么不能用setuid()直接修改SUID呢?

如果这里可以用setuid()直接修改进程的EUID来获得特权权限,那么整个系统的超级权限就不可控制了,这违背了最小权限模型。所以linux的设计是:setuid()在非特权用户下面,EUID只能设置成为RUID或者SUIDSUID又是EUID的副本,EUID只能是RUID或者文件的所有者ID(在设置了保存用户ID位时)。这样你就不能将有EUID设置成随意的值。

所以对普通用户创建的任何文件,如果没有得到超级用户的授权,那么无论怎么编写代码来设置运行进程的EUID或者SUID,由于这个可执行文件是普通用户自己编写的,所以权限根本没有任何改变。这就是说只有root自己创建的可执行文件并且设置了s位,才能够提升执行该可执行文件的进程的权限。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值