Unix/Linux编程:进程凭证

进程凭证

每个进程都有一套用数字标识的用户ID(UID)和组ID(GID)。这些ID也被叫做进程凭证。具体如下:

  • 实际用户 ID(real user ID)和实际组 ID(real group ID)
  • 有效用户 ID(effective user ID)和有效组 ID(effective group ID)。
  • 保存的 set-user-ID(saved set-user-ID)和保存的 set-group-ID(saved set-group-ID)。
  • 文件系统用户 ID(file-system user ID)和文件系统组 ID(file-system group ID)(Linux专有)
  • 辅助组 ID。

实际用户 ID 和实际组 ID

实际用户 ID 和实际组 ID 确定了进程所属的用户和组。作为登录过程的步骤之一,登录shell 从/etc/passwd 文件中读取相应用户密码记录的第三字段和第四字段,置为其实际用户 ID和实际组 ID。当创建新进程(比如,shell 执行一程序)时,将从其父进程中继承这些 ID。

有效用户 ID 和有效组 ID

在大多数Unix实现中,当进程尝试执行各种操作(即系统调用)时,将结合有效用户ID、有效组ID、辅助组ID一起来确定授予进程的权限。比如:

  • 当进程访问文件、System V进程间通信(IPC)对象之类的系统资源时,此类ID会决定系统授予进程的权限,而这些资源的属主则另由与之相关的用户 ID和组 ID 来决定。
  • 内核还会使用有效用户 ID 来决定一个进程是否能向另一个进程发送信号
  • 有效用户 ID 为 0(root 的用户 ID)的进程拥有超级用户的所有权限。这样的进程又称为特权级进程(privileged process)。而某些系统调用只能由特权级进程执行
  • 通常,有效用户 ID 及组 ID 与其相应的实际 ID 相等,但有两种方法能够致使二者不同。其一是使用下面目录[获取和修改进程凭证]中所讨论的系统调用,其二是执行 set-user-ID 和 set-group-ID 程序。

Set-User-ID 和 Set-Group-ID 程序

  • set-user-ID 程序会将进程的有效用户 ID 置为可执行文件的用户 ID(属主),从而获得常规情况下并不具有的权限。、
  • set-group-ID 程序对进程有效组 ID 实现类似任务。
  • 术语 set-user-ID程序和 set-group-ID 程序有时也简称为 set-UID 程序和 set-GID 程序

与其他文件一样,可执行文件的用户 ID 和组 ID 决定了该文件的所有权。另外,可执行文件还拥有两个特别的权限位 set-user-ID 位和 set-group-ID 位。(实际上,任何文件都是如此,但此处只关注可执行文件的这两个权限位。)可使用 chmod 命令来设置这些权限位。非特权用户能够对其拥有的文件进行设置,而特权级用户(CAP_FOWNER)能够对任何文件进行设置。例如:

在这里插入图片描述
正如本例所示,也有可能对这两个权限位都进行设置,虽然这一做法并不常见。当使用ls –l 命令查看文件权限时,如果为程序设置了 set-user-ID 权限位和 set-group-ID 权限位,那么通常用来表示文件可执行权限的 x 标识会被 s 标识所替换
在这里插入图片描述

Linux 系统中经常使用的 set-user-ID 程序包括:

  • passwd(1),用于更改用户密码;
  • mount(8) 和 umount(8),用于加载和卸载文件系统
  • su(1),允许用户以另一用户的身份运行 shell。

set-group-ID 程序的例子之一为 wall(1),用来向 tty 组下辖的所有终端(通常情况下,所有终端都属于该组)写入一条消息

有些程序必须以root用户身份运行,比如需要访问/etc/shadow的程序。对于这样的程序,如果我们欲使该程序可为任一用户执行,必须将其设置为 set-user-ID-root程序,如下所示:
在这里插入图片描述

保存 set-user-ID 和保存 set-group-ID

设计保存 set-user-ID(saved set-user-ID)和保存 set-group-ID (saved set-group-ID),意在与set-user-ID 和 set-group-ID 程序结合使用。当执行程序时,将会(依次)发生如下事件(在诸多事件之中)

  • 若可执行文件的 set-user-ID (set-group-ID)权限位已开启,则将进程的有效用户(组)ID置为可执行文件的属主。若未设置 set-user-ID (set-group-ID)权限位,则进程的有效用户(组)ID 将保持不变\
  • 保存 set-user-ID 和保存 set-group-ID 的值由对应的有效 ID 复制而来。无论正在执行的文件是否设置了 set-user-ID 或 set-group-ID 权限位,这一复制都将进行。

辅助组ID

  • 辅助组ID用于标识进程所属的若干附加组。
  • 新进程从其父进程处继承这些ID,登录shell从系统组文件中获取其辅助组ID。
  • 将这些 ID 与有效 ID 以及文件系统 ID相结合,就能决定对文件、System V IPC 对象和其他系统资源的访问权限

文件系统用户 ID 和组 ID

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

通常,文件系统用户 ID 和组 ID 的值等同于相应的有效用户 ID 和组 ID(因而一般也等同于相应的实际用户 ID 和组 ID)。此外,只要有效用户或组 ID 发生了变化,无论是通过系统调用,还是通过执行 set-user-ID 或者 set-group-ID 程序,则相应的文件系统 ID 也将随之改变为同一值。由于文件系统 ID 对有效 ID 如此的“亦步亦趋”,这意味着在特权和权限检查方面,Linux 实际上跟其他 UNIX 实现非常类似。只有当使用 Linux 特有的两个系统调用(setfsuid() 和 setfsgid())时,才可以刻意制造出文件系统 ID 与相应有效 ID 的不同,因而 Linux 也不同于其他的 UNIX 实现

自内核 2.0 起,Linux 开始在信号发送权限方面遵循 SUSv3 所强制规定的规则,且这些规则不再涉及目标进程的有效用户 ID(参考 20.5 节)。因此,从严格意义上来讲,保留文件系统 ID 特性已无必要(如今,进程可以根据需要,审慎而明智地利用本章稍后介绍的系统调用,使以非特权值对有效用户 ID 的赋值来去自由,以实现预期结果),但为了与现有软件保持兼
容,这一功能得以保留了下来

获取和修改进程凭证

为了获取和变更本章已然论及的各种用户 ID 和组 ID,Linux 提供了一系列系统调用和库函数。SUSv3 仅对这些 API 中的部分做了规范,余下部分中,有一些在其他 UNIX实现中得以广泛应用,还有少量是 Linux 所特有的。

可以利用 Linux 系统特有的 proc/PID/status 文件,通过对其中 Uid、Gid 和 Groups各行信息的检查,来获取任何进程的凭证,这与下面即将介绍的系统调用有异曲同工之妙。Uid 和 Gid 各行,按实际、有效、保存设置和文件系统 ID 的顺序来展示相应标识符

获取和修改实际、有效和保存设置标识

能完成这些任务的系统调用有多个,有时彼此间的功能还相互重叠,这是由于各种系统调用分别源于不同的UNIX 实现

获取实际ID和有效ID

  • 系统调用 getuid()和 getgid()分别返回调用进程的实际用户 ID 和组 ID。
  • 系统调用geteuid()和 getegid()分别返回调用进程的有效用户 ID 和组 ID。
NAME
       getuid, geteuid - 获取用户ID

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

       uid_t getuid(void);
       uid_t geteuid(void);

DESCRIPTION
       getuid() 返回调用进程的真实用户ID 

       geteuid() 返回调用进程的有效用户ID 

ERRORS
       对这些系统函数的调用总会成功

CONFORMING TO
       POSIX.1-2001, 4.3BSD.

NOTES
   History
       在UNIX V6中,返回了getuid()调用(euid << 8+ uid。 UNIX V7引入了单独的调用getuid()geteuid()。

       原始的Linux getuid()geteuid()系统调用仅支持16位用户ID。 随后,Linux 2.4添加了支持32位ID的getuid32()geteuid32()。 glibc的getuid()geteuid()包装函数透明地处理了内核版本之间的差异。 
NAME
       getgid, getegid - get group identity

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

       gid_t getgid(void);
       gid_t getegid(void);

DESCRIPTION
       getgid() 返回调用进程的组用户ID 

       getegid() 返回调用进程的有效组ID 

ERRORS
       对这些系统函数的调用总会成功.

CONFORMING TO
       POSIX.1-2001, 4.3BSD.

NOTES
       原始的Linux getgid()getegid()系统调用仅支持16位组ID。 随后,Linux 2.4添加了支持32位ID的getgid32()getegid32()。 glibc getgid()getegid()包装函数透明地处理内核版本之间的差异。 

修改有效 ID

NAME
       setuid - 设置uid

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

       int setuid(uid_t uid);

DESCRIPTION
       ....

RETURN VALUE
       成功时,返回零。 如果出错,则返回-1,并正确设置errno。 

NAME
       setuid - set user identity

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

       int setgid(uid_t uid);

DESCRIPTION
       ....

RETURN VALUE
       成功时,返回零。 如果出错,则返回-1,并正确设置errno。


进程使用 setuid()和 setgid()系统调用能对其凭证做哪些修改呢?其规则取决于进程是否拥有特权(即有效用户 ID 为 0)。适用于 setuid()系统调用的规则如下

  • 当非特权进程调用setuid()时,仅能修改进程的有效用户ID。而且,仅能将有效用户 ID修改成相应的实际用户 ID 或保存 set-user-ID。(企图违反此约束将引发 EPERM 错误。)

    • 这意味着,对于非特权用户而言,仅当执行 set-user-ID 程序时,setuid()系统调用才起作用,因为在执行普通程序时,进程的实际用户 ID、有效用户 ID 和保存 set-user-ID 三者之值均相等。
    • 在一些派生自 BSD 的实现中,非特权进程对 setuid() 或 setgid()的调用,其语义有别于与其他 UNIX 实现:系统调用会修改实际、有效和保存设置 ID(将其改为当前的实际或有效 ID 值)。
  • 当特权进程以一个非 0 参数调用 setuid()时,其实际用户 ID、有效用户 ID 和保存 set-user-ID均被置为 uid 参数所指定的值

    • 这一操作是单向的,一旦特权进程以此方式修改了其 ID,那么所有特权都将丢失,且之后也不能再使用 setuid()调用将有效用户 ID 重置为 0。
    • 如果不希望发生这种情况,请使用 seteuid()或者 setreuid()系统调用来替代 setuid()。

使用 setgid()系统调用修改组 ID 的规则与之相类似,仅需要把 setuid()替换为 setgid(),把用户替换为组。因之,规则 1 与前述完全一致,但在规则 2 中,由于对组 ID 的修改不会引起进程特权的丢失(拥有特权与否由有效用户 ID 决定),特权级程序可以使用 setgid()对组 ID进行任意修改

对 set-user-ID-root 程序(即其有效用户 ID 的当前值为 0)而言,以不可逆方式放弃进程所有特权的首选方法是使用下面的系统调用(以实际用户 ID 值来设置有效用户 ID 和保存set-user-ID)

if(setuid(getuid()) == -1){
	perror("setuid");
	exit(0);
}

获取和修改文件系统 ID

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值