saved set user id
saved set user id 相当于是一个 buffer,在 exec 函数启动之后,它会拷贝 effective user id 位的信息覆盖自己。对于非 root 用户来说,可以在未来使用 setuid() 函数将 effective user id 设置成为 real user id 或 saved set user id 中的任何一个。但是不允许非 root 用户用 setuid() 函数把 effective user id 设置成为任何第三个 user id。
对于 root 用户来说,调用 setuid() 的时候,将会设置所有的这三个 user id。
从总体上来看,进程中 real user id, effective user id 和 saved set user id 的设计是为了让 unprivilege user 可以获得两种不同的权限。同时我们也可以得出下面的结论:
Linux 系统通过进程的有效用户 ID(effective user id) 和有效用户组 ID(effective group id) 来决定进程对系统资源的访问权限。
其实我们通过 ps aux 查看的结果中,第一列显示的就是进程的 effective user:
Shell 中外部命令的执行方式
在 shell 中执行的命令分为内部命令和外部命令两种。
内部命令:内建的,相当于 shell 的子函数
外部命令:在文件系统的某个路径下的一个可执行文件
外部命令的执行过程如下:
Shell 通过 fork() 函数建立一个新的子进程,新的子进程为当前 shell 进程的一个副本。
在新的进程里,从 PATH 变量所列出的目录中寻找指定的命令程序。当命令名称包含有斜杠(/)符号时,将略过路径查找步骤。
在新的进程里,通过 exec 系列函数,以所找到的新程序替换 shell 程序并执行。
子进程退出后,最初的 shell 会接着从终端读取并执行下一条命令。
我们通过下面的例子来理解在 shell 中执行外部命令的过程,例子很简单就是通过 cat 命令查看一个文本文件 test.log:
$ cat test.log
我们先来检查一下当前用户以及相关文件的权限:
当前用户 nick 的 real user id 为 1000,/bin/cat 文件的所有者为 root,但是所有人都有执行权限,test.log 文件的所有者为 nick。我们结合下图来介绍 cat test.log 命令的执行过程:
当我们在 shell 中执行一个外部程序的时候,默认情况下进程的 effective user ID 等于 real user ID,进程的 effective group ID 等于 real group ID(接下来的介绍中省略 group ID)。当我们以用户 nick 登录系统,并在 bash 中键入 cat test.log 命令并回车后。Bash 先通过 fork() 建立一个新的子进程,这个新的子进程是当前 bash 进程的一个副本。新的进程在 PATH 变量指定的路径中搜索 cat 程序,找到 /bin/cat 程序后检查其权限。/bin/cat 程序的所有者为 root,但是其他人具有读和执行的权限,所以新进程可以通过 exec 函数用 cat 程序的代码段替换当前进程中的代码段(把 /bin/cat 程序加载到了内存中,此时的进程已经变成了 cat 进程,cat 进程会从 _start 函数开始执行)。由于 cat 进程是由用户 nick 启动的,所以 cat 进程的 effective user ID 是 1000(nick)。同时 cat 进程的 effective user ID 和 test.log 文件的 owner ID 相同(都是 1000),所以 cat 进程拥有对此文件的 rw- 权限,那么顺理成章地就可以读写 test.log 文件的内容了。
下面我们演示一个通过设置特殊权限 set uid ID 改变进程 effective user ID 的例子。
创建文件 root.log,权限为 640,此时只有 root 有权限读写该文件的内容,用户 nick 连读取该文件的权限都没有:
然后通过设置特殊权限 set uid ID 让运行 cat 程序的进程具有 root 权限:
$ sudo chmod 4755 /bin/cat
现在可以了!因为运行 cat 程序的进程的 effective user ID 变成了 root。记得要把 /bin/cat 的权限改回去呀:
$ sudo chmod 755 /bin/cat
Shell 脚本的执行方式在 shell 中执行脚本的方式和执行外部命令的方式差不多,比如我们要执行下面的脚本:
$ /bin/bash ./test.sh