Unix中access和open函数的区别
《Unix环境高级编程》学习笔记
学习内容:
在书中图4-8提供了access函数运行实例,自己加了些简单的错误检查机制。程序如下:
#include"apue.h"
#include<fcntl.h>//O_RDONLY
int main(int argc, char* argv[])
{
if(argc!=2)
err_quit("usage: ./a.out filename\n");
if(access(argv[1],F_OK)<0)
{
err_quit("The file doesn't exist.\n");
}
else
{
if(access(argv[1],R_OK)<0)//测试读权限
err_ret("access error for %s",argv[1]);
else
printf("read access OK.\n");
if(access(argv[1],W_OK)<0)//测试写权限
err_ret("access error for %s",argv[1]);
else
printf("write access OK.\n");
if(access(argv[1],X_OK)<0)//测试执行权限
err_ret("access error for %s",argv[1]);
else
printf("execute access OK.\n");
if(open(argv[1],O_RDONLY)<0)//只读打开
err_ret("open %s error.",argv[1]);
else
printf("open for reading %s OK.\n",argv[1]);
}
exit(0);
}
该程序主要对比了access和open函数的功能。
access函数可以用来测试 real usr ID (实际用户ID)对某些文件是否具有访问权限(对应int mode的常量参数有R_OK,W_OK,X_OK,F_OK);而open函数的返回结果可以测试用户进程的 effective usr ID (有效用户ID)对某些文件是否有访问权限(对应int oflag的的参数有O_RDONLY,O_WRONLY,O_RWONLY等)。
实际测试:
接下来我们在终端输入命令查看程序运行结果。首先查看此可执行文件的详细信息:
$ ls -l a.out
-rwxrwxr-x 1 sar 15945 Nov 30 12:10 a.out
其中第一位-表示文件,后九位是a.out文件的权限位,依次对应三种身份所拥有的权限,身份顺序为:owner、group、others,权限顺序为:readable、writable、executable。此文件的FUID(程序文件的owner ID)为sar,即程序的拥有者是sar,sar对应的权限有readable,writable和executable(9位文件权限符中的前三位)。
再测试可执行文件的权限:
$ ./a.out a.out
read access OK.
write access OK.
execute access OK.
open for reading OK.
执行./a.out a.out命令时,shell会fork()出一个子进程,该进程是 执行a.out(可执行文件)的进程,进程创建者是登录用户。此时该进程的有效用户ID还是这个实际用户的ID(登陆系统的普通用户ID,通常情况下有效用户ID和实际用户ID是相同的)。而且a.out文件也是该普通用户创建的,那么用open函数读a.out文件时,由于该进程的有效用户ID和a.out文件拥有者ID相同,该进程就得到了“rwx”的权限,从输出结果中最后一行可以得到验证。注:有效用户ID是针对进程的概念,不是针对文件的概念。
而access函数用来测试实际用户ID能否得到相应访问权限。此时的实际用户ID和有效用户ID相同,那么也同时拥有了“rwx”的权限,从前三行的结果中可以看到。
$ ls -l /etc/shadow
-r-------- 1 root 1315 Jul 17 2002 /etc/shadow
在Linux系统中,/etc/shadow文件通常用来存放用户密码。可以看到文件拥有者是root,其权限为可读,其他用户没有任何权限。
再通过用户创建的进程 测试 /etc/shadow文件 权限:
$ ./a.out /etc/shadow
access error for /etc/shadow: Permission denied
access error for /etc/shadow: Permission denied
access error for /etc/shadow: Permission denied
open /etc/shadow error.: Permission denied
由于实际用户ID和有效用户ID相同,而/etc/shadow文件的所有者是root,即实际用户ID和有效用户ID都和文件拥有者ID不匹配,那么就不会得到“r”权限,无论是open还是access函数都不能正常访问。
接下来我们修改a.out文件的拥有者,再测试access和open函数:
$ sudo su
Password:
$ chown root a.out
$ ls -l a.out
-rwxrwxr-x 1 root 15945 Nov 30 12:10 a.out
可以看到文件拥有者变成了root,但此时所属群组还是sar(书中没有写)。
此时再测试a.out可执行文件:
$ ./a.out a.out
read access OK.
write access OK.
execute access OK.
open for reading a.out OK.
按理说文件拥有者ID已经和 进程(执行a.out可执行文件的进程)的有效用户ID、实际用户ID不匹配,为什么该进程还能对a.out文件有相应的访问权限呢?这就涉及到权限匹配的顺序问题,有点类似短路原则:
也就是说该进程的组ID和a.out文件的组ID匹配成功,因为a.out文件的所属群组ID未更改,只是更改了文件所有者。
$ chmod u+s a.out
-rwsrwxr-x 1 root 15945 Nov 30 12:10 a.out
$ ./a.out /etc/shadow
access error for /etc/shadow: Permission denied
access error for /etc/shadow: Permission denied
access error for /etc/shadow: Permission denied
open for reading /etc/shadow OK.
可以看到此时a.out可执行文件的权限位有SUID位,《Unix环境高级编程》中给出的解释是:‘‘When this file is executed, set the effective user ID of the process to be the owner of the file (st_uid).’’
那么当新创建的进程(要执行a.out可执行文件的进程)发现SUID位时,在exec()函数中内核(kernel)就会临时把该进程的有效用户ID更改为a.out的文件拥有者ID。那么此时该进程的有效用户ID为root,调用open函数访问/etc/shadow文件时,用户ID匹配成功,所以能够以只读方式打开shadow文件。
但是此时该进程的实际用户ID仍为普通用户ID,那么当用access函数测试时,就得不到/etc/shadow文件的任何访问权限,得到访问拒绝的结果,从结果中前三行可以看到。
总结:
这篇博客很好的诠释了Linux中有效用户id、实际用户id、设置用户id的区别及进程访问文件机制,对我帮助很大。也欢迎大家评论留言!