libselinux: flush the class/perm string mapping cache on policy reload
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 435fae64a9
("libselinux: Remove unused flush_class_cache method")
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@tycho.nsa.gov>
This commit is contained in:
parent
c56c2a3b2d
commit
b408d72ca9
3 changed files with 46 additions and 5 deletions
|
@ -10,11 +10,26 @@
|
|||
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) {
|
||||
|
@ -33,9 +48,11 @@ int selinux_check_access(const char *scon, const char *tcon, const char *class,
|
|||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = avc_context_to_sid(tcon, &tcon_id);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
rc = avc_context_to_sid(tcon, &tcon_id);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
(void) avc_netlink_check_nb();
|
||||
|
||||
sclass = string_to_security_class(class);
|
||||
if (sclass == 0) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue