如何检查我是否有权限在Linux上以C打开文件而不打开它?
我希望能够检查文件是否可以在Linux上打开(读或读写)。但是,我无法控制将要打开文件的代码,因此我无法像平常一样打开文件,然后处理错误 我知道,由于在调用返回后但在打开调用之前权限发生更改,所以在任何检查中都会出现争用条件,但我正试图避免从我无法控制的库中记录一些不希望出现的错误 我知道,但我不想尝试复制检查用户ID和组ID的逻辑。使用或,尽管您几乎肯定总是想使用前者。您可以使用:如何检查我是否有权限在Linux上以C打开文件而不打开它?,c,linux,C,Linux,我希望能够检查文件是否可以在Linux上打开(读或读写)。但是,我无法控制将要打开文件的代码,因此我无法像平常一样打开文件,然后处理错误 我知道,由于在调用返回后但在打开调用之前权限发生更改,所以在任何检查中都会出现争用条件,但我正试图避免从我无法控制的库中记录一些不希望出现的错误 我知道,但我不想尝试复制检查用户ID和组ID的逻辑。使用或,尽管您几乎肯定总是想使用前者。您可以使用: access("filename", R_OK); 或 检查您的UID或EUID是否具有相应文件的读取权限。(
access("filename", R_OK);
或
检查您的UID或EUID是否具有相应文件的读取权限。(如果您正在运行setuid,UID和EUID将有所不同)(编辑:添加此选项的原因是使用此方法可以确保您可以避免比赛条件。也就是说,这是一种相当棘手的方法,因此可能仅仅处理潜在的比赛条件是一种更好的实用方法)
如果您的目标是保护您不拥有的代码不受未处理错误的影响,那么使用LD_PRELOAD拦截open调用本身可能会很有用。malloc的一个例子如下:
这里是我关于如何做到这一点的快速即兴创作——基本上是一个拦截器,它将向您发射一个交互式shell来纠正错误
警告:许多打开的调用实际上由于合法的原因而失败,例如,当程序在路径中的不同目录上试图查找文件时,请将此代码视为一个教育示例,仅用于此示例代码-如果您非常接近实际使用,您的代码肯定需要更智能。说了这么多,我们开始吃肉吧
首先,你无法控制的“进攻性”计划:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int res = 0;
printf("About to try to open the file...\n");
res = open("/tmp/unreadable", O_RDONLY);
printf("The result after opening: %d\n", res);
if (res < 0) {
perror("Could not open, and here is what the errno says");
} else {
char buf[1024];
int fd = res;
res = read(fd, buf, sizeof(buf));
printf("Read %d bytes, here are the first few:\n", res);
buf[30] = 0;
printf("%s\n", buf);
close(fd);
}
}
#包括
#包括
#包括
#包括
int main(int argc,char*argv[]){
int res=0;
printf(“即将尝试打开文件…\n”);
res=打开(“/tmp/unreadable”,仅限ordu);
printf(“打开后的结果:%d\n”,res);
如果(res<0){
perror(“无法打开,这是errno所说的”);
}否则{
char-buf[1024];
int-fd=res;
res=读取(fd、buf、sizeof(buf));
printf(“读取%d字节,以下是前几个:\n”,res);
buf[30]=0;
printf(“%s\n”,buf);
关闭(fd);
}
}
然后拦截器:
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
static int (*real_open)(const char *pathname, int flags, ...)=NULL;
static void __open_trace_init(void)
{
real_open = dlsym(RTLD_NEXT, "open");
if (NULL == real_open) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
return;
}
}
int open(const char *pathname, int flags, ...)
{
if(real_open==NULL)
__open_trace_init();
va_list va;
int res = 0;
do {
if (flags & O_CREAT) {
int mode = 0;
va_start(va, flags);
mode = va_arg(va, int);
va_end(va);
fprintf(stderr, "open(%s, %x, %x) = ", pathname, flags, mode);
res = real_open(pathname, flags, mode);
fprintf(stderr, "%d\n", res);
} else {
fprintf(stderr, "open(%s, %x) = ", pathname, flags);
res = real_open(pathname, flags);
fprintf(stderr, "%d\n", res);
}
if (res < 0) {
printf("The open has returned an error. Please correct and we retry.\n");
system("/bin/sh");
}
} while (res < 0);
return res;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义、使用
#包括
静态int(*real_open)(常量字符*路径名,int标志,…)=NULL;
静态无效\打开\跟踪\初始化(无效)
{
real_open=dlsym(RTLD_NEXT,“open”);
如果(空==实开){
fprintf(stderr,“dlsym”中的错误:%s\n”,dlerror();
返回;
}
}
int open(常量字符*路径名,int标志,…)
{
if(实开==NULL)
__打开_trace_init();
va_列表va;
int res=0;
做{
if(flags&O_CREAT){
int模式=0;
va_开始(va,标志);
模式=va_arg(va,int);
va_端(va);
fprintf(标准,“打开(%s,%x,%x)=”,路径名,标志,模式);
res=实际打开(路径名、标志、模式);
fprintf(标准字符,“%d\n”,res);
}否则{
fprintf(stderr,“打开(%s,%x)=”,路径名,标志);
res=实际打开(路径名、标志);
fprintf(标准字符,“%d\n”,res);
}
如果(res<0){
printf(“打开部分返回错误。请更正,我们重试。\n”);
系统(“/bin/sh”);
}
}而(res<0);
返回res;
}
下面是跑步时的样子:
ayourtch@ayourtch-lnx:~$ echo This is unreadable >/tmp/unreadable
ayourtch@ayourtch-lnx:~$ chmod 0 /tmp/unreadable
ayourtch@ayourtch-lnx:~/misc/stackoverflow$ LD_PRELOAD=./intercept ./a.out
About to try to open the file...
open(/tmp/unreadable, 0) = -1
The open has returned an error. Please correct and we retry.
open(/dev/tty, 802) = 3
open(/dev/tty, 802) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/lib/terminfo/x/xterm, 0) = 3
open(/etc/inputrc, 0) = 3
sh-4.1$ ls -al /tmp/unreadable
---------- 1 ayourtch ayourtch 19 2011-10-18 13:03 /tmp/unreadable
sh-4.1$ chmod 444 /tmp/unreadable
sh-4.1$ exit
open(/home/ayourtch/.bash_history, 401) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 201) = 3
open(/tmp/unreadable, 0) = 3
The result after opening: 3
Read 19 bytes, here are the first few:
This is unreadable
�0
ayourtch@ayourtch-lnx:~/misc/stackoverflow$
ayourtch@ayourtch-lnx:~$echo这不可读>/tmp/不可读
ayourtch@ayourtch-lnx:~$chmod 0/tmp/不可读
ayourtch@ayourtch-lnx:~/misc/stackoverflow$LD\u PRELOAD=./截取/a.out
即将尝试打开该文件。。。
打开(/tmp/不可读,0)=-1
open返回了一个错误。请更正,我们将重试。
打开(/dev/tty,802)=3
打开(/dev/tty,802)=3
打开(/home/ayourtch/.bash_history,0)=3
打开(/home/ayourtch/.bash_history,0)=3
打开(/lib/terminfo/x/xterm,0)=3
打开(/etc/inputrc,0)=3
sh-4.1$ls-al/tmp/不可读
----------1阿约奇阿约奇19 2011-10-18 13:03/tmp/不可读
sh-4.1$chmod 444/tmp/不可读
sh-4.1美元退出
打开(/home/ayourtch/.bash_history,401)=3
打开(/home/ayourtch/.bash_history,0)=3
打开(/home/ayourtch/.bash_history,201)=3
打开(/tmp/不可读,0)=3
开放后的结果:3
读取19个字节,以下是前几个字节:
这是不可读的
�0
ayourtch@ayourtch-lnx:~/misc/stackoverflow$
顺便说一句,这个示例还暴露了第一个“测试”代码中的一个明显错误-我应该检查读取的字符数是否至少为30,并相应地放置空字符
不管怎么说,这些代码应该是有缺陷的,超出了控制范围,所以有缺陷是件好事——否则你就不需要使用这种黑客手段了:——)我不同意“几乎肯定想使用前者”-如果您有一个setuid/setgid二进制文件,那么判断您是否将通过授予对给定资源的访问权限来升级用户的访问通常很有用。相关:如果库生成错误消息,则库的设计很差。停止使用它。正是为了解决这类问题,库应该设计为返回错误值,而不是生成无效的错误消息。在修复之前,库是libusb的,所以我没有太多选择。
euidaccess
!(我不知道这家伙的存在!)+1
ayourtch@ayourtch-lnx:~$ echo This is unreadable >/tmp/unreadable
ayourtch@ayourtch-lnx:~$ chmod 0 /tmp/unreadable
ayourtch@ayourtch-lnx:~/misc/stackoverflow$ LD_PRELOAD=./intercept ./a.out
About to try to open the file...
open(/tmp/unreadable, 0) = -1
The open has returned an error. Please correct and we retry.
open(/dev/tty, 802) = 3
open(/dev/tty, 802) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/lib/terminfo/x/xterm, 0) = 3
open(/etc/inputrc, 0) = 3
sh-4.1$ ls -al /tmp/unreadable
---------- 1 ayourtch ayourtch 19 2011-10-18 13:03 /tmp/unreadable
sh-4.1$ chmod 444 /tmp/unreadable
sh-4.1$ exit
open(/home/ayourtch/.bash_history, 401) = 3
open(/home/ayourtch/.bash_history, 0) = 3
open(/home/ayourtch/.bash_history, 201) = 3
open(/tmp/unreadable, 0) = 3
The result after opening: 3
Read 19 bytes, here are the first few:
This is unreadable
�0
ayourtch@ayourtch-lnx:~/misc/stackoverflow$