KDE: admittance of kio-admin into openSUSE
#KDE #PolkitTable of Contents
- 1) Introduction
- 2) History of Privileged File Operations in KDE
- 5) Assessment of Security Concerns
- 6) Rationale for Accepting kio-admin into openSUSE
- 7) Recommendations for Users of kio-admin or gvfs
- 8) Next Steps for Inclusion of kio-admin
- 8) References
1) Introduction
kio-admin is a KDE component which allows to
perform privileged file operations in GUI applications. It implements a D-Bus
service running as root
and uses Polkit for authentication. A typical use
case of kio-admin is found in KDE’s Dolphin file manager, which allows to
enter an admin mode, in which file manager operations are carried out as
root
instead of as the unprivileged user.
The initial 2022 request to add kio-admin to openSUSE was rejected by us due to security concerns. Some time ago we have been asked to revisit this package to see if it could be added now. The security assessment of kio-admin is a difficult one, nonetheless we decided to accept it into openSUSE this time since the probability of actual exploits is low. The following sections explore the history of privileged file operations in KDE up to kio-admin, the rationale for accepting it into openSUSE at this point and recommendations for safely performing privileged file operations in complex scenarios.
2) History of Privileged File Operations in KDE
KTextEditor File Saving Operation
Historically, for performing privileged operations in GUI applications, the
complete application had to be executed as root
. Doing so is generally
discouraged, since GUI programs are complex and not always written with
potential security issues in mind. Also the inner workings of the X11 protocol
and X server implementations have been a reason for security woes when running
graphical applications with raised privileges.
One way out of this “all or nothing” approach was the introduction of
Polkit, which allows to define individual actions for
privileged operations. These actions are authenticated by the polkitd
daemon. Applications are then typically separated in two parts. An
unprivileged graphical application which contains the majority of the code
communicates with a back end to execute the privileged operations.
The back end is much smaller in size and runs as root
.
This approach works well when the nature of the privileged operation has a
clearly defined scope like “enable a VPN connection” or “connect to a
Bluetooth device”. Problems arise, though, when this is not the case. This
happened in KDE in 2017, when a D-Bus service with Polkit
authentication was added to KTextEditor. This service is
part of a feature which allows an unprivileged user to save a file to a
privileged file system location after entering the root
password. From the end
user’s point of view this makes perfect sense: why shouldn’t the user of a
typical single-user desktop system be allowed to change configuration files
this way?
Former security team member Sebastian Krahmer was kind of outraged by this idea, though, because the Polkit action lacked a well-defined scope. Writing arbitrary data to arbitrary files on disk can lead to all kinds of problematic situations. Let’s consider a few scenarios:
- a file is saved in a system configuration directory owned by
root
, like/etc/fstab
. This is likely the typical use case considered by the upstream developers. - a file is (accidentally) saved to an unprivileged location the user controls
anyway like
/home/$USERNAME/some_file
. The privilege escalation would not even be necessary in this case, but acting withroot
privileges in this location is dangerous. - a file is saved to a configuration or state directory owned by an
unprivileged service user like
/var/lib/chrony
. Now three different user accounts are involved: the unprivileged user providing the file content and asking for the operation, theroot
user performing the privileged operation and the service userchrony
which actually controls the file system location where the write is about to happen. - a file on a pseudo file system like
/proc
or/sys
could be changed this way. Pseudo files often have special semantics with regards to the way data is written to them. Without being aware of this, the privileged KTextEditor helper component could cause side effects or simply not fulfill correctly what the user had in mind. - a target path might not yet exist. Should it be newly created? What ownership and mode should be applied to such a newly created file?
- a target path might exist, but have a special file type. By naively accessing such files, the write could be redirected to unintended locations (by following symbolic links) or denial-of-service could occur (by blocking on a FIFO pipe). What should be done in this case: should the write operation fail, should the special file be silently replaced or should the user be asked interactively what to do?
We can see from this list that many different situations can result from what looked like just a simple “file save” operation in the beginning. We reviewed the code for this KTextEditor feature more closely and discovered CVE-2018-10361, a local root exploit when the privileged back end attempts to save a file in a directory owned by another unprivileged user.
After the finding was fixed by upstream we asked for a number of improvements before we would accept this D-Bus service into openSUSE:
- the destination path should be added to the Polkit authentication message (bsc#1147035). Currently it would only show a generic message about manipulating a privileged file. To prevent potential accidents or even spoofing, the message should clearly state what is being authenticated.
- the target file ownership and mode should be completely defined either by
the back end, or by the front end (bsc#1147038).
Currently the front end could optionally pass the file owner and group, but
not the mode. For creating new files, the GUI part should ideally actively ask
the user for the desired file properties. This is because neither the back
end nor the front end can reliably guess the mode for new files. Should it
be world readable or not? Should it be controlled by
root
, or by a service account or even another interactive user? This is something that can only be answered by the user asking for the operation to be performed. - the back end should not replace anything except regular files. Symlinks should not be followed, any other special files should not be accessed or replaced (bsc#1147041).
- when writing to a directory not owned by
root
, then the back end component should drop privileges to the owner of the directory before performing file operations within it (bsc#1147043). - a restriction on destination file systems should be implemented, e.g. to prevent the operation on pseudo file systems (bsc#1147045).
We never heard back from upstream or our KDE maintainers on any of these points, therefore this part of KTextEditor is still not enabled on openSUSE. Implementing most of the changes we requested would have been well within reach; only the dynamic authentication message involved some obstacles, because the Qt and KDE framework libraries for Polkit did not fully support this at the time.
KIO Framework for Privileged File System Operations
While there was no progress with the requested improvements of the “save file
to an arbitrary location” feature in KTextEditor, developers of the KIO
framework around the same time attempted to extend the
concept of privileged file operations via D-Bus and Polkit even further. There
was an effort to make a full range of privileged file operations available to
GUI applications: chmod()
, chown()
,
unlink()
, mkdir()
, rmdir()
, open()
, opendir()
, rename()
,
symlink()
and utime()
. We were asked to participate in design
discussions about this feature.
The resulting D-Bus service ended up being something like a mini kernel in user
space, offering all kinds of file system APIs. Some of the problems observed
in KTextEditor became even worse in this approach. While in KTextEditor it is at
least clear that only regular files are supposed to be accessed, nothing is
known about the context of the file operations in KIO. All the individual
operations are detached from each other. Applications typically need to
perform a specific task covering a certain range of file operations that are
logically related. One such task might be to atomically replace a file in a
privileged location by a new version of the file. This would require a
sequence of calls like open()
for the new file to be created, a chown()
to
assign the desired ownership to it and finally a rename()
to replace the old
file by the new file. The KIO D-Bus service did not have this additional
context information and thus could not clearly inform the user about what is
going to happen.
Only a single Polkit action “org.kde.kio.file.exec” was used to authenticate
any of these privileged file system operations. The authentication message
displayed to users is, like in KTextEditor, a generic one. Users won’t be
able to determine exactly what kind of file operation they are authenticating.
The user will either be presented with multiple authentication requests for a
single task (auth_admin
Polkit setting) or the D-Bus service will cache
authentication for some time (auth_admin_keep
Polkit setting) thereby
allowing an unknown amount and range of file operations to be performed for an
indefinite time span. In both cases the scope of what is authenticated is
unclear to an average end user.
Since a generic D-Bus service that offers file operations cannot know what the logical goal of a client application is, the service basically needs to expose all variants of file system operations and the flags influencing them to applications. Modelling this on D-Bus correctly and completely is a difficult task. Such an approach also puts a burden on the applications that now need to implement complex sequences of system calls indirectly via an asynchronous D-Bus IPC interface.
Another approach to make an interface like this work in a robust and
safe way could be to implement some form of transaction handling. The
application would request a task like “replace /etc/fstab” and register a
sequence of calls that are logically related for this task like:
open /etc/fstab.new
, chmod /etc/fstab.new
, rename /etc/fstab.new
to
/etc/fstab
. The back end would then only authenticate and allow these file
operations on the requested paths. This again would lead to a highly complex
interface, however.
Securely operating in arbitrary file system locations with raised privileges
is already a highly difficult task when doing so using plain system calls.
The program has to take into account the necessity to perform a privilege drop
to the owner of a directory, it needs to avoid following symbolic links in
many situations, it might need to open files using the O_PATH
flag to avoid
accessing dangerous special files unwittingly. The task just seems too complex
to cover it generically using an abstract IPC interface. Polkit as an
authentication mechanism is also not suited well for such a kind of generic
API.
As a consequence of the involved complexities, after long
discussions, upstream abandoned this
feature. The code is still present in the KIO
repository, but the privileged file_helper
is not
installed.
The kio-admin Back End
With this we arrive at kio-admin. We were
asked for inclusion of this D-Bus service in 2022. It
is another variation on the “privileged file operation” theme. Only this time
it is not an integral feature of the KIO framework, but a separate component
running as root
that acts as a regular client towards KIO.
We decided not to accept kio-admin for mostly the same reasons as we have stated previously regarding KTextEditor and the KIO framework feature above. In 2024 we have been asked to revisit kio-admin, to check if it improved in the meantime.
Sadly the situation did not change much. The range of file operations offered
is very similar to what was proposed for KIO: chown()
, chmod()
, mkdir()
,
listing directory contents and so on. In some respects the API is even worse
than what was proposed for KIO earlier, because all operations are performed
on paths, not on file descriptors. There is less control over individual
operations with regards to following symbolic links and other behaviour of
system calls. The implementation of the D-Bus service is more complex,
requests are asynchronously forwarded by kio-admin to KIO. The kio-admin D-Bus
API also uses
URIs like
“file:///etc/fstab” instead of plain paths.
Again there exists only a single Polkit action “org.kde.kio.admin.commands” which uses a generic authentication message for authorization of any of the offered operations. The scope of the request that gets authenticated remains again unclear to users.
The actual implementation of the file operations found in the KIO framework is often naive with respect to occurrence of symbolic links and also subject to race conditions, should a third user account in the system have control over the directory in which the operations take place.
Integration of kio-admin into the Dolphin File Manager
One of the main use cases of the kio-admin component is found in the Dolphin file manager “admin mode” feature. This is a mode in which all file operations are forwarded to the kio-admin back end, to perform them with raised privileges.
The way this feature is implemented in Dolphin is actually well thought out. There are clear warnings and a visible red bar at the top as long as the “acting as admin” mode is active. Also Dolphin rejects changing symbolic link targets and correctly displays that link permissions cannot be changed.
This cannot completely fix things like race conditions on part of the kio-admin back end, however. When Dolphin sees a regular file for example, and triggers a request at kio-admin to operate on it, the path could be replaced by some other file type by the time the KIO framework starts operating on it.
5) Assessment of Security Concerns
The concerns we have about privileged file operations exposed via D-Bus APIs
affect local system security. These days it is often argued that nearly all
Linux desktop systems are single-user desktops and thus local system security
is not important. The attack surface found in kio-admin can still affect
defense-in-depth, though. Consider file operations taking place in directories
owned by unprivileged service users or by nobody
. If such an account is
compromised, then attack vectors like symlink attacks can lead to full
privilege escalation. In this sense, every Linux system could be considered a
multi-user system, even if no other human interactive users are present.
The general purpose nature of such APIs makes it hard to judge what future uses might look like. Once such an API is accepted into the distribution, it is difficult to keep track of additional consumers of the API. The proliferation of its use, maybe also in the area of non-interactive background tasks, could increase the dangers we already identified.
For these reasons we rejected the inclusion of the kio-admin API into openSUSE up until now.
6) Rationale for Accepting kio-admin into openSUSE
We have dealt with these types of APIs in KDE since 2017 without achieving any notable improvements. As we are responsible for product security we tried to protect our users from potentially harmful components. At this point, though, we don’t believe that this situation will change anytime soon. Meanwhile users still want to use features like the one found in Dolphin, and don’t understand why openSUSE does not include them.
We realize that using non-robust APIs is still an improvement over running
graphical applications completely as root
. Also in its current form, the
likelihood that an operation interactively performed via kio-admin is actually
exploited, is low.
There also exists a GNOME desktop component called gvfs which is very similar to kio-admin. It was accepted into openSUSE in 2017 without looking in detail at its purpose and API design. In the context of the discussions about KTextEditor we performed a second more in-depth review, during which we found problems closely resembling the concerns about kio-admin discussed in this article. Still, we decided to keep it in openSUSE, due to historical reasons.
Thus, on the grounds of equal treatment and to allow for a good user experience on openSUSE, we have now decided to set aside our concerns about kio-admin and admit it into openSUSE. This feels like the pragmatic choice to us given the circumstances. We would have liked to see a more robust and transparent API design, however. We hope that upstream developers find ways to better address our concerns in the future, meanwhile we still recommend end users to be careful when using these features and take heed of the recommendations we give in the next section.
7) Recommendations for Users of kio-admin or gvfs
Unfortunately there are many pitfalls when performing privileged file
operations. We assume that even power users tend to make mistakes when running
shell commands as root
operating in directories controlled or influenced by
non-root users, like in /tmp
. Following is some general advice that can help
to avoid such mistakes.
For a start, APIs like kio-admin and gvfs are usually safe to use when file
operations happen in directories owned by root
, like in /etc
(note that
sub-directories of /etc
can again be owned by non-root users). Special care
should be taken when changing files in directories controlled by another
user, like another user’s home directory or files owned by a service user
account.
In such scenarios it is safer to perform the operations in a root
shell, and
one should be very careful not to follow symbolic links while doing so. Many
file management utilities offer specific switches to avoid following them. The
chmod
utility, for example, will by default follow symbolic links in the
target file path unless the -h
switch is passed to it.
Even these switches only protect against symbolic links in the final component
of a path. Consider the command chmod -h 644
/var/lib/chrony/sub-dir/target
. /var/lib/chrony
is controlled by the
chrony
service user account. Thus the unprivileged chrony
user can turn
sub-dir
into a symbolic link pointing to a privileged location like /etc
.
If /etc/target
existed then the command above would make this file
world-readable.
Therefore an even better approach to editing files owned by another account is
to assume their identity, for example by invoking sudo -u <user> -g <group>
/bin/bash
. This way no elevated privileges that could be abused by a
compromised account are present in the first place.
8) Next Steps for Inclusion of kio-admin
Documenting our concerns in this blog post is the first step of the process to add kio-admin to openSUSE. We will reference this blog post and some hints in dedicated README files added to the kio-admin and gvfs packages. We will also document this in the openSUSE wiki. When all of this is done we will perform the necessary steps to allow kio-admin into openSUSE Tumbleweed, which we believe will happen within the next two weeks.