This improves the robustness of programs using selinux_check_access() in the face of policy updates that alter the values of the class or permissions that they are checking. Otherwise, a policy update can trigger false permission denials, as in https://bugzilla.redhat.com/show_bug.cgi?id=1264051 Changes to the userspace class/permission definitions should still be handled with care, as not all userspace object managers have been converted to use selinux_check_access() and even those that do use it are still not entirely safe against an interleaving of a policy reload and a call to selinux_check_access(). The change does however address the issue in the above bug and avoids the need to restart systemd. This change restores the flush_class_cache() function that was removed in commit 435fae64a931301ac00930af1eebc28bd9b0c576 because it had no users at the time, but makes it hidden to avoid exposing it as part of the libselinux ABI. Signed-off-by: Stephen Smalley <sds@xxxxxxxxxxxxx> --- libselinux/src/checkAccess.c | 20 ++++++++++++++++++-- libselinux/src/selinux_internal.h | 2 ++ libselinux/src/stringrep.c | 22 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/libselinux/src/checkAccess.c b/libselinux/src/checkAccess.c index 29be16e..d91e5bb 100644 --- a/libselinux/src/checkAccess.c +++ b/libselinux/src/checkAccess.c @@ -10,11 +10,25 @@ static pthread_once_t once = PTHREAD_ONCE_INIT; static int selinux_enabled; +static int avc_reset_callback(uint32_t event __attribute__((unused)), + security_id_t ssid __attribute__((unused)), + security_id_t tsid __attribute__((unused)), + security_class_t tclass __attribute__((unused)), + access_vector_t perms __attribute__((unused)), + access_vector_t *out_retained __attribute__((unused))) +{ + flush_class_cache(); + return 0; +} + static void avc_init_once(void) { selinux_enabled = is_selinux_enabled(); - if (selinux_enabled == 1) - avc_open(NULL, 0); + if (selinux_enabled == 1) { + if (avc_open(NULL, 0)) + return; + avc_add_callback(avc_reset_callback, AVC_CALLBACK_RESET, 0, 0, 0, 0); + } } int selinux_check_access(const char *scon, const char *tcon, const char *class, const char *perm, void *aux) { @@ -37,6 +51,8 @@ int selinux_check_access(const char *scon, const char *tcon, const char *class, if (rc < 0) return rc; + (void) avc_netlink_check_nb(); + sclass = string_to_security_class(class); if (sclass == 0) { rc = errno; diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h index 844e408..46566f6 100644 --- a/libselinux/src/selinux_internal.h +++ b/libselinux/src/selinux_internal.h @@ -102,6 +102,8 @@ hidden_proto(security_get_initial_context); hidden_proto(security_get_initial_context_raw); hidden_proto(selinux_reset_config); +hidden void flush_class_cache(void); + extern int load_setlocaldefs hidden; extern int require_seusers hidden; extern int selinux_page_size hidden; diff --git a/libselinux/src/stringrep.c b/libselinux/src/stringrep.c index 9ae8248..16e67fb 100644 --- a/libselinux/src/stringrep.c +++ b/libselinux/src/stringrep.c @@ -158,6 +158,28 @@ err1: return NULL; } +hidden void flush_class_cache(void) +{ + struct discover_class_node *cur = discover_class_cache, *prev = NULL; + size_t i; + + while (cur != NULL) { + free(cur->name); + + for (i=0 ; i<MAXVECTORS ; i++) + free(cur->perms[i]); + + free(cur->perms); + + prev = cur; + cur = cur->next; + + free(prev); + } + + discover_class_cache = NULL; +} + security_class_t string_to_security_class(const char *s) { struct discover_class_node *node; -- 2.1.0 _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.