5-Minute Read

Polkit explained

What is PolKit

PolKit is a standard authorization method for Linux. It usually consists of PolKit daemon, PolKit session agent and the helper program shipped by applications that use PolKit. As a user, you don’t really interact with PolKit itself, usually you interact with PolKit session agent. Like KDE’s PolKit agent:

KDE PolKit Agent

You can consider it as sudo of the GUI. But it works completely different than sudo. I spent two days trying to figure out what PolKit is, how it works, and how to use it to write a GUI tool that requires root privileges but can’t be run as root itself.

How PolKit work

Unlike the permission system you see on Android or iOS devices, PolKit itself doesn’t do the actual work. The main component of PolKit is the PolKit daemon, which runs as root and registers itself on DBus system bus. The daemon monitors actions and rules configuration files, which in turn define the rules and individual actions can be executed on the system. The PolKit action itself is a descriptive name, and the rules are used to determine whether the given action can be execueted given the request process/user permission level. The PolKit agent is another process that runs in the user’s login session and communicates with the PolKit daemon. Whenever an action requires user/root password for authorization, the PolKit daemon notifies the agent for password prompt. The PolKit agent isn’t shipped with PolKit daemon because different desktop environment may want to have their own password prompt styles.

The PolKit daemon is only used for authorization, it cannot execute any programs. So to do something as root, a helper is used. The helper is the bridge between the acual application and the PolKit daemon. When the application wants to do something that requires root permission, it asks helper to do it instead. After the helper receives the request from the application, it asks PolKit daemon to check if the application has the permission to do the work. If the anwser is “yes”, then helper performs the said job. Since the helper is the one that executes the job that requires root privileges, it must be started with root privileges.

[PolKit is purely for authorization]

For an application developer’s perspective, two programs must be written if the application is to be able to perform work that requires root privileges. One is the application itself, and the other is the helper that performs the root work. When the privileged work needs to be performed, the application sends a request to the helper, which in turn sends another request to PolKit daemon to ask if the application has sufficient priveleges to perform the action. Upon receiving the positive response, the helper performs the work for application. The difference between PolKit and Android/iOS permission system is who does the actual job. In PolKit, it’s the helper that usually comes with the application. But for Android/iOS, it’s the operating system.

You may wonder how the helper can launch with root privilege, because if the user can launch the helper with root privilege, then why can’t he launch the application with root privilege at first? And if the user can launch the application with root privilege, then what is the purpose of PolKit? Well, in fact, the user cannot launch the application with root privilege, nor can they launch the helper with root privilege. Unless they are root or they start the application/helper with sudo. Who then give the root privilege to the helper? Someone with root privilege has to do it somehow, right? The usual mechanism is DBus autostart. I won’t go into depth here on how DBus autostart works, what it essentially does is when a DBus message is send to an interface and the interface is configured as DBus autostart, DBus will start the interface first. And we can also configure so that DBus starts the interface as root.

An example

Recently I developed Formats & Language KCM. One of the task of the KCM is to activate and generate system locales. On some distributions, you must first enable locales in /etc/locale.gen and then call locale-gen to make the locale available. Both operations require root priveleges. But we can’t start plasma-setting as root, it’s not safe. So we have to rely on PolKit instead. I decided to have my helper listen on system bus, so we don’t end up with two helpers on two sessions. The whole authorization process goes like this:

flow graph

Bear with my drawing :)

As we can see, it’s the helper that does the real work. Polkit is only used for authorization. However, to make sure that no other program can exploit our helper, we need to verify that the program sending message to the helper really has authorization. This is done through the subject parameter of the request we send to PolKit. Polkit support two types of subjects. One is POSIX PID, the other is DBus system service path. Each message has its own path, and DBus protocol ensures that one program can’t forge the service path of another program. So what we do in helper is pass the service path from the KCM request to PolKit. Of course, another program can still ask helper to generate locale, but PolKit will ask user for password, and the password prompt will show KCM’s description. We also make sure to filter out invalid input in helper to avoid generating invalid locales even if the user enters the password for the malicious program.

Actually you can just use KAuth, it abstractsa lot of details for you. But for learning and to avoid another dependency to plasma-workspace(although I still introduced PolKit-qt1), I went the hard way.

Recent Posts

Categories

About

A young developer who loves Linux.