From fbd9acb2dc2aa55902c48a83f157082849209fba Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Thu, 11 Apr 2019 13:11:54 -0700 Subject: [PATCH] LSM: SafeSetID: add read handler For debugging a running system, it is very helpful to be able to see what policy the system is using. Add a read handler that can dump out a copy of the loaded policy. Signed-off-by: Jann Horn Signed-off-by: Micah Morton --- security/safesetid/lsm.h | 1 + security/safesetid/securityfs.c | 35 +++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/security/safesetid/lsm.h b/security/safesetid/lsm.h index 4a34f558d964..db6d16e6bbc3 100644 --- a/security/safesetid/lsm.h +++ b/security/safesetid/lsm.h @@ -41,6 +41,7 @@ struct setuid_rule { struct setuid_ruleset { DECLARE_HASHTABLE(rules, SETID_HASH_BITS); + char *policy_str; struct rcu_head rcu; }; diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c index 250d59e046c1..997b403c6255 100644 --- a/security/safesetid/securityfs.c +++ b/security/safesetid/securityfs.c @@ -19,7 +19,7 @@ #include "lsm.h" -static DEFINE_SPINLOCK(policy_update_lock); +static DEFINE_MUTEX(policy_update_lock); /* * In the case the input buffer contains one or more invalid UIDs, the kuid_t @@ -67,6 +67,7 @@ static void __release_ruleset(struct rcu_head *rcu) hash_for_each_safe(pol->rules, bucket, tmp, rule, next) kfree(rule); + kfree(pol->policy_str); kfree(pol); } @@ -85,6 +86,7 @@ static ssize_t handle_policy_update(struct file *file, pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL); if (!pol) return -ENOMEM; + pol->policy_str = NULL; hash_init(pol->rules); p = buf = memdup_user_nul(ubuf, len); @@ -92,6 +94,11 @@ static ssize_t handle_policy_update(struct file *file, err = PTR_ERR(buf); goto out_free_pol; } + pol->policy_str = kstrdup(buf, GFP_KERNEL); + if (pol->policy_str == NULL) { + err = -ENOMEM; + goto out_free_buf; + } /* policy lines, including the last one, end with \n */ while (*p != '\0') { @@ -135,10 +142,10 @@ out_free_rule: * What we really want here is an xchg() wrapper for RCU, but since that * doesn't currently exist, just use a spinlock for now. */ - spin_lock(&policy_update_lock); + mutex_lock(&policy_update_lock); rcu_swap_protected(safesetid_setuid_rules, pol, lockdep_is_held(&policy_update_lock)); - spin_unlock(&policy_update_lock); + mutex_unlock(&policy_update_lock); err = len; out_free_buf: @@ -162,7 +169,27 @@ static ssize_t safesetid_file_write(struct file *file, return handle_policy_update(file, buf, len); } +static ssize_t safesetid_file_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t res = 0; + struct setuid_ruleset *pol; + const char *kbuf; + + mutex_lock(&policy_update_lock); + pol = rcu_dereference_protected(safesetid_setuid_rules, + lockdep_is_held(&policy_update_lock)); + if (pol) { + kbuf = pol->policy_str; + res = simple_read_from_buffer(buf, len, ppos, + kbuf, strlen(kbuf)); + } + mutex_unlock(&policy_update_lock); + return res; +} + static const struct file_operations safesetid_file_fops = { + .read = safesetid_file_read, .write = safesetid_file_write, }; @@ -181,7 +208,7 @@ static int __init safesetid_init_securityfs(void) goto error; } - policy_file = securityfs_create_file("whitelist_policy", 0200, + policy_file = securityfs_create_file("whitelist_policy", 0600, policy_dir, NULL, &safesetid_file_fops); if (IS_ERR(policy_file)) { ret = PTR_ERR(policy_file); -- 2.30.2