在Linux上获取Qt应用程序的root访问权限的正确方法

8

你好

背景:

我正在创建一个针对Linux系统的OpenVPN包装应用程序,该应用程序已接近完成。我遇到了一些小问题。

OpenVPN需要root访问权限来修改路由表(添加和删除路由)。这里情况变得有些模糊和令人困惑。

希望通过扩展此问题,共享一些行业标准答案和解决方案。

文档:

所以,经过数小时的搜索,我编制了一个可能获得root访问权限的方法清单,但是没有任何官方信息或真正的指导可以获取此SU特权。

让我们考虑以下方法。


1. 使用pkexec & polkits

请在此处找到官方freedesktop polkit文档,并在此处获取有关最佳实践的一些信息。

在线上可以找到一些使用pkexec和polkits的教程 - 此处,这帮助我创建了我的polkit文件。 - SO Thread - 还有一个针对Qt应用程序的可爱的小教程

简要解释一下(我的理解)关于pkexecpolkits

  • polkits:

polkits由操作和规则组成(详见文档以进行深入阅读和解释)。它定义了应用程序的操作和与之相关联的规则。规则可以被定义为属于特定组的用户,其中操作查询规则,如果它已成功通过规则,则用户会自动验证(无需弹出密码提示),否则他们需要输入管理员密码

  • pkexec:

一个用于与polkit操作接口并认证应用程序以获取root访问权限的应用程序。

这些需要将操作添加到/usr/share/polkit-1/actions//usr/share/polkit-1/rules.d/(以及其他目录,请参见文档中的所有位置)

这种方法似乎被广泛使用(但需要更多解释才能容易理解,依我之见)

注意:可使用qt-polkit库,详见其GitHub仓库

简短版TL;DR,请参见此处

我创建的polkit文件(请注意,这可能不正确,但适用于我):

可以找到/添加的位置(还有其他位置)

/usr/share/polkit-1/actions

策略工具文件名: com.myappname.something.policy // .policy 必须包含在内

注意:

com.myappname.something

“命名空间”是指策略的名称(阅读文档时,这一点可能不太清楚)。

Policy Kit 内容

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD polkit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/software/polkit/policyconfig-1.dtd">
<policyconfig>
  <vendor>My App Name</vendor>
  <vendor_url>http://myappurl.com/</vendor_url>

  <action id="com.myappname.something.myaction-name">
    <description>Run the polkit for My App which requires it for X usage</description>
    <message>My App requires access to X, which requires root authentication, please let me have su access</message>
    <icon_name>myappname</icon_name>
    <defaults>
      <allow_any>auth_admin_keep</allow_any>
      <allow_inactive>auth_admin_keep</allow_inactive>
      <allow_active>auth_admin_keep</allow_active>
    </defaults>
    <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/myappname</annotate>
    <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
  </action>

</policyconfig>

关于我的策略文件的注释(重要部分)

  • 这只是一个示例,请参阅文档以获取官方示例和描述:
  • <vendor>My App Name</vendor> 是应用程序名称,可以包含空格
  • <action id="com.myappname.something.myaction-name"> 这里可以填写任何操作名称。

  • 注意! 文件名为 -> com.myappname.something.policyaction id 为 -> com.myappname.something.myaction-name 应具有相同的命名空间。

  • 图标名称应符合最新的 freedesktop 图标规范,可在此处找到。

TL;DR(或者不想读太多): 图标位置在:

 1. /home/yourusername/.icons (sometimes not there)
 2. /home/yourusername/.local/share/icons
 2. /usr/share/icons

只要它们符合大小并且是PNG格式,您可以仅传递文件名(省略格式)。
  • Very Important:

    <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/myappname</annotate>
    <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
    
在调用pkexec <myappname>时,如果没有以下这些行(老实说,我不太确定它们的目的),你会遇到类似于以下错误的问题:
2017-12-19 12::58:24 Fatal: QXcbConnection: Could not connect to display  ((null):0, (null))

Aborted (core dumped)

注意:保持key不变,但您可以并且可能应该将exec.path关键字更改为您的应用程序位置。 策略工具包是如何工作的? 简而言之,如果您查看之前提到的可爱的示例(并跳过所有非常相似的文件名称),就会变得清晰明了。
当一个人运行时:
pkexec <myappname> 

这将调用本地认证代理以root的身份运行应用程序(在我们的情况下)。

这是由上面提到的actions(策略工具包)处理的。此外,规则利用action id执行其他查询等操作,可以在上面提供的示例中查看。

一旦输入了管理员密码,根据在默认设置中输入的“设置”(请参见此处),我们有:

auth_admin_keep

类似于auth_admin,但授权只保留一段时间(例如5分钟)。

因此,在我的OpenVPN应用程序中,用户可以连接到OpenVPN连接,直到再次请求密码的下一个5分钟。


2. Sudo (/etc/sudoers):

这似乎是大多数需要root访问权限的用户使用的方法,但不建议这样做:

例如,通过带有参数的singleShot QProcess 调用来运行主应用程序之前检查root访问权限:

/bin/sh -c sudo -v

如果你在各种Linux发行版中使用此命令,将会导致退出代码为1(因此我需要寻找替代方案)。


3. setuid():

这里可以找到一个非常好的示例,但不幸的是,它似乎在现代Linux发行版上无法正常工作,因为它是一个易于被利用的安全漏洞。

简而言之,该过程要求:

chmod +x <executable>

同时检查应用程序中是否设置了s位,并使用getuid()获取用户ID和getgid()获取用户组ID来进行验证。

这些函数可以在Linux头文件中找到,定义如下:

<sys/types.h> // defines structs
<unistd.h>    // defines methods

然而,这似乎在现代Linux系统中无法正常工作。以下是以普通用户身份运行具有设置了s位的根拥有的应用程序的输出:
2017-12-19 12::21:08 Fatal: FATAL: The application binary appears to be running setuid, this is a security hole. ((null):0, (null))

Aborted (core dumped)

告别setuid()


4. 其他方法:

  • PAM

更多阅读请参见

此问题的后续问题,请参见 这个 QT 论坛问题


问题:

上述内容可能只包括获取 root 访问权限的一小部分可用方法,然而考虑到一些应用程序在全球范围内使用,它们经常受到攻击或分解甚至被破坏。

进行这项研究后,我的知识得到了扩展,但并没有给出推荐的确切方法,只是提供了一些提示。

问题:

在工业界中哪种方法优先使用,即何时应该使用其中一种(PAM vs polkits vs 简单的 sudo),如果有其他方法可用,那么这些方法是否更受欢迎?


有趣的问题,文笔优美。 - gavinb
@gavinb 我正在忙着添加一些额外的内容,敬请期待 - CybeX
嗨@CybeX,你当时集成的最佳解决方案是什么?我有同样的问题,我将使用linuxdeployqt来分发我的应用程序。在我的应用程序中,我同时拥有Wireguard和OpenVPN。 - Shahzain ali
1
@shahzain-ali 我最终使用了polkit选项 - 为此制作了一个小的帮助/信息存储库:https://github.com/cybex-dev/polkit-action-test - CybeX
2个回答

3

然而,在现代 Linux 系统中,似乎并不起作用。 执行设置了 s 位的 root 拥有的应用程序,并以普通(非特权)用户身份运行的输出如下:

2017-12-19 12::21:08 Fatal: FATAL: The application binary appears to be running setuid, this is a security hole. ((null):0, (null))

上述错误与现代Linux系统无关。这是Qt对于无知的开发者滥用setuid而实施的保护。

只需调用

QCoreApplication::setSetuidAllowed(true)

当您的应用程序启动时,您可以很好地执行setuid()。您甚至可以在降级到“普通”用户之前运行特权命令。
总结:
您的Qt应用程序必须具有根所有者和设置了setuid位。例如,debmaker是我想要从中执行特权操作的Qt应用程序。因此,在构建debmaker之后,我执行以下操作:
sudo chown root:root debmaker
sudo chmod 4755 debmaker

后者设置了setuid位。

现在运行Qt应用程序。

./debmaker

这个应用程序首先会检查 geteuid()==0 和 getuid()==1000 (1000 是我的用户 ID,0 是 root)。

然后它会启动一个新的进程(使用 Qt 中的 QProcess)。这将在特权模式下运行。 (例如,我的子进程名为 chroot)

现在通过调用来降低主应用程序(我的 debmaker)到普通用户级别

setuid(getuid());

chroot(子进程)将继续以root用户身份运行。

主应用程序现在不再以提升模式运行,但仍可向其仍在以提升模式运行的子进程发送请求。

QProcess *chroot = new QProcess;
blah blah setup the chroot and start it
chroot->write("chown root:root /home/oosman/foo");

最后一行将会向子进程发送一条消息。在子进程中读取标准输入,解析命令,并检查以确保其不是恶意的(或根据您的意图而定!),然后执行该命令。

感谢您的回复,我会测试并汇报结果。 - CybeX

0

研究得很好。但我想添加新的情况: 我认为更好的方法是创建一个新的Unix组,并授予组成员对目标配置文件的写访问权限。您只需将用户添加到组中。只有这些用户才能更改路由。 如果路由是由特定程序定义的。您可以仅允许组成员运行该程序。


感谢夸奖,但是OpenVPN二进制文件接受配置文件(或参数),并且它本身执行并创建连接,修改路由等。我的应用程序只是包装了这个过程。然而,我将在polkit规则中添加组概念(因为我计划在自己的应用程序中实现这一点,即myvpngroup)。 - CybeX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接