Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper method to acquire root access on Linux for Qt applications

Tags:

c++

linux

sudo

root

qt

Good day

Background:

I am creating an OpenVPN wrapper application for Linux systems which is near completion. I have run into a little snag.

OpenVPN requires root access to modify routing tables (add and remove routes). Here is where things get a little vague and confusing.

Hopefully, by extending this question, some industry standard answers and solutions could be shared.

Documentation:

So, after a number of hours searching, I compiled a list of possible methods for acquiring root access, however none seem to be official nor any real solid guidance given to acquire this SU privilege.

Let us consider the following methods.


1. Using pkexec & polkits

Please find official freedesktop polkit documentation here and here for some information on best practises

There are a few tutorials found online using pkexec and polkits - here, this helped me create my polkit file. - SO Thread - And a lovely small tutorial for Qt applications

To give a brief explanation (of my understanding) about pkexec and polkits:

  • polkits:

polkits consist of actions and rules (see documenation for indepth reading and explaination). It defines actions of an application and the rules associated with it. Rules can be defined as a user belonging to a specific group, whereby the action queries the rule, it it has succeeded in passing the rule, the user is authenticated automatically (with no popup password prompt), else they are required to enter an administrator password

  • pkexec:

a application used to interface with the polkit actions and authenticate an application to acquire root access.

These require adding an action into /usr/share/polkit-1/actions/ and /usr/share/polkit-1/rules.d/ (amongst other directories, see documentation for all locations)

This method seems to be well used (but requires a bit more explaination to be understood easily, imo)

note: There are qt-polkit libraries made available for usage, see their github repository

For a simple TL;DR version, see this

The polkit file I created (note this may not be correct, but it does work for me):

Location where it can be found/added (there are others too)

/usr/share/polkit-1/actions

Policy Kit file name: com.myappname.something.policy // the .policy is required

Note:

com.myappname.something

is refereed to as the namespace of the policy (reading the documenation, this will not be clear)

Policy Kit content

<?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>

Notes about my policy file (the important bits)

  • This is just an example, see documentation for official examples and descriptions:
  • <vendor>My App Name</vendor> is the application name, it can have spaces
  • <action id="com.myappname.something.myaction-name"> any action name here.

  • NOTE! The file name -> com.myappname.something.policy and, the action id -> com.myappname.something.myaction-name should have the same namespace

  • The icon name should be in accordance with with the latest freedesktop icon spec found here

TL;DR (or don't want to): icon locations are:

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

as long as they conform to the sizes and are a .png, you can pass the filename only (omitting the format)

  • Very Important:

    <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/myappname</annotate>
    <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
    

When calling pkexec <myappname>, and NOT having these lines (honestly, I am not quite sure purpose of them), one will run into an error similar to this:

2017-12-19 12::58:24 Fatal: QXcbConnection: Could not connect to display  ((null):0, (null))

Aborted (core dumped)

Note: keep the key the same, however you can and probably should change the exec.path key to your application location.

How do policy kits work?

In short, if you review the lovely example mentioned earlier (and skip all the very similar names of files), it becomes clear.

When one runs:

pkexec <myappname> 

this calls the local authentication agent to run the application (in our case) as root.

This is handled by the actions (the policy kit mentioned above). Further more, the rules utilize the action id to perform addition queries, etc which can be viewed in the provided examples above.

Once the admin password has been entered, depending on the 'settings' entered into defaults (see here), we have:

auth_admin_keep

Like auth_admin but the authorization is kept for a brief period (e.g. five minutes).

Thus, the user can (in my OpenVPN application) connect to an OpenVPN connection for the next 5 minutes, until the password is requested again.


2. Sudo (/etc/sudoers):

This seems to be the go to method for most users who required root access, however it is not recommended:

e.g. Check root access before running the main application by calling a singleShot QProcess with arguments:

/bin/sh -c sudo -v

will result in exit code of 1 in various Linux distributions (thus causing me to search for alternatives)


3. setuid():

A very good example can be found here, unfortunatly it doesn't seem to work on modern Linux distros for the reason of it being a security hole which can easily be exploited.

In short, the process requires one to:

chmod +x <executable>

and checking that the s bit is set in the application using getuid() to get the user id and getgid() to get the group id of the user.

These functions can be found in Linux headers defined by:

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

However, this does not seem to work with modern day Linux systems. Here is the output of executing a root owned application with the s bit set, run as a normal (unprivileged) user:

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)

Farewell, setuid()


4. Other methods:

  • PAM

for further reading, see

  • man page
  • tutorial/explanation
  • A QT Forum question regarding cross platform PAM intergration

    • QT usermode Qt provides a small implementation for acquiring root access, but it is only available to Linux distrobutions using yum package manager.

A question followed up on this, see this QT Forum question


Problem:

Above may only include a small portion of available methods to acquire root access, however considering that some applications are used globally, they are often attacked or pried apart or even compromised.

After doing this research, it has broadened my knowledge, but hasn't given me a sure-fire method which is recommended, but only hints.

Question:

Which method above is preferred in industry i.e. when should I use the one over the other (PAM vs polkits vs simple sudo) and if other methods are available, are these preferred?

like image 446
CybeX Avatar asked Dec 19 '17 10:12

CybeX


1 Answers

However, this does not seem to work with modern day Linux systems. Here is the output of executing a root owned application with the s bit set, run as a normal (unprivileged) user:

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

The above error has nothing to do with modern day Linux systems. Its a Qt protection against stupid developers using setuid without understanding it.

Simply call

QCoreApplication::setSetuidAllowed(true)

when your application starts, and then you can do setuid() just fine. You can even run privileged commands before dropping down to 'normal' user.

Summary:

Your Qt application must have root owner, and setuid bit set. Example debmaker is my Qt application that I want to perform privileged actions from. So after I build debmaker, I do:

sudo chown root:root debmaker
sudo chmod 4755 debmaker

(The latter sets the setuid bit)

Now run the Qt application

./debmaker

First thing the app does is check geteuid()==0 and getuid()==1000 (1000 is my user id, 0 is the root)

Then it launches a new process (using QProcess in Qt). This will run in privileged mode. (Example my child process is called chroot)

Now drop the main application (my debmaker) to normal user level by calling

setuid(getuid());

chroot (the child process ) will keep running as root user.

The main application, now no longer is running in elevated mode, but can send requests to its child process which is still running in elevated mode.

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

The last line will send a message to the child process. You read the stdin in the child process, parse the command, check it to ensure its not malicious (or malicious depending on your intent!) and execute the command.

like image 63
rosewater Avatar answered Nov 12 '22 01:11

rosewater