bind()
在我的自定义环境中,预加载了一个拦截器库,它运行,connect()
等调用的特殊实现。
我看到的问题是,每当应用程序使用 command 显式启用功能时setcap
,执行应用程序无法预加载拦截器库并调用默认 libc connect()
。
这是预期的行为吗?如果是,禁用的原因可能是什么LD_PRELOAD
?
是否有任何调整或方法可以用来成功预加载启用功能的库?
bind()
在我的自定义环境中,预加载了一个拦截器库,它运行,connect()
等调用的特殊实现。
我看到的问题是,每当应用程序使用 command 显式启用功能时setcap
,执行应用程序无法预加载拦截器库并调用默认 libc connect()
。
这是预期的行为吗?如果是,禁用的原因可能是什么LD_PRELOAD
?
是否有任何调整或方法可以用来成功预加载启用功能的库?
就像 Oliver Matthews 回答LD_PRELOAD
的那样,出于安全原因,setuid 二进制文件和具有文件功能的二进制文件都被禁用。
要在仍启用文件功能的同时预加载库,您有两种选择:
设置预加载库 setuid root
(Linux 动态链接器ld.so
甚至会为启用 setuid/file-capability 的二进制文件预加载库,如果库由 root 拥有并标记为 set-uid。)
使用 setuid 根包装器
包装器获得完全的 root 权限(真实和有效用户和组 ID 均为零),并将原始真实用户和组 ID 存储到例如环境变量中。
预加载的库有一个构造函数,例如
static void my_library_init(void) __attribute__((constructor));
static void my_library_init(void)
{
/* ... */
}
它在之前自动运行main()
(但可能在其他预加载库中的其他构造函数之后,或者在预加载库所依赖的库中)。
此构造函数获得所需的功能,可以通过环境变量 ( getenv()
, cap_from_text()
) 或二进制可执行文件本身 ( cap_from_file("/proc/self/exe")
) 指定。
构造函数必须临时使用prctl(PR_SET_KEEPCAPS, 1)
来保持身份更改的能力,并保留CAP_SETUID
能够CAP_SETGID
将身份从 root 更改为环境变量中指定的用户和组的能力,然后再将自身限制为最终的能力集。
这两种选择都有明显的安全考虑。我建议在预加载的库构造函数中进行完整性检查(和清除LD_PRELOAD
)。如果有任何可疑之处,请使用_exit()
立即中止该过程。
一般来说,为了简单起见(实现和安全问题),我推荐第一个选项,但如果由于某种原因无法使用它,我也可以为第二种情况提供概念验证代码。(我已经验证这两个选项都适用于运行 3.8.0-27-generic x86-64 内核的 Ubuntu 12.04.2 LTS,使用 ext4 文件系统。)
希望这可以帮助。
是的,这是出于安全原因(请参阅 参考资料man sudo
)。
您必须通过在开始main()
使用时从代码中显式打开库dlopen
(或通过包装 main 或类似方法)来解决它。