zathura - SELinux confined
About
When hardening a Linux workstation, it is important to isolate and control the applications. There are several tools available, but none as powerful as SELinux.
zathura is a customizable and minimalistic document viewer which supports PDF documents and other file formats.
This blog post describes how to securely confine the zathura application to a SELinux domain.
Threat model
zathura supports PDF documents by loading the poppler rendering engine plugin.
poppler is a complete implementation of ISO 32000-1 (Portable document format), and supports both simple and complex PDF documents. Complex documents may contain annotations, forms, multimedia and even dynamic code.
Arbitrary code execution
Complex PDF documents can possibly trigger parsing vulnerabilities in the rendering engine. A method to detect parsing errors is fuzzing, and poppler is continuously scanned with oss-fuzz.
The have been several findings, some which have resulted in denial of service vulnerabilities.
- CVE-2017-9775 - [nvd.nist.gov]
- CVE-2018-19149 - [gitlab.freedesktop.org]
Also, there are several heap overflow bugs, some which could be exploitable.
In addition, zathura can execute shell commands directly from the application. This is convenient, but may not be considered to be core functionality for a PDF viewer.
Hardening
- Limit the possibility to execute code outside of the zathura SELinux domain
- Deny launching new interactive shells from zathura
Unauthorized network connections
PDF documents may initialize outgoing network connections. Either intentionally if the user follows HTTP links in the document, or by built-in phone home functionality when the document is opened.
Hardening
- Deny all TCP/UDP network connectivity
Unauthorized file access or modification
Zathura only requires access to a subset of files on the system:
- The PDF files (in read-only mode)
- Configuration file ($HOME/.config/zathura/zathurarc)
- Bookmarks and history ($HOME.local/share/zathura/)
- User and system fonts
In addition, zathura requires access to inter-process communication (IPC) resources.
Hardening
- Open PDF documents in read-only mode
- Deny access to files and directories in $HOME containing sensitive information like SSH private keys or GPG private keys
System Setup
This module is developed on Gentoo Hardened with OpenRC. (Profile: default/linux/amd64/17.1/hardened/selinux (stable))
The system is configured to run SELinux enforcing in strict mode. Thus, both network daemons and users are running confined in SELinux domains.
The system does not run systemd or PolicyKit.
zathura is installed from Gentoo Portage:
What is SELinux?
Security-Enhanced Linux (SELinux) is a Mandatory Access Control (MAC) system implemented in the Linux kernel.
Traditionally, the secure state of a Linux system has been largely dependant on the behaviour of its users. The user controls access to their resources (files, processes, …) by utilizing Discretionary Access Control (DAC) mechanisms (chown/chmod/setfacl,…). The operating system then decides whether access is allowed by matching the process UID/GID against the requested resource UID/GID.
MAC complements DAC, and gives the system administrator full control over which actions that are allowed on the system. MAC is configured by policies that define which actions a subject (user, process, …) can perform on an object (files, TCP/UDP socket, …). MAC is enforced by the operating system, and a non-privileged user cannot modify the policies.
Note: MAC policies are evaluated after DAC (DAC, then MAC). If a DAC rule states that a user cannot access a file, the user is denied access to the file even if the MAC policy states otherwise.
MAC has several advantages:
- Restrict the privileges of all users, even the superuser (root).
- Limit the impact of a vulnerability in a network daemon (sandboxing).
- Control access to resources at the system call level.
More information: SELinux - wiki.gentoo.org
Generate zathura policy module skeleton
The SELinux policy module template is generated using sepolicy generate:
NOTE: The user running zathura is mapped to the SELinux user staff_u which is mapped to the SELinux policy role staff_r. The staff_u user is initially operating within the staff_t security context (domain).
Set file contexts
Define file context to ensure correct SELinux labelling of zathura’s files and directories:
The configuration settings are stored in /etc/selinux/strict/contexts/files/file_contexts.local
As root, re-label all zathura files in $HOME, and the zathura binary:
Build and install module
As root, build and install the policy module skeleton:
NOTE: The policy has to be rebuilt and reinstalled after every change. On rpm-based systems, the module can be built and installed by executing zathura.sh.
SELinux domain transition
When the user executes the zathura binary, the process is transitioned into the zathura_t domain. The required rules are created when creating the policy module, and can be inspected using sesearch:
Customizing the SELinux policy module
Implementing a SELinux policy module is complex. Some recommendations:
- Consult documentation at:
- https://wiki.gentoo.org/wiki/SELinux
- https://selinuxproject.org/
man 8 selinux
andman 1 sesearch
- Use
audit2allow
,audit2why
, andstrace --failed
- Monitor the audit log for SELinux denials
- Use pre-defined interfaces and definitions (/usr/share/selinux/strict/)
In this blog post, we focus on only a subset of the module. The complete module is located here [gitlab.com]
Overview
The policy module comprises three files; zathura.fc, zathura.if and zathura.te. The majority of the policy is defined in the type enforcement file (zathura.te). The policy defines rules for several resources:
- Allow transition into the zathura_t domain from staff_t
- Define explicit deny rules to mitigate threats described in threat model
- Handle process operations, FIFO, sockets and dbus
- Manage zathura_t resources (zathura_rw_t)
- Restrict access to $HOME folder and files
- Allow access to Wayland, tty/pts, themes (GTK and cursor) and Fonts (user and system)
- Manage access to temporary file systems
Explicit deny operations
An important section of the policy is the rules that denies access to resources. This section utilizes both pre-defined functions (rw_socket_perms) and directly defines system calls (read and open):
Granular control of files in $HOME
Access to files in $HOME is restricted using system calls and interfaces (xdg_read_config_files):
Access to dbus (IPC)
To improve readability and adhere to the DRY principle, create and use interfaces. This inteface can be called to control access to the DBUS session bus:
Validate sandbox
After installing the policy module, we can verify the mitigation rules enforced by the zathura_t policy.
First, transition into the zathura_t domain by launching a shell with newrole.
Deny UDP connections
Test connecting to a network resource using UDP:
zathura is not authorized to resolve the DNS address, SELinux creates the following entry in audit log:
Mar 08 18:34:17 laptop kernel: audit: type=1400 audit(1645860857.586:7135): avc: denied { read } for pid=15119 comm=”nc” name=”hosts” dev=”dm-1” ino=131301 scontext=staff_u:staff_r:zathura_t tcontext=system_u:object_r:net_conf_t tclass=file permissive=0
Test connecting to an IP address over UDP:
Zathura is not allowed to create an UDP socket:
Mar 08 18:35:10 laptop kernel: audit: type=1400 audit(1645860857.586:7136): avc: denied { create } for pid=15119 comm=”nc” scontext=staff_u:staff_r:zathura_t tcontext=staff_u:staff_r:zathura_t tclass=udp_socket permissive=0
Deny TCP connections
Test connecting to a network resource using TCP:
zathura is not allowed to create a TCP socket:
Mar 08 18:36:09 laptop kernel: audit: type=1400 audit(1645860969.743:7137): avc: denied { create } for pid=15132 comm=”nc” scontext=staff_u:staff_r:zathura_t tcontext=staff_u:staff_r:zathura_t tclass=tcp_socket permissive=0
Restrict access to SSH keys
Test accessing SSH private keys:
zathura is not allowed to do any file or directory operations on $HOME/.ssh:
Mar 08 18:39:38 laptop kernel: audit: type=1400 audit(1645861178.260:7183): avc: denied { getattr } for pid=23637 comm=”ls” path=”/home/esp0x31/.ssh” dev=”dm-3” ino=8258997 scontext=staff_u:staff_r:zathura_t tcontext=staff_u:object_r:ssh_home_t tclass=dir permissive=0
Modifying PDF documents
Launch the zathura application, open a PDF document, and overwrite the document:
Writing the document fails since zathura cannot execute the write syscall.
Mar 08 20:32:34 laptop kernel: audit: type=1400 audit(1646058754.677:9627): avc: denied { write } for pid=7453 comm=”zathura” name=”Linux_Programming_Interface.pdf” dev=”dm-3” ino=5767287 scontext=staff_u:staff_r:zathura_t tcontext=staff_u:object_r:user_home_t tclass=file permissive=0
NOTE: The zathura application can communicate with other processes using dbus. This could make it possible for zathura to indirectly access resources outside of the sandbox.
Additional hardening: seccomp
Seccomp is a security mechanism in the Linux kernel which restricts the system calls that are permitted from a user mode process. zathura implements seccomp filters.
Verify seccomp status on zathura process:
Note: Using ps with the Z option show that the zathura is running in the zatura_t domain.
The Seccomp_filters: 1 value indicates that zathura is using a user supplied filter [github.com]
Compared to SELinux, Seccomp filters are less granular. A system call executed by the process is either allowed or not, on all objects. Nevertheless, seccomp adds an additional layer of security, and is commonly used to isolate containers.
Conclusion
This blog post has discussed why and how to implement a custom SELinux policy module for zathura.
SELinux is complex. The policies operates on a system call level, and it has a steep learning curve. Nevertheless, it is an excellent tool for hardening a Linux workstation, or server.
An alternative to writing a SELinux policy module could be Bubblewrap [wiki.archlinux.org]. A lightweight sandbox application using cgroups, namespaces and seccomp.
Stay tuned for another blog post.