写在前面
1. 本文内容对应《 UNIX 环境高级编程》 ( 第 2 版 ) 》第 4 章。
2. 总结了用户 ID 和文件访问权限检查的概念,以及设置用户 ID 的用法。
3. 希望本文对您有所帮助,也欢迎您给我提意见和建议。
用户
ID一个进程与两类 ID 相关:
l 实际用户 ID 和实际组 ID ,用于标识用户究竟是谁。这两个字段在登录时取自口令文件中的登录项。可以使用 getuid 和 getgid 函数查询。
l 有效用户 ID 和有效组 ID ,与附加组 ID 一起,用于文件访问权限检查。可以使用 geteuid 和 getegid 函数查询。通常,等于实际用户 ID 和实际组 ID 。
一个文件仅将其所有者 ID 和所有组 ID 记录在 stat 结构的 st_uid 和 st_gid 字段。
文件访问权限检查
进程每次打开,创建或删除一个文件时,内核就进行文件访问权限检查。内核按顺序执行以下四步,一旦满足就停止检查:
l 若进程的有效用户 ID 是 0 (超级用户),则允许访问。
l 若进程的有效用户 ID 等于文件的所有者 ID ,并且所有者适当的访问权限位被设置,则允许访问,否则拒绝访问。
l 若进程的有效组 ID 或进程的附加组 ID 之一等于文件的组 ID ,并且适当的访问权限位被设置,则允许访问,否则拒绝访问。
l 若其他用户适当的访问权限位被设置,则允许访问,否则拒绝访问。
设置用户
ID可以看到,权限检查依赖于有效 ID ,而与实际 ID 无关。使用 access 函数可以按照实际用户 ID 和实际组 ID 进行权限检查。通常,进程的有效 ID 等于实际 ID 。一个例外是使用设置用户 ID 位和设置组 ID 位。如果一个程序文件的设置用户 ID 位被置位,那么,当执行此文件时,将进程的有效用户 ID 设置为文件所有者 ID ( st_uid )。设置组 ID 的机制与此类似。文件的设置用户 ID 位和设置组 ID 位包含在 stat 结构的 st_mode 字段中,可用宏 S_ISUID 和 S_ISGID 测试。
实验程序如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h>
int main(int argc, char *argv[]) { struct stat st;
if(argc != 2) { printf("usage: a.out <filename>./n"); exit(0); } stat(argv[1], &st); printf("uid=%d, gid=%d, euid=%d, egid=%d./n", getuid(), getgid(), geteuid(), getegid()); printf("%s: st_uid=%d, st_gid=%d./n", argv[1], st.st_uid, st.st_gid); if(access(argv[1], R_OK) < 0) printf("access error for %s./n", argv[1]); else printf("access ok for %s./n", argv[1]); if(open(argv[1], O_RDONLY) < 0) printf("open error for %s./n", argv[1]); else printf("open ok for %s./n", argv[1]); exit(0); } |
运行结果为:
pydeng@pydeng-laptop:~/apue.2e/mytest$ sudo su root@pydeng-laptop:/home/pydeng/apue.2e/mytest# chown root a.out root@pydeng-laptop:/home/pydeng/apue.2e/mytest# chmod u+s a.out root@pydeng-laptop:/home/pydeng/apue.2e/mytest# ls -l a.out -rwsr-xr-x 1 root pydeng 9566 2009-08-12 19:28 a.out root@pydeng-laptop:/home/pydeng/apue.2e/mytest# exit pydeng@pydeng-laptop:~/apue.2e/mytest$ ./a.out /etc/shadow uid=1000, gid=1000, euid=0, egid=1000. /etc/shadow: st_uid=0, st_gid=42. access error for /etc/shadow. open ok for /etc/shadow. |
新文件和目录的所有权
新文件的用户 ID 设置为进程的有效用户 ID 。至于组 ID 有些复杂。在 linux 下,新文件的组 ID 取决于它所在目录的设置组 ID 位是否设置。如果该目录的这一位已经设置,则将新文件的组 ID 设置为目录的组 ID ,否则将新文件的组 ID 设置为进程的有效组 ID 。
实验程序如下:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h>
int main(int argc, char *argv[]) { struct stat st;
if(argc != 2) { printf("usage: a.out <filename>./n"); exit(0); } if(open(argv[1], O_CREAT) < 0) { printf("create file error./n"); exit(0); } stat(argv[1], &st); printf("%s: st_uid=%d, st_gid=%d./n", argv[1], st.st_uid, st.st_gid); exit(0); } |
运行结果为:
pydeng@pydeng-laptop:~/apue.2e/mytest$ sudo su root@pydeng-laptop:/home/pydeng/apue.2e/mytest# chmod 777 /usr/ root@pydeng-laptop:/home/pydeng/apue.2e/mytest# exit pydeng@pydeng-laptop:~/apue.2e/mytest$ ./a.out /usr/mytemp /usr/mytemp: st_uid=1000, st_gid=1000.
pydeng@pydeng-laptop:~/apue.2e/mytest$ sudo su root@pydeng-laptop:/home/pydeng/apue.2e/mytest# chmod g+s /usr/ root@pydeng-laptop:/home/pydeng/apue.2e/mytest# ls -l / drwxrwsrwx 12 root root 4096 2009-08-12 19:46 usr ... root@pydeng-laptop:/home/pydeng/apue.2e/mytest# exit pydeng@pydeng-laptop:~/apue.2e/mytest$ ./a.out /usr/mytemp2 /usr/mytemp2: st_uid=1000, st_gid=0.
pydeng@pydeng-laptop:~/apue.2e/mytest$ sudo su root@pydeng-laptop:/home/pydeng/apue.2e/mytest# chmod g-s /usr root@pydeng-laptop:/home/pydeng/apue.2e/mytest# chmod 755 /usr |