From 9fe3ac2862a8c175520a0f275f39f548c2cf9d1e Mon Sep 17 00:00:00 2001 From: Petr Lautrbach Date: Feb 27 2020 11:38:39 +0000 Subject: [PATCH 1/5] setroubleshoot.util: get_rpm_nvr_by_type() and get_rpm_nvr_by_scontext() get_rpm_nvr_by_scontext(scontext) Finds an SELinux module which defines given SELinux context ##### arguments * `scontext(s)`: an SELinux context ##### return values * `nvr(s)`: nvr of rpm which ships module where SELinux type used in `scontext` is defined ##### usage >>> get_rpm_nvr_by_scontext("system_u:system_r:syslogd_t:s0") selinux-policy- >>> get_rpm_nvr_by_scontext("system_u:system_r:mysqld_log_t:s0") mysqld-selinux- >>> get_rpm_nvr_by_scontext("system_u:system_r:timedatex_t:s0") selinux-policy- get_rpm_nvr_by_type(selinux_type) Finds an SELinux module which defines given SELinux type ##### arguments * `selinux_type(s)`: an SELinux type ##### return values * `nvr(s)`: nvr of rpm which ships module where `selinux_type` is defined ##### usage >>> get_rpm_nvr_by_type("sshd_t") selinux-policy- >>> get_rpm_nvr_by_type("mysqld_log_t") mysqld-selinux --- diff --git a/framework/src/setroubleshoot/util.py b/framework/src/setroubleshoot/util.py index b826e7f..a692691 100755 --- a/framework/src/setroubleshoot/util.py +++ b/framework/src/setroubleshoot/util.py @@ -35,6 +35,8 @@ __all__ = [ 'get_rpm_nvr_from_header', 'get_rpm_nvr_by_name', 'get_rpm_nvr_by_file_path', + 'get_rpm_nvr_by_type', + 'get_rpm_nvr_by_scontext', 'is_hex', 'split_rpm_nvr', 'file_types', @@ -62,6 +64,7 @@ __all__ = [ 'Retry', ] +import bz2 import six import datetime import glob @@ -69,6 +72,7 @@ from gi.repository import GObject import os import pwd import re +import selinux import sys import textwrap import time @@ -404,6 +408,81 @@ def split_rpm_nvr(nvr): name = '-'.join(components[:-2]) return (name, version, release) +def get_rpm_nvr_by_type(selinux_type): + """ +Finds an SELinux module which defines given SELinux type + +##### arguments + +* `selinux_type(s)`: an SELinux type + +##### return values + +* `nvr(s)`: nvr of rpm which ships module where `selinux_type` is defined + +##### usage + +>>> get_rpm_nvr_by_type("sshd_t") +selinux-policy- + +>>> get_rpm_nvr_by_type("mysqld_log_t") +mysqld-selinux + + """ + retval, policytype = selinux.selinux_getpolicytype() + if retval != 0: + return None + typedef = "(type {})\n".format(selinux_type) + modules = [] + for (dirpath, dirnames, filenames) in os.walk("/var/lib/selinux/{}/active/modules".format(policytype)): + if "cil" in filenames: + try: + defined = False + try: + # cil files are bzip2'ed by default + defined = typedef.encode() in bz2.open("{}/cil".format(dirpath)) + except: + # maybe cil file is not bzip2'ed, try plain text + defined = typedef in open("{}/cil".format(dirpath)) + + if defined: + modules.append(dirpath) + except: + # something's wrong, move on + # FIXME: log a problem? + pass + + if len(modules) > 0: + return get_rpm_nvr_by_file_path(sorted(modules)[-1]) + + return None + +def get_rpm_nvr_by_scontext(scontext): + """ +Finds an SELinux module which defines given SELinux context + +##### arguments + +* `scontext(s)`: an SELinux context + +##### return values + +* `nvr(s)`: nvr of rpm which ships module where SELinux type used in `scontext` is defined + +##### usage + +>>> get_rpm_nvr_by_scontext("system_u:system_r:syslogd_t:s0") +selinux-policy- + +>>> get_rpm_nvr_by_scontext("system_u:system_r:mysqld_log_t:s0") +mysqld-selinux- + +>>> get_rpm_nvr_by_scontext("system_u:system_r:timedatex_t:s0") +selinux-policy- + + """ + context = selinux.context_new(str(scontext)) + return get_rpm_nvr_by_type(str(selinux.context_type_get(context))) def get_user_home_dir(): uid = os.getuid() From 5242f26ab29c7787a6071d10bf613e6b283512ef Mon Sep 17 00:00:00 2001 From: Petr Lautrbach Date: Feb 27 2020 11:38:39 +0000 Subject: [PATCH 2/5] Export setroubleshoot.utils.get_rpm_nvr_by_scontext via DBUS $ dbus-send --system --print-reply --dest=org.fedoraproject.SetroubleshootPrivileged \ /org/fedoraproject/SetroubleshootPrivileged/object \ org.fedoraproject.SetroubleshootPrivileged.get_rpm_nvr_by_scontext \ string:"system_u:system_r:mysqld_log_t:s0" org.fedoraproject.SetroubleshootPrivileged is available only for `setroubleshoot` user and it's supposed to be a privileged helper which is used by `setroubleshootd` --- diff --git a/framework/Makefile.am b/framework/Makefile.am index 56a8b37..f330b7c 100644 --- a/framework/Makefile.am +++ b/framework/Makefile.am @@ -14,12 +14,15 @@ dbus_session_DATA = sealert.service dbus_systemservicedir = $(datadir)/dbus-1/system-services dbus_systemservice_DATA = \ org.fedoraproject.Setroubleshootd.service \ - org.fedoraproject.SetroubleshootFixit.service + org.fedoraproject.SetroubleshootFixit.service \ + org.fedoraproject.SetroubleshootPrivileged.service dbus_systemdir = $(sysconfdir)/dbus-1/system.d dbus_system_DATA = \ org.fedoraproject.Setroubleshootd.conf \ - org.fedoraproject.SetroubleshootFixit.conf + org.fedoraproject.SetroubleshootFixit.conf \ + org.fedoraproject.SetroubleshootPrivileged.conf + polkit_systemdir = $(datadir)/polkit-1/actions polkit_system_DATA = \ diff --git a/framework/org.fedoraproject.SetroubleshootPrivileged.conf b/framework/org.fedoraproject.SetroubleshootPrivileged.conf new file mode 100644 index 0000000..aaa0a0f --- /dev/null +++ b/framework/org.fedoraproject.SetroubleshootPrivileged.conf @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/framework/org.fedoraproject.SetroubleshootPrivileged.service b/framework/org.fedoraproject.SetroubleshootPrivileged.service new file mode 100644 index 0000000..7a45458 --- /dev/null +++ b/framework/org.fedoraproject.SetroubleshootPrivileged.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.fedoraproject.SetroubleshootPrivileged +Exec=/usr/share/setroubleshoot/SetroubleshootPrivileged.py +User=root diff --git a/framework/src/Makefile.am b/framework/src/Makefile.am index e1782d5..bf53763 100644 --- a/framework/src/Makefile.am +++ b/framework/src/Makefile.am @@ -38,7 +38,8 @@ pkglibexec_SCRIPTS = \ pkgdir = $(datarootdir)/setroubleshoot pkg_SCRIPTS = \ SetroubleshootFixit.py \ - updater.py + updater.py \ + SetroubleshootPrivileged.py pkgconfig_DATA = \ setroubleshoot.conf \ diff --git a/framework/src/SetroubleshootPrivileged.py b/framework/src/SetroubleshootPrivileged.py new file mode 100644 index 0000000..858115b --- /dev/null +++ b/framework/src/SetroubleshootPrivileged.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 + +# Authors: Petr Lautrbach +# +# Copyright (C) 2020 Red Hat, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import dbus +import dbus.service +from dbus.mainloop.glib import DBusGMainLoop +from gi.repository import GLib +import setroubleshoot.util +import signal + +DBusGMainLoop(set_as_default=True) + +class Privileged(dbus.service.Object): + + def __init__(self, timeout=10): + self.timeout = timeout + self.alarm(self.timeout) + + bus = dbus.SystemBus() + bus.request_name("org.fedoraproject.SetroubleshootPrivileged") + bus_name = dbus.service.BusName("org.fedoraproject.SetroubleshootPrivileged", bus=bus) + dbus.service.Object.__init__(self, bus_name, "/org/fedoraproject/SetroubleshootPrivileged/object") + + def alarm(self, timeout=10): + signal.alarm(timeout) + + @dbus.service.method("org.fedoraproject.SetroubleshootPrivileged", in_signature='s', out_signature='s') + def get_rpm_nvr_by_scontext(self, scontext): + signal.alarm(self.timeout) + rpmnvr = setroubleshoot.util.get_rpm_nvr_by_scontext(scontext) + if rpmnvr is None: + return "" + + return rpmnvr + +if __name__ == "__main__": + privileged = Privileged() + + loop = GLib.MainLoop() + loop.run() From 72de472c510e6e3d50c72efbd1e1fd291ed35b68 Mon Sep 17 00:00:00 2001 From: Petr Lautrbach Date: Feb 27 2020 11:38:39 +0000 Subject: [PATCH 3/5] setroubleshoot.utils.get_rpm_nvr_by_scontext add option to use DBUS method Using keyword `use_dbus=True`: get_rpm_nvr_by_scontext("system_u:system_r:timedatex_t:s0", use_dbus=True) the function calls org.fedoraproject.SetroubleshootPrivileged.get_rpm_nvr_by_scontext DBUS method in order to get data using privileged process. --- diff --git a/framework/src/setroubleshoot/util.py b/framework/src/setroubleshoot/util.py index a692691..94bb988 100755 --- a/framework/src/setroubleshoot/util.py +++ b/framework/src/setroubleshoot/util.py @@ -67,6 +67,7 @@ __all__ = [ import bz2 import six import datetime +import dbus import glob from gi.repository import GObject import os @@ -423,10 +424,10 @@ Finds an SELinux module which defines given SELinux type ##### usage >>> get_rpm_nvr_by_type("sshd_t") -selinux-policy- +'selinux-policy-... >>> get_rpm_nvr_by_type("mysqld_log_t") -mysqld-selinux +'mysql-selinux-... """ retval, policytype = selinux.selinux_getpolicytype() @@ -457,7 +458,7 @@ mysqld-selinux return None -def get_rpm_nvr_by_scontext(scontext): +def get_rpm_nvr_by_scontext(scontext, use_dbus=False): """ Finds an SELinux module which defines given SELinux context @@ -472,17 +473,31 @@ Finds an SELinux module which defines given SELinux context ##### usage >>> get_rpm_nvr_by_scontext("system_u:system_r:syslogd_t:s0") -selinux-policy- +'selinux-policy-... >>> get_rpm_nvr_by_scontext("system_u:system_r:mysqld_log_t:s0") -mysqld-selinux- +'mysql-selinux-... ->>> get_rpm_nvr_by_scontext("system_u:system_r:timedatex_t:s0") -selinux-policy- +>>> get_rpm_nvr_by_scontext("system_u:system_r:timedatex_t:s0", use_dbus=True) +'selinux-policy-... """ - context = selinux.context_new(str(scontext)) - return get_rpm_nvr_by_type(str(selinux.context_type_get(context))) + if use_dbus: + bus = dbus.SystemBus() + + try: + remote_object = bus.get_object("org.fedoraproject.SetroubleshootPrivileged", + "/org/fedoraproject/SetroubleshootPrivileged/object") + + return str(remote_object.get_rpm_nvr_by_scontext(str(scontext), + dbus_interface = "org.fedoraproject.SetroubleshootPrivileged")) + except dbus.DBusException: + from traceback import print_exc + print_exc() + return None + else: + context = selinux.context_new(str(scontext)) + return get_rpm_nvr_by_type(str(selinux.context_type_get(context))) def get_user_home_dir(): uid = os.getuid() From 74926ff27b35329819d74ea53eef2aff376cc6e1 Mon Sep 17 00:00:00 2001 From: Petr Lautrbach Date: Feb 27 2020 11:38:39 +0000 Subject: [PATCH 4/5] Add Local SELinux policy package version to analyses reports Sometimes a SELinux domain is shipped by other than selinux-policy packages. In this case it's useful to report other package policy version together with selinux-policy version, e.g. for the following AVC: type=AVC msg=audit(1582621541.469:6896): avc: denied { write } for pid=1627505 comm="python3" name="plautrba" dev="dm-4" ino=19529729 scontext=system_u:system_r:mysqld_t:s0 tcontext=unconfined_u:object_r:user_home_dir_t:s0 tclass=dir permissive=1 a report will contain the following lines: SELinux Policy RPM selinux-policy-3.14.5-24.fc32.1.contrib.50770ffc2a14.noarch Local Policy RPM mysql-selinux-1.0.0-9.fc32.noarch --- diff --git a/framework/src/setroubleshoot/signature.py b/framework/src/setroubleshoot/signature.py index 711c287..7287eec 100755 --- a/framework/src/setroubleshoot/signature.py +++ b/framework/src/setroubleshoot/signature.py @@ -120,6 +120,7 @@ class SEEnvironment(XmlSerialize): 'kernel': {'XMLForm': 'element'}, 'policy_type': {'XMLForm': 'element'}, 'policy_rpm': {'XMLForm': 'element'}, + 'local_policy_rpm': {'XMLForm': 'element'}, 'enforce': {'XMLForm': 'element'}, 'selinux_enabled': {'XMLForm': 'element', 'import_typecast': boolean, }, 'selinux_mls_enabled': {'XMLForm': 'element', 'import_typecast': boolean, }, @@ -141,6 +142,7 @@ class SEEnvironment(XmlSerialize): self.platform, self.kernel = get_os_environment() self.policy_type = selinux.selinux_getpolicytype()[1] self.policy_rpm = get_rpm_nvr_by_name("selinux-policy") + self.local_policy_rpm = self.policy_rpm self.policyvers = str(selinux.security_policyvers()) enforce = selinux.security_getenforce() if enforce == 0: @@ -312,6 +314,7 @@ class SEFaultSignatureInfo(XmlSerialize): setattr(self, k, v) self.report_count = 1 self.plugin_list = [] + self.environment.local_policy_rpm = get_rpm_nvr_by_scontext(self.scontext, use_dbus=True) def update_merge(self, siginfo): if siginfo.last_seen_date != self.last_seen_date: @@ -524,7 +527,8 @@ class SEFaultSignatureInfo(XmlSerialize): text += format_2_column_name_value(_("Host"), default_text(self.sig.host)) text += format_2_column_name_value(_("Source RPM Packages"), default_text(self.format_rpm_list(self.src_rpm_list))) text += format_2_column_name_value(_("Target RPM Packages"), default_text(self.format_rpm_list(self.tgt_rpm_list))) - text += format_2_column_name_value(_("Policy RPM"), default_text(env.policy_rpm)) + text += format_2_column_name_value(_("SELinux Policy RPM"), default_text(env.policy_rpm)) + text += format_2_column_name_value(_("Local Policy RPM"), default_text(env.local_policy_rpm)) text += format_2_column_name_value(_("Selinux Enabled"), default_text(env.selinux_enabled)) text += format_2_column_name_value(_("Policy Type"), default_text(env.policy_type)) text += format_2_column_name_value(_("Enforcing Mode"), default_text(env.enforce)) From a9a1d1b99c30208006a86474c19ab288c933afb6 Mon Sep 17 00:00:00 2001 From: Petr Lautrbach Date: Feb 27 2020 11:38:39 +0000 Subject: [PATCH 5/5] Report bug on a package which owns the related SELinux domain --- diff --git a/framework/src/setroubleshoot/browser.py b/framework/src/setroubleshoot/browser.py index 22ffd13..4b1c143 100644 --- a/framework/src/setroubleshoot/browser.py +++ b/framework/src/setroubleshoot/browser.py @@ -997,7 +997,8 @@ class BugReport: text_buf = self.error_submit_text.get_buffer() content = text_buf.get_text(text_buf.get_start_iter(), text_buf.get_end_iter(), False) - signature = report.createAlertSignature("selinux-policy", + local_policy_package = get_rpm_source_package(self.alert.environment.local_policy_rpm) + signature = report.createAlertSignature(local_policy_package, "setroubleshoot", self.alert.get_hash(), self.summary, diff --git a/framework/src/setroubleshoot/util.py b/framework/src/setroubleshoot/util.py index 94bb988..77b3668 100755 --- a/framework/src/setroubleshoot/util.py +++ b/framework/src/setroubleshoot/util.py @@ -37,6 +37,7 @@ __all__ = [ 'get_rpm_nvr_by_file_path', 'get_rpm_nvr_by_type', 'get_rpm_nvr_by_scontext', + 'get_rpm_source_package', 'is_hex', 'split_rpm_nvr', 'file_types', @@ -499,6 +500,29 @@ Finds an SELinux module which defines given SELinux context context = selinux.context_new(str(scontext)) return get_rpm_nvr_by_type(str(selinux.context_type_get(context))) +def get_rpm_source_package(name): + """ + Find a source package for `name` rpm + + >>> get_rpm_source_package("policycoreutils-python-utils") + 'policycoreutils' + + >>> get_rpm_source_package("selinux-policy-targeted") + 'selinux-policy' + + """ + if name is None: + return None + + src = None + try: + import subprocess + src = subprocess.check_output(["rpm", "-q", "--qf", "%{SOURCERPM}", name], universal_newlines=True).rsplit('-',2)[0] + except: + syslog.syslog(syslog.LOG_ERR, "failed to retrieve rpm info for %s" % name) + return src + + def get_user_home_dir(): uid = os.getuid() try: