diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-09 12:05:16 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-01-09 12:05:16 -0800 |
commit | 9f9310bf87348e36a98ffa09c4e285908c14f592 (patch) | |
tree | 71b76c4c38d152278a15583520b11494f010dce1 /security/selinux/selinuxfs.c | |
parent | eab23bc8a807dbd32ac4f20af4a146d1679f57a3 (diff) | |
parent | bbf5a1d0e5d0fb3bdf90205aa872636122692a50 (diff) |
Merge tag 'selinux-pr-20240105' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux updates from Paul Moore:
- Add a new SELinux initial SID, SECINITSID_INIT, to represent
userspace processes started before the SELinux policy is loaded in
early boot.
Prior to this patch all processes were marked as SECINITSID_KERNEL
before the SELinux policy was loaded, making it difficult to
distinquish early boot userspace processes from the kernel in the
SELinux policy.
For most users this will be a non-issue as the policy is loaded early
enough during boot, but for users who load their SELinux policy
relatively late, this should make it easier to construct meaningful
security policies.
- Cleanups to the selinuxfs code by Al, mostly on VFS related issues
during a policy reload.
The commit description has more detail, but the quick summary is that
we are replacing a disconnected directory approach with a temporary
directory that we swapover at the end of the reload.
- Fix an issue where the input sanity checking on socket bind()
operations was slightly different depending on the presence of
SELinux.
This is caused by the placement of the LSM hooks in the generic
socket layer as opposed to the protocol specific bind() handler where
the protocol specific sanity checks are performed. Mickaƫl has
mentioned that he is working to fix this, but in the meantime we just
ensure that we are replicating the checks properly.
We need to balance the placement of the LSM hooks with the number of
LSM hooks; pushing the hooks down into the protocol layers is likely
not the right answer.
- Update the avc_has_perm_noaudit() prototype to better match the
function definition.
- Migrate from using partial_name_hash() to full_name_hash() the
filename transition hash table.
This improves the quality of the code and has the potential for a
minor performance bump.
- Consolidate some open coded SELinux access vector comparisions into a
single new function, avtab_node_cmp(), and use that instead.
A small, but nice win for code quality and maintainability.
- Updated the SELinux MAINTAINERS entry with additional information
around process, bug reporting, etc.
We're also updating some of our "official" roles: dropping Eric Paris
and adding Ondrej as a reviewer.
- Cleanup the coding style crimes in security/selinux/include.
While I'm not a fan of code churn, I am pushing for more automated
code checks that can be done at the developer level and one of the
obvious things to check for is coding style.
In an effort to start from a "good" base I'm slowly working through
our source files cleaning them up with the help of clang-format and
good ol' fashioned human eyeballs; this has the first batch of these
changes.
I've been splitting the changes up per-file to help reduce the impact
if backports are required (either for LTS or distro kernels), and I
expect the some of the larger files, e.g. hooks.c and ss/services.c,
will likely need to be split even further.
- Cleanup old, outdated comments.
* tag 'selinux-pr-20240105' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: (24 commits)
selinux: Fix error priority for bind with AF_UNSPEC on PF_INET6 socket
selinux: fix style issues in security/selinux/include/initial_sid_to_string.h
selinux: fix style issues in security/selinux/include/xfrm.h
selinux: fix style issues in security/selinux/include/security.h
selinux: fix style issues with security/selinux/include/policycap_names.h
selinux: fix style issues in security/selinux/include/policycap.h
selinux: fix style issues in security/selinux/include/objsec.h
selinux: fix style issues with security/selinux/include/netlabel.h
selinux: fix style issues in security/selinux/include/netif.h
selinux: fix style issues in security/selinux/include/ima.h
selinux: fix style issues in security/selinux/include/conditional.h
selinux: fix style issues in security/selinux/include/classmap.h
selinux: fix style issues in security/selinux/include/avc_ss.h
selinux: align avc_has_perm_noaudit() prototype with definition
selinux: fix style issues in security/selinux/include/avc.h
selinux: fix style issues in security/selinux/include/audit.h
MAINTAINERS: drop Eric Paris from his SELinux role
MAINTAINERS: add Ondrej Mosnacek as a SELinux reviewer
selinux: remove the wrong comment about multithreaded process handling
selinux: introduce an initial SID for early boot processes
...
Diffstat (limited to 'security/selinux/selinuxfs.c')
-rw-r--r-- | security/selinux/selinuxfs.c | 144 |
1 files changed, 66 insertions, 78 deletions
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 6c596ae7fef9..0619a1cbbfbe 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -336,12 +336,9 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, unsigned long *ino); /* declaration for sel_make_policy_nodes */ -static struct dentry *sel_make_disconnected_dir(struct super_block *sb, +static struct dentry *sel_make_swapover_dir(struct super_block *sb, unsigned long *ino); -/* declaration for sel_make_policy_nodes */ -static void sel_remove_entries(struct dentry *de); - static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { @@ -508,13 +505,13 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, struct selinux_policy *newpolicy) { int ret = 0; - struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry; - unsigned int tmp_bool_num, old_bool_num; - char **tmp_bool_names, **old_bool_names; - int *tmp_bool_values, *old_bool_values; + struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir; + unsigned int bool_num = 0; + char **bool_names = NULL; + int *bool_values = NULL; unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */ - tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino); + tmp_parent = sel_make_swapover_dir(fsi->sb, &tmp_ino); if (IS_ERR(tmp_parent)) return PTR_ERR(tmp_parent); @@ -532,8 +529,8 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, goto out; } - ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num, - &tmp_bool_names, &tmp_bool_values); + ret = sel_make_bools(newpolicy, tmp_bool_dir, &bool_num, + &bool_names, &bool_values); if (ret) goto out; @@ -542,38 +539,30 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi, if (ret) goto out; + lock_rename(tmp_parent, fsi->sb->s_root); + /* booleans */ - old_dentry = fsi->bool_dir; - lock_rename(tmp_bool_dir, old_dentry); d_exchange(tmp_bool_dir, fsi->bool_dir); - old_bool_num = fsi->bool_num; - old_bool_names = fsi->bool_pending_names; - old_bool_values = fsi->bool_pending_values; - - fsi->bool_num = tmp_bool_num; - fsi->bool_pending_names = tmp_bool_names; - fsi->bool_pending_values = tmp_bool_values; - - sel_remove_old_bool_data(old_bool_num, old_bool_names, old_bool_values); + swap(fsi->bool_num, bool_num); + swap(fsi->bool_pending_names, bool_names); + swap(fsi->bool_pending_values, bool_values); fsi->bool_dir = tmp_bool_dir; - unlock_rename(tmp_bool_dir, old_dentry); /* classes */ - old_dentry = fsi->class_dir; - lock_rename(tmp_class_dir, old_dentry); d_exchange(tmp_class_dir, fsi->class_dir); fsi->class_dir = tmp_class_dir; - unlock_rename(tmp_class_dir, old_dentry); + + unlock_rename(tmp_parent, fsi->sb->s_root); out: + sel_remove_old_bool_data(bool_num, bool_names, bool_values); /* Since the other temporary dirs are children of tmp_parent * this will handle all the cleanup in the case of a failure before * the swapover */ - sel_remove_entries(tmp_parent); - dput(tmp_parent); /* d_genocide() only handles the children */ + simple_recursive_removal(tmp_parent, NULL); return ret; } @@ -1351,54 +1340,48 @@ static const struct file_operations sel_commit_bools_ops = { .llseek = generic_file_llseek, }; -static void sel_remove_entries(struct dentry *de) -{ - d_genocide(de); - shrink_dcache_parent(de); -} - static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir, unsigned int *bool_num, char ***bool_pending_names, int **bool_pending_values) { int ret; - ssize_t len; - struct dentry *dentry = NULL; - struct inode *inode = NULL; - struct inode_security_struct *isec; - char **names = NULL, *page; + char **names, *page; u32 i, num; - int *values = NULL; - u32 sid; - ret = -ENOMEM; page = (char *)get_zeroed_page(GFP_KERNEL); if (!page) - goto out; + return -ENOMEM; - ret = security_get_bools(newpolicy, &num, &names, &values); + ret = security_get_bools(newpolicy, &num, &names, bool_pending_values); if (ret) goto out; + *bool_num = num; + *bool_pending_names = names; + for (i = 0; i < num; i++) { - ret = -ENOMEM; + struct dentry *dentry; + struct inode *inode; + struct inode_security_struct *isec; + ssize_t len; + u32 sid; + + len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); + if (len >= PAGE_SIZE) { + ret = -ENAMETOOLONG; + break; + } dentry = d_alloc_name(bool_dir, names[i]); - if (!dentry) - goto out; + if (!dentry) { + ret = -ENOMEM; + break; + } - ret = -ENOMEM; inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); if (!inode) { dput(dentry); - goto out; - } - - ret = -ENAMETOOLONG; - len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); - if (len >= PAGE_SIZE) { - dput(dentry); - iput(inode); - goto out; + ret = -ENOMEM; + break; } isec = selinux_inode(inode); @@ -1416,23 +1399,8 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_ inode->i_ino = i|SEL_BOOL_INO_OFFSET; d_add(dentry, inode); } - *bool_num = num; - *bool_pending_names = names; - *bool_pending_values = values; - - free_page((unsigned long)page); - return 0; out: free_page((unsigned long)page); - - if (names) { - for (i = 0; i < num; i++) - kfree(names[i]); - kfree(names); - } - kfree(values); - sel_remove_entries(bool_dir); - return ret; } @@ -1961,20 +1929,40 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name, return dentry; } -static struct dentry *sel_make_disconnected_dir(struct super_block *sb, +static int reject_all(struct mnt_idmap *idmap, struct inode *inode, int mask) +{ + return -EPERM; // no access for anyone, root or no root. +} + +static const struct inode_operations swapover_dir_inode_operations = { + .lookup = simple_lookup, + .permission = reject_all, +}; + +static struct dentry *sel_make_swapover_dir(struct super_block *sb, unsigned long *ino) { - struct inode *inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); + struct dentry *dentry = d_alloc_name(sb->s_root, ".swapover"); + struct inode *inode; - if (!inode) + if (!dentry) return ERR_PTR(-ENOMEM); - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode = sel_make_inode(sb, S_IFDIR); + if (!inode) { + dput(dentry); + return ERR_PTR(-ENOMEM); + } + + inode->i_op = &swapover_dir_inode_operations; inode->i_ino = ++(*ino); /* directory inodes start off with i_nlink == 2 (for "." entry) */ inc_nlink(inode); - return d_obtain_alias(inode); + inode_lock(sb->s_root->d_inode); + d_add(dentry, inode); + inc_nlink(sb->s_root->d_inode); + inode_unlock(sb->s_root->d_inode); + return dentry; } #define NULL_FILE_NAME "null" |