【问题标题】:Open a file as root, but drop privileges before reading from it?以 root 身份打开文件,但在读取文件之前放弃权限?
【发布时间】:2018-02-08 04:48:29
【问题描述】:

TL;DR

我正在编写一个 C 程序。我需要拥有fopen sysfs 文件的 root 权限,并且我仍然需要 root 权限才能读取它。但是,由于我的程序需要不断地读取 sysfs 文件,这意味着它需要一直提升权限。我想尽快放弃 root 权限。解决这个问题的公认方法是什么?

详情

我正在编写一个与 sysfs 交互的程序。如果我在 shell 上运行命令,我会使用:

myuser@mymachine:~$ sudo su
root@mymachine:/home/myhomedir# cd /sys/class/gpio
root@mymachine:/sys/class/gpio# echo 971 > export
root@mymachine:/sys/class/gpio# cat gpio971/value
0
root@mymachine:/sys/class/gpio# exit

我需要在非特权用户可调用的 C 程序中运行这些命令。一种方法是使用fopenfprintffscanf 等以通常的方式编写程序,并让用户通过sudo 运行程序。但是,这意味着用户需要是 sudoer,并且程序将始终具有 root 权限。

另一个我非常喜欢的解决方案(因为不必将用户添加到 sudoers)是将程序的所有者更改为 root,并设置 setuid 位。 (我是从 here 那里学到的)。

但是,我想知道一些事情。我想做的是在程序的euid 为 0 时打开 sysfs 文件,然后立即删除所有权限(为了安全起见)。然后,既然文件已经打开,我们只需 setuid() 到用户的 UID。但是,尽管我不能完全确定,但这行不通。这是我的代码的相关部分:

//At this point, due to the file permissions on the executable,
//euid = 0 and ruid = 1000. I know the following 4 lines work.
FILE *export = fopen("/sys/class/gpio/export", "wb");
fprintf(export, "971\n");
fclose(export);

FILE *sw_gpio = fopen("/sys/class/gpio971/value", "rb");

setuid(1000);
//Now euid = 1000 and ruid = 1000

int switch_val = -1;
fscanf(sw_gpio, "%d", &switch_val);
printf("Switch value: %d\n", switch_val); //-1
//Even though the only possible values in this sysfs file are 0 and 1,
//switch_val is still equal to -1

fclose(sw_gpio);

所以看来我需要保持提升的权限才能读取/sys/class/gpio/gpio971/value。但这正是我想要的!该程序将需要在整个程序执行期间轮询该值,并且我不希望始终拥有 root 权限。

最后,为了完成,这里是我在我的可执行文件上设置的权限:

-rwsr-xr-x 1 root myuser 10943 Jan 1 20:17 main*

那么如何放弃 root 权限,但继续从访问控制的 sysfs 文件中读取呢?

【问题讨论】:

  • 这对我来说按预期工作。尝试检查fscanf 的返回值,如果等于EOF,则调用perror 告诉您失败的原因。您还应该对 fopen 调用进行错误检查。
  • @dbush 我在我的实际代码中都有perror(我并没有将我的原始帖子与它们混淆)。所有调用都返回成功。
  • 我已经有一段时间没有玩过 SUID-bits 之类的东西了,但是seteuid()(设置有效用户 ID)会做你想做的事吗?设置二进制的 setuid 位后,我认为您可以在真实(非特权)用户和所有者(suid)用户之间切换。
  • 这种问题在vsftpd (see this video for an overview) 的设计中得到了很好的解决。基本上,如果一个程序具有 root 权限,那么它根本不应该直接与不受信任的客户端交互。相反,将所有客户端交互委托给具有较低权限的单独子进程,并使用IPC 以谨慎控制的方式传达此交互。
  • 解决底层问题的正确方法是设置一个允许访问gpio伪文件的udev规则到一个合适的组(gpio是常见的),然后添加允许摆弄的用户与该组的 gpio 引脚。对于规则本身,请查看例如Raspberry Pi 论坛this discussion 的最后一篇文章——应该是两条规则,两条线;不过,我自己还没有测试过。这样,您的程序根本不需要 setuid/setgid,也不需要关心权限。

标签: c linux setuid sysfs


【解决方案1】:

我没有用/sysfs 尝试过这个,但即使是普通文件,我的理解是文件streams 在调用setuid() 后不会保留访问权限。但是,出于我不明白的原因,文件 handles 确实如此。因此,如果您的系统表现得像我的(x64 上的 Fedora 20),您可能可以使用 open()/read() 而不是 fopen()/fscanf()

【讨论】:

  • 你能做一些研究吗,我个人不喜欢“如果你的系统表现得像我的”,这不是一个可靠的解决方案。
  • 我可以确认此解决方案适用于我的系统! (为了繁荣,请注意 sysfs 文件句柄需要 fflush()ed 和 rewind()ed 以便您的程序跟踪文件的当前内容。否则,它将缓冲文件中的旧值)。顺便问一下,有谁知道为什么流和文件句柄的权限会被区别对待?
  • @Mahkoe 现在是个有趣的问题。我想,流可以关闭描述符并在需要时重新打开它,所以它可能与此有关。但我不知道标准是否要求实现不这样做。请记住,fflush() 是必需的,因为默认情况下会缓冲流,而如果您直接使用文件描述符,则不需要刷新任何内容,除非您自己缓冲某些内容。
  • 流或句柄对权限一无所知。 file 设置权限并控制您打开它的能力(即创建句柄)。流通常是句柄周围的薄包装。你的解释听起来很可疑。
  • 我对这个问题非常感兴趣,花了十分钟在我的笔记本电脑上查看它。我发布了我发现的内容,希望对某人有所帮助。如果有人希望我对跨平台、编译器和库的句柄和流之间的差异进行详尽的调查,我的咨询费率为每小时 150 英镑,外加增值税 :)
猜你喜欢
  • 1970-01-01
  • 2021-05-18
  • 1970-01-01
  • 2011-03-27
  • 2018-01-30
  • 1970-01-01
  • 2015-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多