Merge pull request #23986 from poettering/cache-selinux-unit-labels

selinux: make selinux access checks based on unit file contexts read at unit load time
This commit is contained in:
Lennart Poettering
2022-07-21 10:24:51 +02:00
committed by GitHub
8 changed files with 62 additions and 69 deletions
+1
View File
@@ -894,6 +894,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AccessSELinuxContext", "s", NULL, offsetof(Unit, access_selinux_context), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+19
View File
@@ -58,6 +58,7 @@
#include "seccomp-util.h"
#endif
#include "securebits-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "socket-netlink.h"
#include "specifier.h"
@@ -6096,6 +6097,7 @@ int unit_load_fragment(Unit *u) {
assert(u->id);
if (u->transient) {
u->access_selinux_context = mfree(u->access_selinux_context);
u->load_state = UNIT_LOADED;
return 0;
}
@@ -6141,7 +6143,24 @@ int unit_load_fragment(Unit *u) {
u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
u->fragment_mtime = 0;
u->access_selinux_context = mfree(u->access_selinux_context);
} else {
#if HAVE_SELINUX
if (mac_selinux_use()) {
_cleanup_freecon_ char *selcon = NULL;
/* Cache the SELinux context of the unit file here. We'll make use of when checking access permissions to loaded units */
r = fgetfilecon_raw(fileno(f), &selcon);
if (r < 0)
log_unit_warning_errno(u, r, "Failed to read SELinux context of '%s', ignoring: %m", fragment);
r = free_and_strdup(&u->access_selinux_context, selcon);
if (r < 0)
return r;
} else
#endif
u->access_selinux_context = mfree(u->access_selinux_context);
u->load_state = UNIT_LOADED;
u->fragment_mtime = timespec_load(&st.st_mtim);
+20 -32
View File
@@ -179,13 +179,14 @@ static int access_init(sd_bus_error *error) {
*/
int mac_selinux_access_check_internal(
sd_bus_message *message,
const char *path,
const char *unit_path,
const char *unit_context,
const char *permission,
const char *function,
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *tclass, *scon;
const char *tclass, *scon, *acon;
_cleanup_free_ char *cl = NULL;
_cleanup_freecon_ char *fcon = NULL;
char **cmdline = NULL;
@@ -214,37 +215,22 @@ int mac_selinux_access_check_internal(
if (r < 0)
return r;
/* The SELinux context is something we really should have
* gotten directly from the message or sender, and not be an
* augmented field. If it was augmented we cannot use it for
* authorization, since this is racy and vulnerable. Let's add
* an extra check, just in case, even though this really
* shouldn't be possible. */
/* The SELinux context is something we really should have gotten directly from the message or sender,
* and not be an augmented field. If it was augmented we cannot use it for authorization, since this
* is racy and vulnerable. Let's add an extra check, just in case, even though this really shouldn't
* be possible. */
assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
r = sd_bus_creds_get_selinux_context(creds, &scon);
if (r < 0)
return r;
if (path) {
/* Get the file context of the unit file */
if (getfilecon_raw(path, &fcon) < 0) {
r = -errno;
log_warning_errno(r, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m",
path,
enforce ? "" : ", ignoring",
permission);
if (!enforce)
return 0;
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
}
if (unit_context) {
/* Nice! The unit comes with a SELinux context read from the unit file */
acon = unit_context;
tclass = "service";
} else {
/* If no unit context is known, use our own */
if (getcon_raw(&fcon) < 0) {
r = -errno;
@@ -254,9 +240,10 @@ int mac_selinux_access_check_internal(
if (!enforce)
return 0;
return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
}
acon = fcon;
tclass = "system";
}
@@ -265,22 +252,22 @@ int mac_selinux_access_check_internal(
struct audit_info audit_info = {
.creds = creds,
.path = path,
.path = unit_path,
.cmdline = cl,
.function = function,
};
r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
r = selinux_check_access(scon, acon, tclass, permission, &audit_info);
if (r < 0) {
r = errno_or_else(EPERM);
errno = -(r = errno_or_else(EPERM));
if (enforce)
sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m");
}
log_full_errno_zerook(LOG_DEBUG, r,
"SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m",
scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(path), isempty(cl) ? "n/a" : cl);
scon, acon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(unit_path), strna(empty_to_null(cl)));
return enforce ? r : 0;
}
@@ -288,7 +275,8 @@ int mac_selinux_access_check_internal(
int mac_selinux_access_check_internal(
sd_bus_message *message,
const char *path,
const char *unit_path,
const char *unit_label,
const char *permission,
const char *function,
sd_bus_error *error) {
+3 -7
View File
@@ -5,14 +5,10 @@
#include "manager.h"
int mac_selinux_access_check_internal(sd_bus_message *message,
const char *path,
const char *permission,
const char *function,
sd_bus_error *error);
int mac_selinux_access_check_internal(sd_bus_message *message, const char *unit_path, const char *unit_label, const char *permission, const char *function, sd_bus_error *error);
#define mac_selinux_access_check(message, permission, error) \
mac_selinux_access_check_internal((message), NULL, (permission), __func__, (error))
mac_selinux_access_check_internal((message), NULL, NULL, (permission), __func__, (error))
#define mac_selinux_unit_access_check(unit, message, permission, error) \
mac_selinux_access_check_internal((message), unit_label_path(unit), (permission), __func__, (error))
mac_selinux_access_check_internal((message), (unit)->fragment_path, (unit)->access_selinux_context, (permission), __func__, (error))
+3
View File
@@ -732,6 +732,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
if (u->access_selinux_context)
fprintf(f, "%s\tAccess SELinux Context: %s\n", prefix, u->access_selinux_context);
following = unit_following(u);
if (following)
fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
+2 -28
View File
@@ -804,6 +804,8 @@ Unit* unit_free(Unit *u) {
free(u->job_timeout_reboot_arg);
free(u->reboot_arg);
free(u->access_selinux_context);
set_free_free(u->aliases);
free(u->id);
@@ -5550,34 +5552,6 @@ bool unit_needs_console(Unit *u) {
return exec_context_may_touch_console(ec);
}
const char *unit_label_path(const Unit *u) {
const char *p;
assert(u);
/* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
* when validating access checks. */
if (IN_SET(u->load_state, UNIT_MASKED, UNIT_NOT_FOUND, UNIT_MERGED))
return NULL; /* Shortcut things if we know there is no real, relevant unit file around */
p = u->source_path ?: u->fragment_path;
if (!p)
return NULL;
if (IN_SET(u->load_state, UNIT_LOADED, UNIT_BAD_SETTING, UNIT_ERROR))
return p; /* Shortcut things, if we successfully loaded at least some stuff from the unit file */
/* Not loaded yet, we need to go to disk */
assert(u->load_state == UNIT_STUB);
/* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
if (null_or_empty_path(p) > 0)
return NULL;
return p;
}
int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) {
int r;
+5 -2
View File
@@ -151,6 +151,11 @@ typedef struct Unit {
char *description;
char **documentation;
/* The SELinux context used for checking access to this unit read off the unit file at load time (do
* not confuse with the selinux_context field in ExecContext which is the SELinux context we'll set
* for processes) */
char *access_selinux_context;
char *fragment_path; /* if loaded from a config file this is the primary path to it */
char *source_path; /* if converted, the source file */
char **dropin_paths;
@@ -944,8 +949,6 @@ int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func);
bool unit_needs_console(Unit *u);
const char *unit_label_path(const Unit *u);
int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
static inline bool unit_has_job_type(Unit *u, JobType type) {