【发布时间】: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 程序中运行这些命令。一种方法是使用fopen、fprintf、fscanf 等以通常的方式编写程序,并让用户通过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,也不需要关心权限。