[systemd-devel] [PATCH v3 2/2] selinux: fix unnecessary generic SELinux check due to unit objects in UNIT_NOT_FOUND
HATAYAMA Daisuke
d.hatayama at jp.fujitsu.com
Thu Jun 18 21:16:48 PDT 2015
systemd creates a unit object of A.service when it is referenced in
various contexts such as that systemd parses a unit file and finds a
dependency, like After=A.service, in some unit file or via systemd is
requested a D-Bus operation such as systemctl status A.service, and
then registers it in manager->units hash table.
If the referenced unit file actually doesn't exist, load state of the
unit object becomes UNIT_NOT_FOUND and is left in manager->units until
it is cleaned up at the next manager loop.
The problem here is that mac_selinux_unit_access_check_strv()
overlooks the situation that some unit object of A.service in
UNIT_NOT_FOUND could still be left in the manager->units hash table
but a new unit file A.service has just been created. Then, the unit
object in UNIT_NOT_FOUND state hides presence of the A.service. As a
result, we don't lead to an SELinux unit access check with a SELinux
context label of the A.service.
To fix this, if manager_load_unit() returns a unit object in
UNIT_NOT_FOUND from the manager->units hash table, clean up the unit
object and retry loading unit files.
---
src/core/manager.c | 4 ++--
src/core/manager.h | 2 ++
src/core/selinux-access.c | 14 ++++++++++++++
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/core/manager.c b/src/core/manager.c
index a1c5433..d05d6fb 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -771,7 +771,7 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
return bus_init(m, try_bus_connect);
}
-static unsigned manager_dispatch_cleanup_queue(Manager *m) {
+unsigned manager_dispatch_cleanup_queue(Manager *m) {
Unit *u;
unsigned n = 0;
@@ -847,7 +847,7 @@ good:
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
}
-static unsigned manager_dispatch_gc_queue(Manager *m) {
+unsigned manager_dispatch_gc_queue(Manager *m) {
Unit *u;
unsigned n = 0;
unsigned gc_marker;
diff --git a/src/core/manager.h b/src/core/manager.h
index 4ef869d..bea7a17 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -322,6 +322,8 @@ void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
void manager_clear_jobs(Manager *m);
+unsigned manager_dispatch_cleanup_queue(Manager *m);
+unsigned manager_dispatch_gc_queue(Manager *m);
unsigned manager_dispatch_load_queue(Manager *m);
int manager_environment_add(Manager *m, char **minus, char **plus);
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index f52bc6d..5921898 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -292,10 +292,24 @@ int mac_selinux_unit_access_check_strv(char **units,
int r;
STRV_FOREACH(i, units) {
+ retry:
r = manager_load_unit(m, *i, NULL, error, &u);
if (r < 0)
return r;
if (u) {
+ /* Clean up an unit object in UNIT_NOT_FOUND
+ * load state if it's still left in m->units,
+ * and then retry to load a given unit file.
+ * Give up UNIT_ERROR case to aovid such as
+ * infinite loop due to ENOMEM.
+ */
+ if (r == 1 && u->load_state == UNIT_NOT_FOUND) {
+ unit_add_to_gc_queue(u);
+ manager_dispatch_gc_queue(m);
+ unit_add_to_cleanup_queue(u);
+ manager_dispatch_cleanup_queue(m);
+ goto retry;
+ }
r = mac_selinux_unit_access_check(u, message, permission, error);
if (r < 0)
return r;
More information about the systemd-devel
mailing list