Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
Jan Rybar | 13ffec06d3 | |
Jan Rybar | a726824204 | |
Jan Rybar | 15bc32c5ba | |
Jan Rybar | 6fbf41703c |
|
@ -0,0 +1,47 @@
|
|||
From 783ec80ec1b4d8f1dc20a2a41dfaddbc1c3f5ab2 Mon Sep 17 00:00:00 2001
|
||||
From: Matthew Leeds <matthew.leeds@endlessm.com>
|
||||
Date: Tue, 11 Dec 2018 12:04:26 -0800
|
||||
Subject: [PATCH] Allow uid of -1 for a PolkitUnixProcess
|
||||
|
||||
Commit 2cb40c4d5 changed PolkitUnixUser, PolkitUnixGroup, and
|
||||
PolkitUnixProcess to allow negative values for their uid/gid properties,
|
||||
since these are values above INT_MAX which wrap around but are still
|
||||
valid, with the exception of -1 which is not valid. However,
|
||||
PolkitUnixProcess allows a uid of -1 to be passed to
|
||||
polkit_unix_process_new_for_owner() which means polkit is expected to
|
||||
figure out the uid on its own (this happens in the _constructed
|
||||
function). So this commit removes the check in
|
||||
polkit_unix_process_set_property() so that new_for_owner() can be used
|
||||
as documented without producing a critical error message.
|
||||
|
||||
This does not affect the protection against CVE-2018-19788 which is
|
||||
based on creating a user with a UID up to but not including 4294967295
|
||||
(-1).
|
||||
---
|
||||
src/polkit/polkitunixprocess.c | 9 ++-------
|
||||
1 file changed, 2 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/src/polkit/polkitunixprocess.c b/src/polkit/polkitunixprocess.c
|
||||
index 78d7251..289a82e 100644
|
||||
--- a/src/polkit/polkitunixprocess.c
|
||||
+++ b/src/polkit/polkitunixprocess.c
|
||||
@@ -228,14 +228,9 @@ polkit_unix_process_set_property (GObject *object,
|
||||
polkit_unix_process_set_pid (unix_process, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
- case PROP_UID: {
|
||||
- gint val;
|
||||
-
|
||||
- val = g_value_get_int (value);
|
||||
- g_return_if_fail (val != -1);
|
||||
- polkit_unix_process_set_uid (unix_process, val);
|
||||
+ case PROP_UID:
|
||||
+ polkit_unix_process_set_uid (unix_process, g_value_get_int (value));
|
||||
break;
|
||||
- }
|
||||
|
||||
case PROP_START_TIME:
|
||||
polkit_unix_process_set_start_time (unix_process, g_value_get_uint64 (value));
|
||||
--
|
||||
2.14.5
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
diff --git a/src/polkit/polkitunixgroup.c b/src/polkit/polkitunixgroup.c
|
||||
index c57a1aaacbb13c4e4297dd812cf5904f2f427b03..309f68918895e0f8b547f8c06f89c6fb1326fe20 100644
|
||||
--- a/src/polkit/polkitunixgroup.c
|
||||
+++ b/src/polkit/polkitunixgroup.c
|
||||
@@ -71,6 +71,7 @@ G_DEFINE_TYPE_WITH_CODE (PolkitUnixGroup, polkit_unix_group, G_TYPE_OBJECT,
|
||||
static void
|
||||
polkit_unix_group_init (PolkitUnixGroup *unix_group)
|
||||
{
|
||||
+ unix_group->gid = -1; /* (git_t) -1 is not a valid GID under Linux */
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -100,11 +101,14 @@ polkit_unix_group_set_property (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PolkitUnixGroup *unix_group = POLKIT_UNIX_GROUP (object);
|
||||
+ gint val;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_GID:
|
||||
- unix_group->gid = g_value_get_int (value);
|
||||
+ val = g_value_get_int (value);
|
||||
+ g_return_if_fail (val != -1);
|
||||
+ unix_group->gid = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -131,9 +135,9 @@ polkit_unix_group_class_init (PolkitUnixGroupClass *klass)
|
||||
g_param_spec_int ("gid",
|
||||
"Group ID",
|
||||
"The UNIX group ID",
|
||||
- 0,
|
||||
+ G_MININT,
|
||||
G_MAXINT,
|
||||
- 0,
|
||||
+ -1,
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_NAME |
|
||||
@@ -166,9 +170,10 @@ polkit_unix_group_get_gid (PolkitUnixGroup *group)
|
||||
*/
|
||||
void
|
||||
polkit_unix_group_set_gid (PolkitUnixGroup *group,
|
||||
- gint gid)
|
||||
+ gint gid)
|
||||
{
|
||||
g_return_if_fail (POLKIT_IS_UNIX_GROUP (group));
|
||||
+ g_return_if_fail (gid != -1);
|
||||
group->gid = gid;
|
||||
}
|
||||
|
||||
@@ -183,6 +188,8 @@ polkit_unix_group_set_gid (PolkitUnixGroup *group,
|
||||
PolkitIdentity *
|
||||
polkit_unix_group_new (gint gid)
|
||||
{
|
||||
+ g_return_val_if_fail (gid != -1, NULL);
|
||||
+
|
||||
return POLKIT_IDENTITY (g_object_new (POLKIT_TYPE_UNIX_GROUP,
|
||||
"gid", gid,
|
||||
NULL));
|
||||
diff --git a/src/polkit/polkitunixprocess.c b/src/polkit/polkitunixprocess.c
|
||||
index 972b7776825d5ccf677ed12ed620fc0c52352547..b02b25894ad120d88ea21d4c96ac8dca1821fcf2 100644
|
||||
--- a/src/polkit/polkitunixprocess.c
|
||||
+++ b/src/polkit/polkitunixprocess.c
|
||||
@@ -159,9 +159,14 @@ polkit_unix_process_set_property (GObject *object,
|
||||
polkit_unix_process_set_pid (unix_process, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
- case PROP_UID:
|
||||
- polkit_unix_process_set_uid (unix_process, g_value_get_int (value));
|
||||
+ case PROP_UID: {
|
||||
+ gint val;
|
||||
+
|
||||
+ val = g_value_get_int (value);
|
||||
+ g_return_if_fail (val != -1);
|
||||
+ polkit_unix_process_set_uid (unix_process, val);
|
||||
break;
|
||||
+ }
|
||||
|
||||
case PROP_START_TIME:
|
||||
polkit_unix_process_set_start_time (unix_process, g_value_get_uint64 (value));
|
||||
@@ -239,7 +244,7 @@ polkit_unix_process_class_init (PolkitUnixProcessClass *klass)
|
||||
g_param_spec_int ("uid",
|
||||
"User ID",
|
||||
"The UNIX user ID",
|
||||
- -1,
|
||||
+ G_MININT,
|
||||
G_MAXINT,
|
||||
-1,
|
||||
G_PARAM_CONSTRUCT |
|
||||
@@ -303,7 +308,6 @@ polkit_unix_process_set_uid (PolkitUnixProcess *process,
|
||||
gint uid)
|
||||
{
|
||||
g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process));
|
||||
- g_return_if_fail (uid >= -1);
|
||||
process->uid = uid;
|
||||
}
|
||||
|
||||
diff --git a/src/polkit/polkitunixuser.c b/src/polkit/polkitunixuser.c
|
||||
index 8bfd3a1fb05ddb56adebd097569a9977b7b922f3..234a6976c573ac65200ee08228cd50111f0c769b 100644
|
||||
--- a/src/polkit/polkitunixuser.c
|
||||
+++ b/src/polkit/polkitunixuser.c
|
||||
@@ -72,6 +72,7 @@ G_DEFINE_TYPE_WITH_CODE (PolkitUnixUser, polkit_unix_user, G_TYPE_OBJECT,
|
||||
static void
|
||||
polkit_unix_user_init (PolkitUnixUser *unix_user)
|
||||
{
|
||||
+ unix_user->uid = -1; /* (uid_t) -1 is not a valid UID under Linux */
|
||||
unix_user->name = NULL;
|
||||
}
|
||||
|
||||
@@ -112,11 +113,14 @@ polkit_unix_user_set_property (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PolkitUnixUser *unix_user = POLKIT_UNIX_USER (object);
|
||||
+ gint val;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_UID:
|
||||
- unix_user->uid = g_value_get_int (value);
|
||||
+ val = g_value_get_int (value);
|
||||
+ g_return_if_fail (val != -1);
|
||||
+ unix_user->uid = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -144,9 +148,9 @@ polkit_unix_user_class_init (PolkitUnixUserClass *klass)
|
||||
g_param_spec_int ("uid",
|
||||
"User ID",
|
||||
"The UNIX user ID",
|
||||
- 0,
|
||||
+ G_MININT,
|
||||
G_MAXINT,
|
||||
- 0,
|
||||
+ -1,
|
||||
G_PARAM_CONSTRUCT |
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_STATIC_NAME |
|
||||
@@ -182,6 +186,7 @@ polkit_unix_user_set_uid (PolkitUnixUser *user,
|
||||
gint uid)
|
||||
{
|
||||
g_return_if_fail (POLKIT_IS_UNIX_USER (user));
|
||||
+ g_return_if_fail (uid != -1);
|
||||
user->uid = uid;
|
||||
}
|
||||
|
||||
@@ -196,6 +201,8 @@ polkit_unix_user_set_uid (PolkitUnixUser *user,
|
||||
PolkitIdentity *
|
||||
polkit_unix_user_new (gint uid)
|
||||
{
|
||||
+ g_return_val_if_fail (uid != -1, NULL);
|
||||
+
|
||||
return POLKIT_IDENTITY (g_object_new (POLKIT_TYPE_UNIX_USER,
|
||||
"uid", uid,
|
||||
NULL));
|
||||
diff --git a/test/data/etc/group b/test/data/etc/group
|
||||
index 12ef328b21b346ee3828ce3aaf15cca83858bd1d..b9acab97211fdf7db521dc0939b2dcfc2c9e350b 100644
|
||||
--- a/test/data/etc/group
|
||||
+++ b/test/data/etc/group
|
||||
@@ -5,3 +5,4 @@ john:x:500:
|
||||
jane:x:501:
|
||||
sally:x:502:
|
||||
henry:x:503:
|
||||
+highuid2:x:4000000000:
|
||||
diff --git a/test/data/etc/passwd b/test/data/etc/passwd
|
||||
index 8544febcd8b1720e5577dfb3f0672a6fef29e701..5cf14a5620259f79806192ca935fee84a29ac96d 100644
|
||||
--- a/test/data/etc/passwd
|
||||
+++ b/test/data/etc/passwd
|
||||
@@ -3,3 +3,5 @@ john:x:500:500:John Done:/home/john:/bin/bash
|
||||
jane:x:501:501:Jane Smith:/home/jane:/bin/bash
|
||||
sally:x:502:502:Sally Derp:/home/sally:/bin/bash
|
||||
henry:x:503:503:Henry Herp:/home/henry:/bin/bash
|
||||
+highuid1:x:2147483648:2147483648:The first high uid:/home/highuid1:/sbin/nologin
|
||||
+highuid2:x:4000000000:4000000000:An example high uid:/home/example:/sbin/nologin
|
||||
diff --git a/test/data/etc/polkit-1/rules.d/10-testing.rules b/test/data/etc/polkit-1/rules.d/10-testing.rules
|
||||
index 446e62291b7fe4c5bacdceb1045350af1a9dc245..98bf062a08cb11fddb7df95d0bcdec1b1ac3587d 100644
|
||||
--- a/test/data/etc/polkit-1/rules.d/10-testing.rules
|
||||
+++ b/test/data/etc/polkit-1/rules.d/10-testing.rules
|
||||
@@ -53,6 +53,27 @@ polkit.addRule(function(action, subject) {
|
||||
}
|
||||
});
|
||||
|
||||
+polkit.addRule(function(action, subject) {
|
||||
+ if (action.id == "net.company.john_action") {
|
||||
+ if (subject.user == "john") {
|
||||
+ return polkit.Result.YES;
|
||||
+ } else {
|
||||
+ return polkit.Result.NO;
|
||||
+ }
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+polkit.addRule(function(action, subject) {
|
||||
+ if (action.id == "net.company.highuid2_action") {
|
||||
+ if (subject.user == "highuid2") {
|
||||
+ return polkit.Result.YES;
|
||||
+ } else {
|
||||
+ return polkit.Result.NO;
|
||||
+ }
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+
|
||||
// ---------------------------------------------------------------------
|
||||
// variables
|
||||
|
||||
diff --git a/test/polkitbackend/test-polkitbackendjsauthority.c b/test/polkitbackend/test-polkitbackendjsauthority.c
|
||||
index b484a26600dbde074ee7d8491f88624fdc83c39c..71aad23e2f5d1a7b15e138f23e6581a31498bad6 100644
|
||||
--- a/test/polkitbackend/test-polkitbackendjsauthority.c
|
||||
+++ b/test/polkitbackend/test-polkitbackendjsauthority.c
|
||||
@@ -330,6 +330,78 @@ static const RulesTestCase rules_test_cases[] = {
|
||||
NULL,
|
||||
POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
|
||||
},
|
||||
+
|
||||
+ {
|
||||
+ /* highuid1 is not a member of group 'users', see test/data/etc/group */
|
||||
+ "group_membership_with_non_member(highuid22)",
|
||||
+ "net.company.group.only_group_users",
|
||||
+ "unix-user:highuid2",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* highuid2 is not a member of group 'users', see test/data/etc/group */
|
||||
+ "group_membership_with_non_member(highuid21)",
|
||||
+ "net.company.group.only_group_users",
|
||||
+ "unix-user:highuid2",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* highuid1 is not a member of group 'users', see test/data/etc/group */
|
||||
+ "group_membership_with_non_member(highuid24)",
|
||||
+ "net.company.group.only_group_users",
|
||||
+ "unix-user:2147483648",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* highuid2 is not a member of group 'users', see test/data/etc/group */
|
||||
+ "group_membership_with_non_member(highuid23)",
|
||||
+ "net.company.group.only_group_users",
|
||||
+ "unix-user:4000000000",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* john is authorized to do this, see 10-testing.rules */
|
||||
+ "john_action",
|
||||
+ "net.company.john_action",
|
||||
+ "unix-user:john",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* only john is authorized to do this, see 10-testing.rules */
|
||||
+ "jane_action",
|
||||
+ "net.company.john_action",
|
||||
+ "unix-user:jane",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* highuid2 is authorized to do this, see 10-testing.rules */
|
||||
+ "highuid2_action",
|
||||
+ "net.company.highuid2_action",
|
||||
+ "unix-user:highuid2",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_AUTHORIZED,
|
||||
+ },
|
||||
+
|
||||
+ {
|
||||
+ /* only highuid2 is authorized to do this, see 10-testing.rules */
|
||||
+ "highuid1_action",
|
||||
+ "net.company.highuid2_action",
|
||||
+ "unix-user:highuid1",
|
||||
+ NULL,
|
||||
+ POLKIT_IMPLICIT_AUTHORIZATION_NOT_AUTHORIZED,
|
||||
+ },
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
21
polkit.spec
21
polkit.spec
|
@ -6,12 +6,18 @@
|
|||
Summary: An authorization framework
|
||||
Name: polkit
|
||||
Version: 0.115
|
||||
Release: 1%{?dist}
|
||||
Release: 2.3%{?dist}
|
||||
License: LGPLv2+
|
||||
URL: http://www.freedesktop.org/wiki/Software/polkit
|
||||
Source0: http://www.freedesktop.org/software/polkit/releases/%{name}-%{version}.tar.gz
|
||||
Source1: http://www.freedesktop.org/software/polkit/releases/%{name}-%{version}.tar.gz.sign
|
||||
Group: System Environment/Libraries
|
||||
|
||||
Patch1: CVE-2018-19788.patch
|
||||
Patch2: start_time-reuse-exploit.patch
|
||||
Patch3: Allow-uid-of-1-for-a-PolkitUnixProcess.patch
|
||||
Patch4: tty-echo-disabled-on-sigint.patch
|
||||
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: glib2-devel >= 2.30.0
|
||||
BuildRequires: expat-devel
|
||||
|
@ -175,6 +181,19 @@ exit 0
|
|||
%{_libdir}/girepository-1.0/*.typelib
|
||||
|
||||
%changelog
|
||||
* Thu Feb 14 2019 Jan Rybar <jrybar@redhat.com> - 0.115-2.3
|
||||
- pkttyagent: PolkitAgentTextListener leaves echo tty disabled if SIGINT/SIGTERM
|
||||
|
||||
* Wed Feb 06 2019 Jan Rybar <jrybar@redhat.com> - 0.115-2.2
|
||||
- Allow pid=-1 for PolkitUnixProcess to prevent 'critical'
|
||||
|
||||
* Wed Jan 09 2019 Jan Rybar <jrybar@redhat.com> - 0.115-2.1
|
||||
- Fix of start_time reuse exploit (slowfork)
|
||||
|
||||
* Fri Dec 07 2018 Jan Rybar <jrybar@redhat.com> - 0.115-2
|
||||
- Fix of CVE-2018-19788, priv escalation with high UIDs
|
||||
- Resolves: rhbz#1655926
|
||||
|
||||
* Tue Jul 10 2018 Miloslav Trmač <mitr@redhat.com> - 0.115-1
|
||||
- Update to 0.115 (CVE-2018-1116)
|
||||
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
From a33fe199e9f085bb0ef859d57cdebfa7b19d8db7 Mon Sep 17 00:00:00 2001
|
||||
From: Colin Walters <walters@verbum.org>
|
||||
Date: Fri, 4 Jan 2019 14:24:48 -0500
|
||||
Subject: [PATCH] backend: Compare PolkitUnixProcess uids for temporary
|
||||
authorizations
|
||||
|
||||
It turns out that the combination of `(pid, start time)` is not
|
||||
enough to be unique. For temporary authorizations, we can avoid
|
||||
separate users racing on pid reuse by simply comparing the uid.
|
||||
|
||||
The original email report is included in full in a new comment.
|
||||
|
||||
Reported-by: Jann Horn <jannh@google.com>
|
||||
|
||||
Closes: https://gitlab.freedesktop.org/polkit/polkit/issues/75
|
||||
---
|
||||
src/polkit/polkitsubject.c | 2 +
|
||||
src/polkit/polkitunixprocess.c | 71 ++++++++++++++++++-
|
||||
.../polkitbackendinteractiveauthority.c | 39 +++++++++-
|
||||
3 files changed, 110 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/polkit/polkitsubject.c b/src/polkit/polkitsubject.c
|
||||
index d4c1182..ccabd0a 100644
|
||||
--- a/src/polkit/polkitsubject.c
|
||||
+++ b/src/polkit/polkitsubject.c
|
||||
@@ -99,6 +99,8 @@ polkit_subject_hash (PolkitSubject *subject)
|
||||
* @b: A #PolkitSubject.
|
||||
*
|
||||
* Checks if @a and @b are equal, ie. represent the same subject.
|
||||
+ * However, avoid calling polkit_subject_equal() to compare two processes;
|
||||
+ * for more information see the `PolkitUnixProcess` documentation.
|
||||
*
|
||||
* This function can be used in e.g. g_hash_table_new().
|
||||
*
|
||||
diff --git a/src/polkit/polkitunixprocess.c b/src/polkit/polkitunixprocess.c
|
||||
index b02b258..78d7251 100644
|
||||
--- a/src/polkit/polkitunixprocess.c
|
||||
+++ b/src/polkit/polkitunixprocess.c
|
||||
@@ -51,7 +51,10 @@
|
||||
* @title: PolkitUnixProcess
|
||||
* @short_description: Unix processs
|
||||
*
|
||||
- * An object for representing a UNIX process.
|
||||
+ * An object for representing a UNIX process. NOTE: This object as
|
||||
+ * designed is now known broken; a mechanism to exploit a delay in
|
||||
+ * start time in the Linux kernel was identified. Avoid
|
||||
+ * calling polkit_subject_equal() to compare two processes.
|
||||
*
|
||||
* To uniquely identify processes, both the process id and the start
|
||||
* time of the process (a monotonic increasing value representing the
|
||||
@@ -66,6 +69,72 @@
|
||||
* polkit_unix_process_new_for_owner() with trusted data.
|
||||
*/
|
||||
|
||||
+/* See https://gitlab.freedesktop.org/polkit/polkit/issues/75
|
||||
+
|
||||
+ But quoting the original email in full here to ensure it's preserved:
|
||||
+
|
||||
+ From: Jann Horn <jannh@google.com>
|
||||
+ Subject: [SECURITY] polkit: temporary auth hijacking via PID reuse and non-atomic fork
|
||||
+ Date: Wednesday, October 10, 2018 5:34 PM
|
||||
+
|
||||
+When a (non-root) user attempts to e.g. control systemd units in the system
|
||||
+instance from an active session over DBus, the access is gated by a polkit
|
||||
+policy that requires "auth_admin_keep" auth. This results in an auth prompt
|
||||
+being shown to the user, asking the user to confirm the action by entering the
|
||||
+password of an administrator account.
|
||||
+
|
||||
+After the action has been confirmed, the auth decision for "auth_admin_keep" is
|
||||
+cached for up to five minutes. Subject to some restrictions, similar actions can
|
||||
+then be performed in this timespan without requiring re-auth:
|
||||
+
|
||||
+ - The PID of the DBus client requesting the new action must match the PID of
|
||||
+ the DBus client requesting the old action (based on SO_PEERCRED information
|
||||
+ forwarded by the DBus daemon).
|
||||
+ - The "start time" of the client's PID (as seen in /proc/$pid/stat, field 22)
|
||||
+ must not have changed. The granularity of this timestamp is in the
|
||||
+ millisecond range.
|
||||
+ - polkit polls every two seconds whether a process with the expected start time
|
||||
+ still exists. If not, the temporary auth entry is purged.
|
||||
+
|
||||
+Without the start time check, this would obviously be buggy because an attacker
|
||||
+could simply wait for the legitimate client to disappear, then create a new
|
||||
+client with the same PID.
|
||||
+
|
||||
+Unfortunately, the start time check is bypassable because fork() is not atomic.
|
||||
+Looking at the source code of copy_process() in the kernel:
|
||||
+
|
||||
+ p->start_time = ktime_get_ns();
|
||||
+ p->real_start_time = ktime_get_boot_ns();
|
||||
+ [...]
|
||||
+ retval = copy_thread_tls(clone_flags, stack_start, stack_size, p, tls);
|
||||
+ if (retval)
|
||||
+ goto bad_fork_cleanup_io;
|
||||
+
|
||||
+ if (pid != &init_struct_pid) {
|
||||
+ pid = alloc_pid(p->nsproxy->pid_ns_for_children);
|
||||
+ if (IS_ERR(pid)) {
|
||||
+ retval = PTR_ERR(pid);
|
||||
+ goto bad_fork_cleanup_thread;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+The ktime_get_boot_ns() call is where the "start time" of the process is
|
||||
+recorded. The alloc_pid() call is where a free PID is allocated. In between
|
||||
+these, some time passes; and because the copy_thread_tls() call between them can
|
||||
+access userspace memory when sys_clone() is invoked through the 32-bit syscall
|
||||
+entry point, an attacker can even stall the kernel arbitrarily long at this
|
||||
+point (by supplying a pointer into userspace memory that is associated with a
|
||||
+userfaultfd or is backed by a custom FUSE filesystem).
|
||||
+
|
||||
+This means that an attacker can immediately call sys_clone() when the victim
|
||||
+process is created, often resulting in a process that has the exact same start
|
||||
+time reported in procfs; and then the attacker can delay the alloc_pid() call
|
||||
+until after the victim process has died and the PID assignment has cycled
|
||||
+around. This results in an attacker process that polkit can't distinguish from
|
||||
+the victim process.
|
||||
+*/
|
||||
+
|
||||
+
|
||||
/**
|
||||
* PolkitUnixProcess:
|
||||
*
|
||||
diff --git a/src/polkitbackend/polkitbackendinteractiveauthority.c b/src/polkitbackend/polkitbackendinteractiveauthority.c
|
||||
index a1630b9..80e8141 100644
|
||||
--- a/src/polkitbackend/polkitbackendinteractiveauthority.c
|
||||
+++ b/src/polkitbackend/polkitbackendinteractiveauthority.c
|
||||
@@ -3031,6 +3031,43 @@ temporary_authorization_store_free (TemporaryAuthorizationStore *store)
|
||||
g_free (store);
|
||||
}
|
||||
|
||||
+/* See the comment at the top of polkitunixprocess.c */
|
||||
+static gboolean
|
||||
+subject_equal_for_authz (PolkitSubject *a,
|
||||
+ PolkitSubject *b)
|
||||
+{
|
||||
+ if (!polkit_subject_equal (a, b))
|
||||
+ return FALSE;
|
||||
+
|
||||
+ /* Now special case unix processes, as we want to protect against
|
||||
+ * pid reuse by including the UID.
|
||||
+ */
|
||||
+ if (POLKIT_IS_UNIX_PROCESS (a) && POLKIT_IS_UNIX_PROCESS (b)) {
|
||||
+ PolkitUnixProcess *ap = (PolkitUnixProcess*)a;
|
||||
+ int uid_a = polkit_unix_process_get_uid ((PolkitUnixProcess*)a);
|
||||
+ PolkitUnixProcess *bp = (PolkitUnixProcess*)b;
|
||||
+ int uid_b = polkit_unix_process_get_uid ((PolkitUnixProcess*)b);
|
||||
+
|
||||
+ if (uid_a != -1 && uid_b != -1)
|
||||
+ {
|
||||
+ if (uid_a == uid_b)
|
||||
+ {
|
||||
+ return TRUE;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ g_printerr ("denying slowfork; pid %d uid %d != %d!\n",
|
||||
+ polkit_unix_process_get_pid (ap),
|
||||
+ uid_a, uid_b);
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+ }
|
||||
+ /* Fall through; one of the uids is unset so we can't reliably compare */
|
||||
+ }
|
||||
+
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
static gboolean
|
||||
temporary_authorization_store_has_authorization (TemporaryAuthorizationStore *store,
|
||||
PolkitSubject *subject,
|
||||
@@ -3073,7 +3110,7 @@ temporary_authorization_store_has_authorization (TemporaryAuthorizationStore *st
|
||||
TemporaryAuthorization *authorization = l->data;
|
||||
|
||||
if (strcmp (action_id, authorization->action_id) == 0 &&
|
||||
- polkit_subject_equal (subject_to_use, authorization->subject))
|
||||
+ subject_equal_for_authz (subject_to_use, authorization->subject))
|
||||
{
|
||||
ret = TRUE;
|
||||
if (out_tmp_authz_id != NULL)
|
||||
--
|
||||
2.20.1
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
diff --git a/src/programs/pkttyagent.c b/src/programs/pkttyagent.c
|
||||
index 3f324b8..3c8d502 100644
|
||||
--- a/src/programs/pkttyagent.c
|
||||
+++ b/src/programs/pkttyagent.c
|
||||
@@ -25,11 +25,44 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
+#include <signal.h>
|
||||
+#include <termios.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <polkit/polkit.h>
|
||||
#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
|
||||
#include <polkitagent/polkitagent.h>
|
||||
|
||||
+
|
||||
+static volatile sig_atomic_t tty_flags_saved;
|
||||
+struct termios ts;
|
||||
+FILE *tty = NULL;
|
||||
+struct sigaction savesigterm, savesigint, savesigtstp;
|
||||
+
|
||||
+
|
||||
+static void tty_handler(int signal)
|
||||
+{
|
||||
+ switch (signal)
|
||||
+ {
|
||||
+ case SIGTERM:
|
||||
+ sigaction (SIGTERM, &savesigterm, NULL);
|
||||
+ break;
|
||||
+ case SIGINT:
|
||||
+ sigaction (SIGINT, &savesigint, NULL);
|
||||
+ break;
|
||||
+ case SIGTSTP:
|
||||
+ sigaction (SIGTSTP, &savesigtstp, NULL);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (tty_flags_saved)
|
||||
+ {
|
||||
+ tcsetattr (fileno (tty), TCSAFLUSH, &ts);
|
||||
+ }
|
||||
+
|
||||
+ kill(getpid(), signal);
|
||||
+}
|
||||
+
|
||||
+
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -74,6 +107,8 @@ main (int argc, char *argv[])
|
||||
GMainLoop *loop = NULL;
|
||||
guint ret = 126;
|
||||
GVariantBuilder builder;
|
||||
+ struct sigaction sa;
|
||||
+ const char *tty_name = NULL;
|
||||
|
||||
/* Disable remote file access from GIO. */
|
||||
setenv ("GIO_USE_VFS", "local", 1);
|
||||
@@ -212,6 +247,27 @@ main (int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
+/* Bash leaves tty echo disabled if SIGINT/SIGTERM comes to polkitagenttextlistener.c::on_request(),
|
||||
+ but due to threading the handlers cannot take care of the signal there.
|
||||
+ Though if controlling terminal cannot be found, the world won't stop spinning.
|
||||
+*/
|
||||
+ tty_name = ctermid(NULL);
|
||||
+ if (tty_name != NULL)
|
||||
+ {
|
||||
+ tty = fopen(tty_name, "r+");
|
||||
+ }
|
||||
+
|
||||
+ if (tty != NULL && !tcgetattr (fileno (tty), &ts))
|
||||
+ {
|
||||
+ tty_flags_saved = TRUE;
|
||||
+ }
|
||||
+
|
||||
+ memset (&sa, 0, sizeof (sa));
|
||||
+ sa.sa_handler = &tty_handler;
|
||||
+ sigaction (SIGTERM, &sa, &savesigterm);
|
||||
+ sigaction (SIGINT, &sa, &savesigint);
|
||||
+ sigaction (SIGTSTP, &sa, &savesigtstp);
|
||||
+
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (loop);
|
||||
|
Loading…
Reference in New Issue