来自unistd.h
的{{3}}函数成功地将真实用户ID(在具有适当权限和所有权的程序中)从普通用户更改为root用户。但是,当在普通用户拥有的程序中使用相同的功能并且具有相同的权限时,函数调用返回0
以获得成功,但实际上不能成功地将真实用户ID从一个普通用户更改为另一个普通用户普通用户。
这种行为背后的原因是什么?什么是最好的(最便携,最安全)解决方案?
以下是try.c
:
#include <unistd.h> /* for the get/set uid functions */
#include <stdio.h> /* for printf and perror */
int main() {
int newuid = geteuid(); /* The goal is to set the real UID to this value. */
printf("Original UID is %d, EUID is %d\n", getuid(), geteuid());
if (setuid(newuid)) perror("setuid reported failure");
printf("UID after setuid is %d\n", getuid());
return 0;
}
我使用gcc
进行了编译,并将权限更改为4711
,即-rws--x--x
。该计划的所有者是用户名bob
,UID 32983
。
当用户alice
,UID 1000
运行try
计划时,她会看到
Original UID is 1000, EUID is 32893
UID after setuid is 1000
换句话说,setuid
函数调用报告成功(返回0),但实际的用户ID实际上并没有改变。
如果我将此可执行文件复制到root拥有的目录(例如/usr/local/bin
),请将所有权更改为root
并将权限保留为4711
,然后在{{{}}时按预期工作运行它:
alice
所以它适用于这种情况!
我试过这个并且在Ubuntu和Debian上得到了相同的行为;内核版本3.2.0-4-amd64,3.5.0-36-generic和3.14-1-amd64;和glibc版本2.13,2.15和2.19。
大多数linux程序只使用有效的UID来确定权限,所以这不重要。但有些人,特别是Original UID is 1000, EUID is 0
UID after setuid is 0
,将忽略有效的UID并恢复到真正的UID。当然有一些setuid
,但great reasons for that似乎是标准的解决方法。 (我正在适当地消毒环境并且有充分的理由去做这件事;即我自己的懒惰。)
无论如何,我很好奇为什么这会对setuid工作bash
而不是普通用户。
答案 0 :(得分:2)
Posix定义了[setuid][1]
对特权用户和非特权用户的工作方式:
如果进程具有适当的权限,setuid()将设置真实 用户ID,有效用户ID以及保存的呼叫的set-user-ID 过程到uid。
如果进程没有适当的权限,但uid是相同的 对于真实用户ID或保存的set-user-ID,setuid()应设置 有效的用户ID到uid; 真实用户ID和保存的set-user-ID必须 保持不变。 (重点补充)
我认为这就是你所经历的。
在理由部分,它提供了一些模糊的解释:
非特权进程调用时
setuid()
和setgid()
函数的各种行为反映了不同历史实现的行为。
并提出建议:
为了便于移植,建议新的非特权应用程序使用
seteuid()
和setegid()
函数。
(唯一的区别是seteuid
的行为对于非特权用户而言并不那么令人惊讶,因为它会按照其名称声明的方式执行。)
理论部分的其余部分是对多年来改变用户ID的各种策略,一些风险和多种解决方案的长期解释,以及(当然)作为理由Posix选择的路径。
答案 1 :(得分:0)
使用非POSIX函数setreuid
有效。在上面的try.c
示例中,我将行更改为:
if (setreuid(newuid, geteuid())) perror("setreuid reported failure");
,当alice
现在运行时,结果符合预期:
Original UID is 1000, EUID is 32893
UID after setreuid is 32893
请注意,虽然setreuid
可能比setuid
具有更多的权力,因为它可以同时设置真实有效的UID,在这种情况下,我并不是要求更改有效的UID。 / p>
如果有人能解释为什么setreuid
有效且setuid
没有,那我肯定会接受另一个答案!