如何在没有在C中的Linux上打开文件的情况下检查我是否有权打开文件?

时间:2011-10-18 10:24:01

标签: c linux

我希望能够检查是否可以在Linux上打开文件(用于读取或读取和写入)。但是我无法控制打开文件的代码,因此我不能做我通常会做的事情,即打开它然后处理错误。

我很感激,在调用返回之后但在打开调用之前,由于权限更改,任何检查都会出现竞争条件,但我正在尝试避免从我无法控制的库中记录某些不需要的错误

我知道stat,但我不想尝试复制检查用户ID和组ID的逻辑。

3 个答案:

答案 0 :(得分:8)

您可以使用:

access("filename", R_OK);

euidaccess("filename", R_OK);

检查您的UID或EUID是否具有对相应文件的读取权限。 (如果你正在运行setuid,UID和EUID会有所不同)

答案 1 :(得分:1)

使用euidaccessaccess,尽管您几乎总是希望使用前者。

答案 2 :(得分:1)

(编辑:添加这个的原因是,通过这种方法,你可以确保你可以避免竞争条件。也就是说,这是一个非常棘手的方法,所以也许只是应对潜在的竞争条件是一个更好的实际方法)

如果你的目标是保护你不拥有的代码免受未处理的错误的影响,那么使用LD_PRELOAD拦截open open本身可能是有用的。 malloc的一个例子是:Overriding 'malloc' using the LD_PRELOAD mechanism

这里我快速即兴发布你如何做到这一点 - 基本上是一个拦截器,它将启动一个交互式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);
  }
}

然后是拦截器:

#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;
}

以下是运行时的样子:

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$

顺便说一下,这个例子也暴露了第一个“测试”代码中的一个明显的错误 - 我应该检查读取的字符数至少为30并相应地放置空字符。

无论如何,该代码应该是错误的并且在控件之外,所以在其中有一个错误是好的 - 否则你不需要使用这种黑客: - )