Compare commits
No commits in common. "rawhide" and "f45" have entirely different histories.
7 changed files with 1 additions and 341 deletions
|
|
@ -1,75 +0,0 @@
|
|||
From 431d5a9a5f7ac4ce61210f4e21ecc64e79c8b31a Mon Sep 17 00:00:00 2001
|
||||
From: Vit Mojzis <vmojzis@redhat.com>
|
||||
Date: Mon, 27 Jul 2026 18:20:44 +0200
|
||||
Subject: [PATCH] Restrict RPC dispatch to registered methods only
|
||||
|
||||
get_method_implementation() used unrestricted getattr() to resolve
|
||||
attacker-controlled method names on handler objects. Since server.py
|
||||
binds the full connection object as the RPC handler via
|
||||
connect_rpc_interface('SETroubleshootServer', self), all inherited
|
||||
internal methods (acquire_write_lock, close_connection, etc.) became
|
||||
callable through crafted RPC requests.
|
||||
|
||||
An attacker could connect to the world-writable UNIX socket and invoke
|
||||
acquire_write_lock as a signal, then send any RPC method call. The
|
||||
response path calls send_data() which tries to acquire the same
|
||||
non-reentrant threading.Lock, deadlocking the daemon and making it
|
||||
unresponsive to all clients.
|
||||
|
||||
Validate the requested method name against interface_registry before
|
||||
calling getattr(). The method must exist in the registry with an
|
||||
rpc_def.type matching the expected RPC type ('method' or 'signal').
|
||||
Unregistered names now fall through to the existing "not implemented"
|
||||
error response instead of being dispatched. The expected_type parameter
|
||||
defaults to None for backward compatibility with any callers outside
|
||||
default_request_handler.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||||
---
|
||||
src/setroubleshoot/rpc.py | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/setroubleshoot/rpc.py b/src/setroubleshoot/rpc.py
|
||||
index aca2d7e..6a6f31a 100755
|
||||
--- a/src/setroubleshoot/rpc.py
|
||||
+++ b/src/setroubleshoot/rpc.py
|
||||
@@ -869,10 +869,17 @@ class RpcChannel(ConnectionIO, RpcManage):
|
||||
result_code, result_msg)
|
||||
self.io_watch_remove()
|
||||
|
||||
- def get_method_implementation(self, interface, method):
|
||||
+ def get_method_implementation(self, interface, method, expected_type=None):
|
||||
handler_obj = self.rpc_handlers.get(interface, None)
|
||||
if handler_obj is None:
|
||||
return None
|
||||
+ if expected_type is not None:
|
||||
+ interface_dict = interface_registry.interfaces.get(interface)
|
||||
+ if interface_dict is None:
|
||||
+ return None
|
||||
+ rpc_def = interface_dict.get(method)
|
||||
+ if rpc_def is None or rpc_def.type != expected_type:
|
||||
+ return None
|
||||
method_ptr = getattr(handler_obj, method, None)
|
||||
return method_ptr
|
||||
|
||||
@@ -971,7 +978,7 @@ class RpcChannel(ConnectionIO, RpcManage):
|
||||
self.handle_return(type, rpc_id, body)
|
||||
elif type == 'method':
|
||||
interface, method, args = convert_rpc_xml_to_args(body)
|
||||
- method_ptr = self.get_method_implementation(interface, method)
|
||||
+ method_ptr = self.get_method_implementation(interface, method, 'method')
|
||||
if method_ptr:
|
||||
try:
|
||||
return_args = method_ptr(*args)
|
||||
@@ -990,7 +997,7 @@ class RpcChannel(ConnectionIO, RpcManage):
|
||||
self.emit_rpc(rpc_id, 'error_return', rpc_error_def, method, err_code, err_msg)
|
||||
elif type == 'signal':
|
||||
interface, method, args = convert_rpc_xml_to_args(body)
|
||||
- method_ptr = self.get_method_implementation(interface, method)
|
||||
+ method_ptr = self.get_method_implementation(interface, method, 'signal')
|
||||
if method_ptr:
|
||||
try:
|
||||
method_ptr(*args)
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
From 920ca71562e462ed4d1d02f3ce1b35dec33bd1bb Mon Sep 17 00:00:00 2001
|
||||
From: Vit Mojzis <vmojzis@redhat.com>
|
||||
Date: Fri, 7 Aug 2026 14:57:21 +0200
|
||||
Subject: [PATCH] Reject logon() when peer credentials are unavailable
|
||||
|
||||
get_credentials() returns uid=None for non-Unix client sockets (e.g. an
|
||||
INET/TCP listener). logon() then compared the supplied username against
|
||||
get_identity(None), which falls back to os.getuid() -- the daemon's own
|
||||
uid -- letting any caller authenticate as the 'setroubleshoot' account
|
||||
without a valid password on such a listener. Refuse logon outright when
|
||||
peer credentials could not be obtained.
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||||
---
|
||||
src/setroubleshoot/server.py | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/src/setroubleshoot/server.py b/src/setroubleshoot/server.py
|
||||
index cf0f345..bc38fa3 100755
|
||||
--- a/src/setroubleshoot/server.py
|
||||
+++ b/src/setroubleshoot/server.py
|
||||
@@ -324,6 +324,9 @@ class SetroubleshootdClientConnectionHandler(ClientConnectionHandler,
|
||||
def logon(self, type, username, password):
|
||||
log_debug("logon(%s) type=%s username=%s" % (self, type, username))
|
||||
|
||||
+ if self.uid is None:
|
||||
+ raise ProgramError(ERR_USER_LOOKUP, detail="peer credentials unavailable; refusing logon on non-unix socket")
|
||||
+
|
||||
if username != get_identity(self.uid):
|
||||
raise ProgramError(ERR_USER_LOOKUP, detail="uid=%s does not match logon username (%s)" % (self.uid, username))
|
||||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
From 49f3d79ff621e7cf3876cea759ded665cd192528 Mon Sep 17 00:00:00 2001
|
||||
From: Vit Mojzis <vmojzis@redhat.com>
|
||||
Date: Fri, 7 Aug 2026 14:58:20 +0200
|
||||
Subject: [PATCH] Fix port handling
|
||||
|
||||
- Cast parsed port to int in SocketAddress.parse_inet_addr()
|
||||
|
||||
An explicit port in an {inet} address (e.g. "{inet}127.0.0.1:16983") was
|
||||
kept as the raw regex-matched string instead of being converted to int.
|
||||
socket.bind() requires an int port for AF_INET, so any configured
|
||||
INET/TCP listener with an explicit port failed with TypeError, silently
|
||||
swallowed by ListeningServer.open()'s exception handler, leaving the
|
||||
daemon running with no client listener at all.
|
||||
|
||||
- Forward the configured default port in get_socket_list_from_config()
|
||||
|
||||
parse_socket_address_list() defaults its default_port parameter to None,
|
||||
and get_socket_list_from_config() called it without passing one through,
|
||||
so any {inet} address with no explicit port (e.g. "{inet}127.0.0.1") ended
|
||||
up with SocketAddress.port = None instead of the configured
|
||||
connection.default_port, causing socket.bind() to fail with TypeError.
|
||||
|
||||
- Fix invalid default connection.default_port value
|
||||
|
||||
The shipped default was '69783' with a comment acknowledging it was a
|
||||
placeholder ("FIXME: figure out defined port"). 69783 exceeds the valid
|
||||
TCP port range (0-65535), so any {inet} listen_for_client/client_connect_to
|
||||
address with no explicit port failed with OverflowError on bind()/connect().
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||||
---
|
||||
src/config.py.in | 2 +-
|
||||
src/setroubleshoot/rpc.py | 4 +++-
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/config.py.in b/src/config.py.in
|
||||
index d8e7e35..603a107 100644
|
||||
--- a/src/config.py.in
|
||||
+++ b/src/config.py.in
|
||||
@@ -182,7 +182,7 @@ An empty string implies no limit''',
|
||||
},
|
||||
'connection': {
|
||||
'default_port': {
|
||||
- 'value': '69783', # FIXME: figure out defined port,
|
||||
+ 'value': '16983',
|
||||
'description': '',
|
||||
},
|
||||
},
|
||||
diff --git a/src/setroubleshoot/rpc.py b/src/setroubleshoot/rpc.py
|
||||
index 6a6f31a..142228f 100755
|
||||
--- a/src/setroubleshoot/rpc.py
|
||||
+++ b/src/setroubleshoot/rpc.py
|
||||
@@ -100,7 +100,7 @@ def get_default_port():
|
||||
|
||||
def get_socket_list_from_config(cfg_section):
|
||||
addr_string = get_config(cfg_section, 'address_list')
|
||||
- socket_addresses = parse_socket_address_list(addr_string)
|
||||
+ socket_addresses = parse_socket_address_list(addr_string, get_default_port())
|
||||
return socket_addresses
|
||||
|
||||
|
||||
@@ -577,6 +577,8 @@ class SocketAddress(object):
|
||||
port = match.group(3)
|
||||
if port is None:
|
||||
port = self.default_port
|
||||
+ else:
|
||||
+ port = int(port)
|
||||
|
||||
if addr == 'hostname':
|
||||
addr = get_hostname()
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
From 66942f3b55339e59f768952312548e7eab3d19ca Mon Sep 17 00:00:00 2001
|
||||
From: Vit Mojzis <vmojzis@redhat.com>
|
||||
Date: Fri, 7 Aug 2026 17:44:46 +0200
|
||||
Subject: [PATCH] Require root to delete alerts via D-Bus
|
||||
|
||||
delete_alert() removed an alert from the shared host database without
|
||||
checking the caller's identity, and the shipped D-Bus policy allowed any
|
||||
local user to invoke it. An unprivileged local user could enumerate alerts
|
||||
with get_all_alerts and delete entries other users/administrators rely on.
|
||||
|
||||
Reject non-root callers in delete_alert() itself, and drop delete_alert
|
||||
from the default D-Bus policy context as defense in depth (root already
|
||||
has full access via the existing <policy user="root"> rule).
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||||
---
|
||||
org.fedoraproject.Setroubleshootd.conf | 3 ---
|
||||
src/setroubleshoot/server.py | 2 ++
|
||||
2 files changed, 2 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/org.fedoraproject.Setroubleshootd.conf b/org.fedoraproject.Setroubleshootd.conf
|
||||
index 65a0daa..910958a 100644
|
||||
--- a/org.fedoraproject.Setroubleshootd.conf
|
||||
+++ b/org.fedoraproject.Setroubleshootd.conf
|
||||
@@ -35,9 +35,6 @@
|
||||
<allow send_destination="org.fedoraproject.Setroubleshootd"
|
||||
send_interface="org.fedoraproject.SetroubleshootdIface"
|
||||
send_member="set_filter"/>
|
||||
- <allow send_destination="org.fedoraproject.Setroubleshootd"
|
||||
- send_interface="org.fedoraproject.SetroubleshootdIface"
|
||||
- send_member="delete_alert"/>
|
||||
<allow send_destination="org.fedoraproject.Setroubleshootd"
|
||||
send_interface="org.freedesktop.DBus.Introspectable"/>
|
||||
</policy>
|
||||
diff --git a/src/setroubleshoot/server.py b/src/setroubleshoot/server.py
|
||||
index bc38fa3..dc29f63 100755
|
||||
--- a/src/setroubleshoot/server.py
|
||||
+++ b/src/setroubleshoot/server.py
|
||||
@@ -665,6 +665,8 @@ Deletes an alert from the database.
|
||||
|
||||
* `success(b)`:
|
||||
"""
|
||||
+ if self.connection.get_unix_user(sender) != 0:
|
||||
+ return False
|
||||
try:
|
||||
database = get_host_database()
|
||||
alert = self._get_alert(local_id, database)
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
From c30163055995cff80222899a35722819d8286417 Mon Sep 17 00:00:00 2001
|
||||
From: Vit Mojzis <vmojzis@redhat.com>
|
||||
Date: Fri, 7 Aug 2026 18:03:16 +0200
|
||||
Subject: [PATCH] Protect against malicious socket blocking
|
||||
|
||||
Set a timeout (socket.timeout config, 5s default) on accepted client
|
||||
sockets so a stuck send() eventually times out instead of hanging forever
|
||||
(send_data() already had a Socket.timeout handler for this). As a second,
|
||||
independent layer, skip clients that have not completed logon() in both
|
||||
places that fan data out to the 'sealert' pool: send_alert_notification()
|
||||
and ClientNotifier.signatures_updated().
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||||
---
|
||||
src/setroubleshoot/rpc.py | 1 +
|
||||
src/setroubleshoot/server.py | 4 ++++
|
||||
2 files changed, 5 insertions(+)
|
||||
|
||||
diff --git a/src/setroubleshoot/rpc.py b/src/setroubleshoot/rpc.py
|
||||
index 142228f..ecfd062 100755
|
||||
--- a/src/setroubleshoot/rpc.py
|
||||
+++ b/src/setroubleshoot/rpc.py
|
||||
@@ -687,6 +687,7 @@ class ListeningServer(ConnectionIO):
|
||||
try:
|
||||
client_socket, client_address = socket.accept()
|
||||
fcntl.fcntl(client_socket.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
|
||||
+ client_socket.settimeout(RpcChannel.socket_timeout)
|
||||
client_handler = self.client_connection_handler_class(self.socket_address)
|
||||
client_handler.open(client_socket, client_address)
|
||||
self.connection_state.update(0, ConnectionState.PROBLEM_FLAGS)
|
||||
diff --git a/src/setroubleshoot/server.py b/src/setroubleshoot/server.py
|
||||
index dc29f63..6adc0c2 100755
|
||||
--- a/src/setroubleshoot/server.py
|
||||
+++ b/src/setroubleshoot/server.py
|
||||
@@ -149,6 +149,8 @@ def send_alert_notification(siginfo):
|
||||
system_bus.send_message(alert)
|
||||
|
||||
for client in connection_pool.clients('sealert'):
|
||||
+ if not (client.connection_state.flags & ConnectionState.AUTHENTICATED):
|
||||
+ continue
|
||||
client.alert(siginfo)
|
||||
|
||||
#------------------------------ Variables -------------------------------
|
||||
@@ -445,6 +447,8 @@ class ClientNotifier(object):
|
||||
|
||||
def signatures_updated(self, type, item):
|
||||
for client in self.connection_pool.clients('sealert'):
|
||||
+ if not (client.connection_state.flags & ConnectionState.AUTHENTICATED):
|
||||
+ continue
|
||||
client.signatures_updated(type, item)
|
||||
|
||||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
From 271fcfc09a53fba13b0e31df18acdc5d2605cf05 Mon Sep 17 00:00:00 2001
|
||||
From: Vit Mojzis <vmojzis@redhat.com>
|
||||
Date: Fri, 7 Aug 2026 18:05:56 +0200
|
||||
Subject: [PATCH] Require privileged access to change email alert recipients
|
||||
|
||||
set_email_recipients() only checked that the RPC connection was
|
||||
authenticated, not that the caller was privileged. Since logon() grants
|
||||
authenticated state to any local user allowed to run the sealert client
|
||||
(client_users='*' by default) and never verifies the supplied password,
|
||||
any such user could overwrite the daemon-wide email_alert_recipients file
|
||||
that controls where SELinux alert emails are sent.
|
||||
|
||||
Reuse the existing fix_cmd privilege model (already used to gate running
|
||||
alert fix commands as root) to restrict this to root and users listed in
|
||||
fix_cmd_users.
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||||
---
|
||||
src/setroubleshoot/server.py | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/src/setroubleshoot/server.py b/src/setroubleshoot/server.py
|
||||
index 6adc0c2..bf7a294 100755
|
||||
--- a/src/setroubleshoot/server.py
|
||||
+++ b/src/setroubleshoot/server.py
|
||||
@@ -364,6 +364,9 @@ class SetroubleshootdClientConnectionHandler(ClientConnectionHandler,
|
||||
if not (self.connection_state.flags & ConnectionState.AUTHENTICATED):
|
||||
raise ProgramError(ERR_NOT_AUTHENTICATED)
|
||||
|
||||
+ if self.uid != 0 and not self.access.user_allowed('fix_cmd', self.username):
|
||||
+ raise ProgramError(ERR_USER_PROHIBITED)
|
||||
+
|
||||
email_recipients = recipients
|
||||
email_recipients.write_recipient_file(email_recipients_filepath)
|
||||
|
||||
--
|
||||
2.53.0
|
||||
|
||||
|
|
@ -6,10 +6,9 @@
|
|||
Summary: Helps troubleshoot SELinux problems
|
||||
Name: setroubleshoot
|
||||
Version: 3.3.37
|
||||
Release: 6%{?dist}
|
||||
Release: 5%{?dist}
|
||||
License: GPL-2.0-or-later
|
||||
URL: https://gitlab.com/setroubleshoot/setroubleshoot
|
||||
VCS: git:https://gitlab.com/setroubleshoot/setroubleshoot.git
|
||||
Source0: https://gitlab.com/-/project/24478376/uploads/cbdfc2a87b350583c32b168fd9aad9fd/setroubleshoot-3.3.37.tar.gz
|
||||
Source1: %{name}.tmpfiles
|
||||
Source2: %{name}.sysusers
|
||||
|
|
@ -17,12 +16,6 @@ Source2: %{name}.sysusers
|
|||
# for j in 00*patch; do printf "Patch: %s\n" $j; done
|
||||
Patch: 0001-Update-GPL2-license-texts-to-the-latest-version.patch
|
||||
Patch: 0002-Limit-RPC-request-size-in-RequestReceiver-to-prevent.patch
|
||||
Patch: 0003-Restrict-RPC-dispatch-to-registered-methods-only.patch
|
||||
Patch: 0004-Reject-logon-when-peer-credentials-are-unavailable.patch
|
||||
Patch: 0005-Fix-port-handling.patch
|
||||
Patch: 0006-Require-root-to-delete-alerts-via-D-Bus.patch
|
||||
Patch: 0007-Protect-against-malicious-socket-blocking.patch
|
||||
Patch: 0008-Require-privileged-access-to-change-email-alert-reci.patch
|
||||
BuildRequires: gcc
|
||||
BuildRequires: make
|
||||
BuildRequires: libcap-ng-devel
|
||||
|
|
@ -201,14 +194,6 @@ to user preference. The same tools can be run on existing log files.
|
|||
%doc AUTHORS COPYING ChangeLog DBUS.md NEWS README TODO
|
||||
|
||||
%changelog
|
||||
* Fri Aug 07 2026 Vit Mojzis <vmojzis@redhat.com> - 3.3.37-6
|
||||
- Require privileged access to change email alert recipients
|
||||
- Protect against malicious socket blocking
|
||||
- Require root to delete alerts via D-Bus
|
||||
- Fix port handling
|
||||
- Reject logon() when peer credentials are unavailable
|
||||
- Restrict RPC dispatch to registered methods only
|
||||
|
||||
* Wed Jul 29 2026 Vit Mojzis <vmojzis@redhat.com> - 3.3.37-5
|
||||
- Update GPL2 license texts to the latest version
|
||||
- Limit RPC request size in RequestReceiver to prevent memory exhaustion
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue