mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-02 03:30:08 -04:00
Merge tag 'apparmor-pr-mainline-2026-03-09' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull AppArmor fixes from John Johansen: - fix race between freeing data and fs accessing it - fix race on unreferenced rawdata dereference - fix differential encoding verification - fix unconfined unprivileged local user can do privileged policy management - Fix double free of ns_name in aa_replace_profiles() - fix missing bounds check on DEFAULT table in verify_dfa() - fix side-effect bug in match_char() macro usage - fix: limit the number of levels of policy namespaces - replace recursive profile removal with iterative approach - fix memory leak in verify_header - validate DFA start states are in bounds in unpack_pdb * tag 'apparmor-pr-mainline-2026-03-09' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: apparmor: fix race between freeing data and fs accessing it apparmor: fix race on rawdata dereference apparmor: fix differential encoding verification apparmor: fix unprivileged local user can do privileged policy management apparmor: Fix double free of ns_name in aa_replace_profiles() apparmor: fix missing bounds check on DEFAULT table in verify_dfa() apparmor: fix side-effect bug in match_char() macro usage apparmor: fix: limit the number of levels of policy namespaces apparmor: replace recursive profile removal with iterative approach apparmor: fix memory leak in verify_header apparmor: validate DFA start states are in bounds in unpack_pdb
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include "include/crypto.h"
|
||||
#include "include/ipc.h"
|
||||
#include "include/label.h"
|
||||
#include "include/lib.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_ns.h"
|
||||
#include "include/resource.h"
|
||||
@@ -62,6 +63,7 @@
|
||||
* securityfs and apparmorfs filesystems.
|
||||
*/
|
||||
|
||||
#define IREF_POISON 101
|
||||
|
||||
/*
|
||||
* support fns
|
||||
@@ -79,7 +81,7 @@ static void rawdata_f_data_free(struct rawdata_f_data *private)
|
||||
if (!private)
|
||||
return;
|
||||
|
||||
aa_put_loaddata(private->loaddata);
|
||||
aa_put_i_loaddata(private->loaddata);
|
||||
kvfree(private);
|
||||
}
|
||||
|
||||
@@ -153,6 +155,71 @@ static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct aa_ns *get_ns_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
if (ref) {
|
||||
struct aa_label *reflabel = container_of(ref, struct aa_label,
|
||||
count);
|
||||
return aa_get_ns(labels_ns(reflabel));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
if (ref)
|
||||
return aa_get_proxy(container_of(ref, struct aa_proxy, count));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
if (ref)
|
||||
return aa_get_i_loaddata(container_of(ref, struct aa_loaddata,
|
||||
count));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void aa_put_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
if (!ref)
|
||||
return;
|
||||
|
||||
switch (ref->reftype) {
|
||||
case REF_RAWDATA:
|
||||
aa_put_i_loaddata(container_of(ref, struct aa_loaddata,
|
||||
count));
|
||||
break;
|
||||
case REF_PROXY:
|
||||
aa_put_proxy(container_of(ref, struct aa_proxy,
|
||||
count));
|
||||
break;
|
||||
case REF_NS:
|
||||
/* ns count is held on its unconfined label */
|
||||
aa_put_ns(labels_ns(container_of(ref, struct aa_label, count)));
|
||||
break;
|
||||
default:
|
||||
AA_BUG(true, "unknown refcount type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void aa_get_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
kref_get(&ref->count);
|
||||
}
|
||||
|
||||
static void aafs_evict(struct inode *inode)
|
||||
{
|
||||
struct aa_common_ref *ref = inode->i_private;
|
||||
|
||||
clear_inode(inode);
|
||||
aa_put_common_ref(ref);
|
||||
inode->i_private = (void *) IREF_POISON;
|
||||
}
|
||||
|
||||
static void aafs_free_inode(struct inode *inode)
|
||||
{
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
@@ -162,6 +229,7 @@ static void aafs_free_inode(struct inode *inode)
|
||||
|
||||
static const struct super_operations aafs_super_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.evict_inode = aafs_evict,
|
||||
.free_inode = aafs_free_inode,
|
||||
.show_path = aafs_show_path,
|
||||
};
|
||||
@@ -262,7 +330,8 @@ static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry,
|
||||
* aafs_remove(). Will return ERR_PTR on failure.
|
||||
*/
|
||||
static struct dentry *aafs_create(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data, void *link,
|
||||
struct dentry *parent,
|
||||
struct aa_common_ref *data, void *link,
|
||||
const struct file_operations *fops,
|
||||
const struct inode_operations *iops)
|
||||
{
|
||||
@@ -299,6 +368,9 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
|
||||
goto fail_dentry;
|
||||
inode_unlock(dir);
|
||||
|
||||
if (data)
|
||||
aa_get_common_ref(data);
|
||||
|
||||
return dentry;
|
||||
|
||||
fail_dentry:
|
||||
@@ -323,7 +395,8 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
|
||||
* see aafs_create
|
||||
*/
|
||||
static struct dentry *aafs_create_file(const char *name, umode_t mode,
|
||||
struct dentry *parent, void *data,
|
||||
struct dentry *parent,
|
||||
struct aa_common_ref *data,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
return aafs_create(name, mode, parent, data, NULL, fops, NULL);
|
||||
@@ -409,7 +482,8 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
|
||||
|
||||
data->size = copy_size;
|
||||
if (copy_from_user(data->data, userbuf, copy_size)) {
|
||||
aa_put_loaddata(data);
|
||||
/* trigger free - don't need to put pcount */
|
||||
aa_put_i_loaddata(data);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
|
||||
@@ -417,7 +491,8 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
|
||||
}
|
||||
|
||||
static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
|
||||
loff_t *pos, struct aa_ns *ns)
|
||||
loff_t *pos, struct aa_ns *ns,
|
||||
const struct cred *ocred)
|
||||
{
|
||||
struct aa_loaddata *data;
|
||||
struct aa_label *label;
|
||||
@@ -428,7 +503,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
|
||||
/* high level check about policy management - fine grained in
|
||||
* below after unpack
|
||||
*/
|
||||
error = aa_may_manage_policy(current_cred(), label, ns, mask);
|
||||
error = aa_may_manage_policy(current_cred(), label, ns, ocred, mask);
|
||||
if (error)
|
||||
goto end_section;
|
||||
|
||||
@@ -436,7 +511,10 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
|
||||
error = PTR_ERR(data);
|
||||
if (!IS_ERR(data)) {
|
||||
error = aa_replace_profiles(ns, label, mask, data);
|
||||
aa_put_loaddata(data);
|
||||
/* put pcount, which will put count and free if no
|
||||
* profiles referencing it.
|
||||
*/
|
||||
aa_put_profile_loaddata(data);
|
||||
}
|
||||
end_section:
|
||||
end_current_label_crit_section(label);
|
||||
@@ -448,8 +526,9 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
|
||||
static ssize_t profile_load(struct file *f, const char __user *buf, size_t size,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
|
||||
int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns);
|
||||
struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private);
|
||||
int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns,
|
||||
f->f_cred);
|
||||
|
||||
aa_put_ns(ns);
|
||||
|
||||
@@ -465,9 +544,9 @@ static const struct file_operations aa_fs_profile_load = {
|
||||
static ssize_t profile_replace(struct file *f, const char __user *buf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
|
||||
struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private);
|
||||
int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY,
|
||||
buf, size, pos, ns);
|
||||
buf, size, pos, ns, f->f_cred);
|
||||
aa_put_ns(ns);
|
||||
|
||||
return error;
|
||||
@@ -485,14 +564,14 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
|
||||
struct aa_loaddata *data;
|
||||
struct aa_label *label;
|
||||
ssize_t error;
|
||||
struct aa_ns *ns = aa_get_ns(f->f_inode->i_private);
|
||||
struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private);
|
||||
|
||||
label = begin_current_label_crit_section();
|
||||
/* high level check about policy management - fine grained in
|
||||
* below after unpack
|
||||
*/
|
||||
error = aa_may_manage_policy(current_cred(), label, ns,
|
||||
AA_MAY_REMOVE_POLICY);
|
||||
f->f_cred, AA_MAY_REMOVE_POLICY);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
@@ -506,7 +585,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
|
||||
if (!IS_ERR(data)) {
|
||||
data->data[size] = 0;
|
||||
error = aa_remove_profiles(ns, label, data->data, size);
|
||||
aa_put_loaddata(data);
|
||||
aa_put_profile_loaddata(data);
|
||||
}
|
||||
out:
|
||||
end_current_label_crit_section(label);
|
||||
@@ -575,7 +654,7 @@ static int ns_revision_open(struct inode *inode, struct file *file)
|
||||
if (!rev)
|
||||
return -ENOMEM;
|
||||
|
||||
rev->ns = aa_get_ns(inode->i_private);
|
||||
rev->ns = get_ns_common_ref(inode->i_private);
|
||||
if (!rev->ns)
|
||||
rev->ns = aa_get_current_ns();
|
||||
file->private_data = rev;
|
||||
@@ -1061,7 +1140,7 @@ static const struct file_operations seq_profile_ ##NAME ##_fops = { \
|
||||
static int seq_profile_open(struct inode *inode, struct file *file,
|
||||
int (*show)(struct seq_file *, void *))
|
||||
{
|
||||
struct aa_proxy *proxy = aa_get_proxy(inode->i_private);
|
||||
struct aa_proxy *proxy = get_proxy_common_ref(inode->i_private);
|
||||
int error = single_open(file, show, proxy);
|
||||
|
||||
if (error) {
|
||||
@@ -1253,18 +1332,17 @@ static const struct file_operations seq_rawdata_ ##NAME ##_fops = { \
|
||||
static int seq_rawdata_open(struct inode *inode, struct file *file,
|
||||
int (*show)(struct seq_file *, void *))
|
||||
{
|
||||
struct aa_loaddata *data = __aa_get_loaddata(inode->i_private);
|
||||
struct aa_loaddata *data = get_loaddata_common_ref(inode->i_private);
|
||||
int error;
|
||||
|
||||
if (!data)
|
||||
/* lost race this ent is being reaped */
|
||||
return -ENOENT;
|
||||
|
||||
error = single_open(file, show, data);
|
||||
if (error) {
|
||||
AA_BUG(file->private_data &&
|
||||
((struct seq_file *)file->private_data)->private);
|
||||
aa_put_loaddata(data);
|
||||
aa_put_i_loaddata(data);
|
||||
}
|
||||
|
||||
return error;
|
||||
@@ -1275,7 +1353,7 @@ static int seq_rawdata_release(struct inode *inode, struct file *file)
|
||||
struct seq_file *seq = (struct seq_file *) file->private_data;
|
||||
|
||||
if (seq)
|
||||
aa_put_loaddata(seq->private);
|
||||
aa_put_i_loaddata(seq->private);
|
||||
|
||||
return single_release(inode, file);
|
||||
}
|
||||
@@ -1387,9 +1465,8 @@ static int rawdata_open(struct inode *inode, struct file *file)
|
||||
if (!aa_current_policy_view_capable(NULL))
|
||||
return -EACCES;
|
||||
|
||||
loaddata = __aa_get_loaddata(inode->i_private);
|
||||
loaddata = get_loaddata_common_ref(inode->i_private);
|
||||
if (!loaddata)
|
||||
/* lost race: this entry is being reaped */
|
||||
return -ENOENT;
|
||||
|
||||
private = rawdata_f_data_alloc(loaddata->size);
|
||||
@@ -1414,7 +1491,7 @@ static int rawdata_open(struct inode *inode, struct file *file)
|
||||
return error;
|
||||
|
||||
fail_private_alloc:
|
||||
aa_put_loaddata(loaddata);
|
||||
aa_put_i_loaddata(loaddata);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1431,7 +1508,6 @@ static void remove_rawdata_dents(struct aa_loaddata *rawdata)
|
||||
|
||||
for (i = 0; i < AAFS_LOADDATA_NDENTS; i++) {
|
||||
if (!IS_ERR_OR_NULL(rawdata->dents[i])) {
|
||||
/* no refcounts on i_private */
|
||||
aafs_remove(rawdata->dents[i]);
|
||||
rawdata->dents[i] = NULL;
|
||||
}
|
||||
@@ -1474,35 +1550,37 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
|
||||
return PTR_ERR(dir);
|
||||
rawdata->dents[AAFS_LOADDATA_DIR] = dir;
|
||||
|
||||
dent = aafs_create_file("abi", S_IFREG | 0444, dir, rawdata,
|
||||
dent = aafs_create_file("abi", S_IFREG | 0444, dir, &rawdata->count,
|
||||
&seq_rawdata_abi_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
rawdata->dents[AAFS_LOADDATA_ABI] = dent;
|
||||
|
||||
dent = aafs_create_file("revision", S_IFREG | 0444, dir, rawdata,
|
||||
&seq_rawdata_revision_fops);
|
||||
dent = aafs_create_file("revision", S_IFREG | 0444, dir,
|
||||
&rawdata->count,
|
||||
&seq_rawdata_revision_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
rawdata->dents[AAFS_LOADDATA_REVISION] = dent;
|
||||
|
||||
if (aa_g_hash_policy) {
|
||||
dent = aafs_create_file("sha256", S_IFREG | 0444, dir,
|
||||
rawdata, &seq_rawdata_hash_fops);
|
||||
&rawdata->count,
|
||||
&seq_rawdata_hash_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
rawdata->dents[AAFS_LOADDATA_HASH] = dent;
|
||||
}
|
||||
|
||||
dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir,
|
||||
rawdata,
|
||||
&rawdata->count,
|
||||
&seq_rawdata_compressed_size_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent;
|
||||
|
||||
dent = aafs_create_file("raw_data", S_IFREG | 0444,
|
||||
dir, rawdata, &rawdata_fops);
|
||||
dent = aafs_create_file("raw_data", S_IFREG | 0444, dir,
|
||||
&rawdata->count, &rawdata_fops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
rawdata->dents[AAFS_LOADDATA_DATA] = dent;
|
||||
@@ -1510,13 +1588,11 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata)
|
||||
|
||||
rawdata->ns = aa_get_ns(ns);
|
||||
list_add(&rawdata->list, &ns->rawdata_list);
|
||||
/* no refcount on inode rawdata */
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
remove_rawdata_dents(rawdata);
|
||||
|
||||
return PTR_ERR(dent);
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
@@ -1540,13 +1616,10 @@ void __aafs_profile_rmdir(struct aa_profile *profile)
|
||||
__aafs_profile_rmdir(child);
|
||||
|
||||
for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) {
|
||||
struct aa_proxy *proxy;
|
||||
if (!profile->dents[i])
|
||||
continue;
|
||||
|
||||
proxy = d_inode(profile->dents[i])->i_private;
|
||||
aafs_remove(profile->dents[i]);
|
||||
aa_put_proxy(proxy);
|
||||
profile->dents[i] = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1580,14 +1653,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name,
|
||||
struct aa_profile *profile,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy);
|
||||
struct dentry *dent;
|
||||
|
||||
dent = aafs_create_file(name, S_IFREG | 0444, dir, proxy, fops);
|
||||
if (IS_ERR(dent))
|
||||
aa_put_proxy(proxy);
|
||||
|
||||
return dent;
|
||||
return aafs_create_file(name, S_IFREG | 0444, dir, &profile->label.proxy->count, fops);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
@@ -1637,7 +1703,8 @@ static const char *rawdata_get_link_base(struct dentry *dentry,
|
||||
struct delayed_call *done,
|
||||
const char *name)
|
||||
{
|
||||
struct aa_proxy *proxy = inode->i_private;
|
||||
struct aa_common_ref *ref = inode->i_private;
|
||||
struct aa_proxy *proxy = container_of(ref, struct aa_proxy, count);
|
||||
struct aa_label *label;
|
||||
struct aa_profile *profile;
|
||||
char *target;
|
||||
@@ -1779,27 +1846,24 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
|
||||
if (profile->rawdata) {
|
||||
if (aa_g_hash_policy) {
|
||||
dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir,
|
||||
profile->label.proxy, NULL, NULL,
|
||||
&rawdata_link_sha256_iops);
|
||||
&profile->label.proxy->count, NULL,
|
||||
NULL, &rawdata_link_sha256_iops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
aa_get_proxy(profile->label.proxy);
|
||||
profile->dents[AAFS_PROF_RAW_HASH] = dent;
|
||||
}
|
||||
dent = aafs_create("raw_abi", S_IFLNK | 0444, dir,
|
||||
profile->label.proxy, NULL, NULL,
|
||||
&profile->label.proxy->count, NULL, NULL,
|
||||
&rawdata_link_abi_iops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
aa_get_proxy(profile->label.proxy);
|
||||
profile->dents[AAFS_PROF_RAW_ABI] = dent;
|
||||
|
||||
dent = aafs_create("raw_data", S_IFLNK | 0444, dir,
|
||||
profile->label.proxy, NULL, NULL,
|
||||
&profile->label.proxy->count, NULL, NULL,
|
||||
&rawdata_link_data_iops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
aa_get_proxy(profile->label.proxy);
|
||||
profile->dents[AAFS_PROF_RAW_DATA] = dent;
|
||||
}
|
||||
#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
@@ -1830,13 +1894,13 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
|
||||
int error;
|
||||
|
||||
label = begin_current_label_crit_section();
|
||||
error = aa_may_manage_policy(current_cred(), label, NULL,
|
||||
error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
|
||||
AA_MAY_LOAD_POLICY);
|
||||
end_current_label_crit_section(label);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
parent = aa_get_ns(dir->i_private);
|
||||
parent = get_ns_common_ref(dir->i_private);
|
||||
AA_BUG(d_inode(ns_subns_dir(parent)) != dir);
|
||||
|
||||
/* we have to unlock and then relock to get locking order right
|
||||
@@ -1880,13 +1944,13 @@ static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
|
||||
int error;
|
||||
|
||||
label = begin_current_label_crit_section();
|
||||
error = aa_may_manage_policy(current_cred(), label, NULL,
|
||||
error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
|
||||
AA_MAY_LOAD_POLICY);
|
||||
end_current_label_crit_section(label);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
parent = aa_get_ns(dir->i_private);
|
||||
parent = get_ns_common_ref(dir->i_private);
|
||||
/* rmdir calls the generic securityfs functions to remove files
|
||||
* from the apparmor dir. It is up to the apparmor ns locking
|
||||
* to avoid races.
|
||||
@@ -1956,27 +2020,6 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
|
||||
|
||||
__aa_fs_list_remove_rawdata(ns);
|
||||
|
||||
if (ns_subns_dir(ns)) {
|
||||
sub = d_inode(ns_subns_dir(ns))->i_private;
|
||||
aa_put_ns(sub);
|
||||
}
|
||||
if (ns_subload(ns)) {
|
||||
sub = d_inode(ns_subload(ns))->i_private;
|
||||
aa_put_ns(sub);
|
||||
}
|
||||
if (ns_subreplace(ns)) {
|
||||
sub = d_inode(ns_subreplace(ns))->i_private;
|
||||
aa_put_ns(sub);
|
||||
}
|
||||
if (ns_subremove(ns)) {
|
||||
sub = d_inode(ns_subremove(ns))->i_private;
|
||||
aa_put_ns(sub);
|
||||
}
|
||||
if (ns_subrevision(ns)) {
|
||||
sub = d_inode(ns_subrevision(ns))->i_private;
|
||||
aa_put_ns(sub);
|
||||
}
|
||||
|
||||
for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) {
|
||||
aafs_remove(ns->dents[i]);
|
||||
ns->dents[i] = NULL;
|
||||
@@ -2001,40 +2044,40 @@ static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
|
||||
return PTR_ERR(dent);
|
||||
ns_subdata_dir(ns) = dent;
|
||||
|
||||
dent = aafs_create_file("revision", 0444, dir, ns,
|
||||
dent = aafs_create_file("revision", 0444, dir,
|
||||
&ns->unconfined->label.count,
|
||||
&aa_fs_ns_revision_fops);
|
||||
if (IS_ERR(dent))
|
||||
return PTR_ERR(dent);
|
||||
aa_get_ns(ns);
|
||||
ns_subrevision(ns) = dent;
|
||||
|
||||
dent = aafs_create_file(".load", 0640, dir, ns,
|
||||
&aa_fs_profile_load);
|
||||
dent = aafs_create_file(".load", 0640, dir,
|
||||
&ns->unconfined->label.count,
|
||||
&aa_fs_profile_load);
|
||||
if (IS_ERR(dent))
|
||||
return PTR_ERR(dent);
|
||||
aa_get_ns(ns);
|
||||
ns_subload(ns) = dent;
|
||||
|
||||
dent = aafs_create_file(".replace", 0640, dir, ns,
|
||||
&aa_fs_profile_replace);
|
||||
dent = aafs_create_file(".replace", 0640, dir,
|
||||
&ns->unconfined->label.count,
|
||||
&aa_fs_profile_replace);
|
||||
if (IS_ERR(dent))
|
||||
return PTR_ERR(dent);
|
||||
aa_get_ns(ns);
|
||||
ns_subreplace(ns) = dent;
|
||||
|
||||
dent = aafs_create_file(".remove", 0640, dir, ns,
|
||||
&aa_fs_profile_remove);
|
||||
dent = aafs_create_file(".remove", 0640, dir,
|
||||
&ns->unconfined->label.count,
|
||||
&aa_fs_profile_remove);
|
||||
if (IS_ERR(dent))
|
||||
return PTR_ERR(dent);
|
||||
aa_get_ns(ns);
|
||||
ns_subremove(ns) = dent;
|
||||
|
||||
/* use create_dentry so we can supply private data */
|
||||
dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL,
|
||||
&ns_dir_inode_operations);
|
||||
dent = aafs_create("namespaces", S_IFDIR | 0755, dir,
|
||||
&ns->unconfined->label.count,
|
||||
NULL, NULL, &ns_dir_inode_operations);
|
||||
if (IS_ERR(dent))
|
||||
return PTR_ERR(dent);
|
||||
aa_get_ns(ns);
|
||||
ns_subns_dir(ns) = dent;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -102,7 +102,7 @@ enum label_flags {
|
||||
|
||||
struct aa_label;
|
||||
struct aa_proxy {
|
||||
struct kref count;
|
||||
struct aa_common_ref count;
|
||||
struct aa_label __rcu *label;
|
||||
};
|
||||
|
||||
@@ -125,7 +125,7 @@ struct label_it {
|
||||
* vec: vector of profiles comprising the compound label
|
||||
*/
|
||||
struct aa_label {
|
||||
struct kref count;
|
||||
struct aa_common_ref count;
|
||||
struct rb_node node;
|
||||
struct rcu_head rcu;
|
||||
struct aa_proxy *proxy;
|
||||
@@ -357,7 +357,7 @@ int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
|
||||
*/
|
||||
static inline struct aa_label *__aa_get_label(struct aa_label *l)
|
||||
{
|
||||
if (l && kref_get_unless_zero(&l->count))
|
||||
if (l && kref_get_unless_zero(&l->count.count))
|
||||
return l;
|
||||
|
||||
return NULL;
|
||||
@@ -366,7 +366,7 @@ static inline struct aa_label *__aa_get_label(struct aa_label *l)
|
||||
static inline struct aa_label *aa_get_label(struct aa_label *l)
|
||||
{
|
||||
if (l)
|
||||
kref_get(&(l->count));
|
||||
kref_get(&(l->count.count));
|
||||
|
||||
return l;
|
||||
}
|
||||
@@ -386,7 +386,7 @@ static inline struct aa_label *aa_get_label_rcu(struct aa_label __rcu **l)
|
||||
rcu_read_lock();
|
||||
do {
|
||||
c = rcu_dereference(*l);
|
||||
} while (c && !kref_get_unless_zero(&c->count));
|
||||
} while (c && !kref_get_unless_zero(&c->count.count));
|
||||
rcu_read_unlock();
|
||||
|
||||
return c;
|
||||
@@ -426,7 +426,7 @@ static inline struct aa_label *aa_get_newest_label(struct aa_label *l)
|
||||
static inline void aa_put_label(struct aa_label *l)
|
||||
{
|
||||
if (l)
|
||||
kref_put(&l->count, aa_label_kref);
|
||||
kref_put(&l->count.count, aa_label_kref);
|
||||
}
|
||||
|
||||
/* wrapper fn to indicate semantics of the check */
|
||||
@@ -443,7 +443,7 @@ void aa_proxy_kref(struct kref *kref);
|
||||
static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy)
|
||||
{
|
||||
if (proxy)
|
||||
kref_get(&(proxy->count));
|
||||
kref_get(&(proxy->count.count));
|
||||
|
||||
return proxy;
|
||||
}
|
||||
@@ -451,7 +451,7 @@ static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy)
|
||||
static inline void aa_put_proxy(struct aa_proxy *proxy)
|
||||
{
|
||||
if (proxy)
|
||||
kref_put(&proxy->count, aa_proxy_kref);
|
||||
kref_put(&proxy->count.count, aa_proxy_kref);
|
||||
}
|
||||
|
||||
void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new);
|
||||
|
||||
@@ -102,6 +102,18 @@ void aa_info_message(const char *str);
|
||||
/* Security blob offsets */
|
||||
extern struct lsm_blob_sizes apparmor_blob_sizes;
|
||||
|
||||
enum reftype {
|
||||
REF_NS,
|
||||
REF_PROXY,
|
||||
REF_RAWDATA,
|
||||
};
|
||||
|
||||
/* common reference count used by data the shows up in aafs */
|
||||
struct aa_common_ref {
|
||||
struct kref count;
|
||||
enum reftype reftype;
|
||||
};
|
||||
|
||||
/**
|
||||
* aa_strneq - compare null terminated @str to a non null terminated substring
|
||||
* @str: a null terminated string
|
||||
|
||||
@@ -185,6 +185,7 @@ static inline void aa_put_dfa(struct aa_dfa *dfa)
|
||||
#define MATCH_FLAG_DIFF_ENCODE 0x80000000
|
||||
#define MARK_DIFF_ENCODE 0x40000000
|
||||
#define MATCH_FLAG_OOB_TRANSITION 0x20000000
|
||||
#define MARK_DIFF_ENCODE_VERIFIED 0x10000000
|
||||
#define MATCH_FLAGS_MASK 0xff000000
|
||||
#define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION)
|
||||
#define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID)
|
||||
|
||||
@@ -379,7 +379,7 @@ static inline bool profile_mediates_safe(struct aa_profile *profile,
|
||||
static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
|
||||
{
|
||||
if (p)
|
||||
kref_get(&(p->label.count));
|
||||
kref_get(&(p->label.count.count));
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -393,7 +393,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
|
||||
*/
|
||||
static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
|
||||
{
|
||||
if (p && kref_get_unless_zero(&p->label.count))
|
||||
if (p && kref_get_unless_zero(&p->label.count.count))
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
@@ -413,7 +413,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
|
||||
rcu_read_lock();
|
||||
do {
|
||||
c = rcu_dereference(*p);
|
||||
} while (c && !kref_get_unless_zero(&c->label.count));
|
||||
} while (c && !kref_get_unless_zero(&c->label.count.count));
|
||||
rcu_read_unlock();
|
||||
|
||||
return c;
|
||||
@@ -426,7 +426,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
|
||||
static inline void aa_put_profile(struct aa_profile *p)
|
||||
{
|
||||
if (p)
|
||||
kref_put(&p->label.count, aa_label_kref);
|
||||
kref_put(&p->label.count.count, aa_label_kref);
|
||||
}
|
||||
|
||||
static inline int AUDIT_MODE(struct aa_profile *profile)
|
||||
@@ -443,7 +443,7 @@ bool aa_policy_admin_capable(const struct cred *subj_cred,
|
||||
struct aa_label *label, struct aa_ns *ns);
|
||||
int aa_may_manage_policy(const struct cred *subj_cred,
|
||||
struct aa_label *label, struct aa_ns *ns,
|
||||
u32 mask);
|
||||
const struct cred *ocred, u32 mask);
|
||||
bool aa_current_policy_view_capable(struct aa_ns *ns);
|
||||
bool aa_current_policy_admin_capable(struct aa_ns *ns);
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "label.h"
|
||||
#include "policy.h"
|
||||
|
||||
/* Match max depth of user namespaces */
|
||||
#define MAX_NS_DEPTH 32
|
||||
|
||||
/* struct aa_ns_acct - accounting of profiles in namespace
|
||||
* @max_size: maximum space allowed for all profiles in namespace
|
||||
|
||||
@@ -87,17 +87,29 @@ struct aa_ext {
|
||||
u32 version;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct aa_loaddata - buffer of policy raw_data set
|
||||
/* struct aa_loaddata - buffer of policy raw_data set
|
||||
* @count: inode/filesystem refcount - use aa_get_i_loaddata()
|
||||
* @pcount: profile refcount - use aa_get_profile_loaddata()
|
||||
* @list: list the loaddata is on
|
||||
* @work: used to do a delayed cleanup
|
||||
* @dents: refs to dents created in aafs
|
||||
* @ns: the namespace this loaddata was loaded into
|
||||
* @name:
|
||||
* @size: the size of the data that was loaded
|
||||
* @compressed_size: the size of the data when it is compressed
|
||||
* @revision: unique revision count that this data was loaded as
|
||||
* @abi: the abi number the loaddata uses
|
||||
* @hash: a hash of the loaddata, used to help dedup data
|
||||
*
|
||||
* there is no loaddata ref for being on ns list, nor a ref from
|
||||
* d_inode(@dentry) when grab a ref from these, @ns->lock must be held
|
||||
* && __aa_get_loaddata() needs to be used, and the return value
|
||||
* checked, if NULL the loaddata is already being reaped and should be
|
||||
* considered dead.
|
||||
* There is no loaddata ref for being on ns->rawdata_list, so
|
||||
* @ns->lock must be held when walking the list. Dentries and
|
||||
* inode opens hold refs on @count; profiles hold refs on @pcount.
|
||||
* When the last @pcount drops, do_ploaddata_rmfs() removes the
|
||||
* fs entries and drops the associated @count ref.
|
||||
*/
|
||||
struct aa_loaddata {
|
||||
struct kref count;
|
||||
struct aa_common_ref count;
|
||||
struct kref pcount;
|
||||
struct list_head list;
|
||||
struct work_struct work;
|
||||
struct dentry *dents[AAFS_LOADDATA_NDENTS];
|
||||
@@ -118,51 +130,54 @@ struct aa_loaddata {
|
||||
|
||||
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
|
||||
|
||||
/**
|
||||
* __aa_get_loaddata - get a reference count to uncounted data reference
|
||||
* @data: reference to get a count on
|
||||
*
|
||||
* Returns: pointer to reference OR NULL if race is lost and reference is
|
||||
* being repeated.
|
||||
* Requires: @data->ns->lock held, and the return code MUST be checked
|
||||
*
|
||||
* Use only from inode->i_private and @data->list found references
|
||||
*/
|
||||
static inline struct aa_loaddata *
|
||||
__aa_get_loaddata(struct aa_loaddata *data)
|
||||
{
|
||||
if (data && kref_get_unless_zero(&(data->count)))
|
||||
return data;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_get_loaddata - get a reference count from a counted data reference
|
||||
* @data: reference to get a count on
|
||||
*
|
||||
* Returns: point to reference
|
||||
* Returns: pointer to reference
|
||||
* Requires: @data to have a valid reference count on it. It is a bug
|
||||
* if the race to reap can be encountered when it is used.
|
||||
*/
|
||||
static inline struct aa_loaddata *
|
||||
aa_get_loaddata(struct aa_loaddata *data)
|
||||
aa_get_i_loaddata(struct aa_loaddata *data)
|
||||
{
|
||||
struct aa_loaddata *tmp = __aa_get_loaddata(data);
|
||||
|
||||
AA_BUG(data && !tmp);
|
||||
if (data)
|
||||
kref_get(&(data->count.count));
|
||||
return data;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
||||
/**
|
||||
* aa_get_profile_loaddata - get a profile reference count on loaddata
|
||||
* @data: reference to get a count on
|
||||
*
|
||||
* Returns: pointer to reference
|
||||
* Requires: @data to have a valid reference count on it.
|
||||
*/
|
||||
static inline struct aa_loaddata *
|
||||
aa_get_profile_loaddata(struct aa_loaddata *data)
|
||||
{
|
||||
if (data)
|
||||
kref_get(&(data->pcount));
|
||||
return data;
|
||||
}
|
||||
|
||||
void __aa_loaddata_update(struct aa_loaddata *data, long revision);
|
||||
bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r);
|
||||
void aa_loaddata_kref(struct kref *kref);
|
||||
void aa_ploaddata_kref(struct kref *kref);
|
||||
struct aa_loaddata *aa_loaddata_alloc(size_t size);
|
||||
static inline void aa_put_loaddata(struct aa_loaddata *data)
|
||||
static inline void aa_put_i_loaddata(struct aa_loaddata *data)
|
||||
{
|
||||
if (data)
|
||||
kref_put(&data->count, aa_loaddata_kref);
|
||||
kref_put(&data->count.count, aa_loaddata_kref);
|
||||
}
|
||||
|
||||
static inline void aa_put_profile_loaddata(struct aa_loaddata *data)
|
||||
{
|
||||
if (data)
|
||||
kref_put(&data->pcount, aa_ploaddata_kref);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
|
||||
@@ -52,7 +52,8 @@ static void free_proxy(struct aa_proxy *proxy)
|
||||
|
||||
void aa_proxy_kref(struct kref *kref)
|
||||
{
|
||||
struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count);
|
||||
struct aa_proxy *proxy = container_of(kref, struct aa_proxy,
|
||||
count.count);
|
||||
|
||||
free_proxy(proxy);
|
||||
}
|
||||
@@ -63,7 +64,8 @@ struct aa_proxy *aa_alloc_proxy(struct aa_label *label, gfp_t gfp)
|
||||
|
||||
new = kzalloc_obj(struct aa_proxy, gfp);
|
||||
if (new) {
|
||||
kref_init(&new->count);
|
||||
kref_init(&new->count.count);
|
||||
new->count.reftype = REF_PROXY;
|
||||
rcu_assign_pointer(new->label, aa_get_label(label));
|
||||
}
|
||||
return new;
|
||||
@@ -375,7 +377,8 @@ static void label_free_rcu(struct rcu_head *head)
|
||||
|
||||
void aa_label_kref(struct kref *kref)
|
||||
{
|
||||
struct aa_label *label = container_of(kref, struct aa_label, count);
|
||||
struct aa_label *label = container_of(kref, struct aa_label,
|
||||
count.count);
|
||||
struct aa_ns *ns = labels_ns(label);
|
||||
|
||||
if (!ns) {
|
||||
@@ -412,7 +415,8 @@ bool aa_label_init(struct aa_label *label, int size, gfp_t gfp)
|
||||
|
||||
label->size = size; /* doesn't include null */
|
||||
label->vec[size] = NULL; /* null terminate */
|
||||
kref_init(&label->count);
|
||||
kref_init(&label->count.count);
|
||||
label->count.reftype = REF_NS; /* for aafs purposes */
|
||||
RB_CLEAR_NODE(&label->node);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -160,9 +160,10 @@ static int verify_dfa(struct aa_dfa *dfa)
|
||||
if (state_count == 0)
|
||||
goto out;
|
||||
for (i = 0; i < state_count; i++) {
|
||||
if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) &&
|
||||
(DEFAULT_TABLE(dfa)[i] >= state_count))
|
||||
if (DEFAULT_TABLE(dfa)[i] >= state_count) {
|
||||
pr_err("AppArmor DFA default state out of bounds");
|
||||
goto out;
|
||||
}
|
||||
if (BASE_TABLE(dfa)[i] & MATCH_FLAGS_INVALID) {
|
||||
pr_err("AppArmor DFA state with invalid match flags");
|
||||
goto out;
|
||||
@@ -201,16 +202,31 @@ static int verify_dfa(struct aa_dfa *dfa)
|
||||
size_t j, k;
|
||||
|
||||
for (j = i;
|
||||
(BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) &&
|
||||
!(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE);
|
||||
((BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) &&
|
||||
!(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE_VERIFIED));
|
||||
j = k) {
|
||||
if (BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE)
|
||||
/* loop in current chain */
|
||||
goto out;
|
||||
k = DEFAULT_TABLE(dfa)[j];
|
||||
if (j == k)
|
||||
/* self loop */
|
||||
goto out;
|
||||
if (k < j)
|
||||
break; /* already verified */
|
||||
BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE;
|
||||
}
|
||||
/* move mark to verified */
|
||||
for (j = i;
|
||||
(BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE);
|
||||
j = k) {
|
||||
k = DEFAULT_TABLE(dfa)[j];
|
||||
if (j < i)
|
||||
/* jumps to state/chain that has been
|
||||
* verified
|
||||
*/
|
||||
break;
|
||||
BASE_TABLE(dfa)[j] &= ~MARK_DIFF_ENCODE;
|
||||
BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE_VERIFIED;
|
||||
}
|
||||
}
|
||||
error = 0;
|
||||
|
||||
@@ -463,13 +479,18 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
/* Equivalence class table defined */
|
||||
u8 *equiv = EQUIV_TABLE(dfa);
|
||||
for (; len; len--)
|
||||
match_char(state, def, base, next, check,
|
||||
equiv[(u8) *str++]);
|
||||
for (; len; len--) {
|
||||
u8 c = equiv[(u8) *str];
|
||||
|
||||
match_char(state, def, base, next, check, c);
|
||||
str++;
|
||||
}
|
||||
} else {
|
||||
/* default is direct to next state */
|
||||
for (; len; len--)
|
||||
match_char(state, def, base, next, check, (u8) *str++);
|
||||
for (; len; len--) {
|
||||
match_char(state, def, base, next, check, (u8) *str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
@@ -503,13 +524,18 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
|
||||
/* Equivalence class table defined */
|
||||
u8 *equiv = EQUIV_TABLE(dfa);
|
||||
/* default is direct to next state */
|
||||
while (*str)
|
||||
match_char(state, def, base, next, check,
|
||||
equiv[(u8) *str++]);
|
||||
while (*str) {
|
||||
u8 c = equiv[(u8) *str];
|
||||
|
||||
match_char(state, def, base, next, check, c);
|
||||
str++;
|
||||
}
|
||||
} else {
|
||||
/* default is direct to next state */
|
||||
while (*str)
|
||||
match_char(state, def, base, next, check, (u8) *str++);
|
||||
while (*str) {
|
||||
match_char(state, def, base, next, check, (u8) *str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -191,19 +191,43 @@ static void __list_remove_profile(struct aa_profile *profile)
|
||||
}
|
||||
|
||||
/**
|
||||
* __remove_profile - remove old profile, and children
|
||||
* @profile: profile to be replaced (NOT NULL)
|
||||
* __remove_profile - remove profile, and children
|
||||
* @profile: profile to be removed (NOT NULL)
|
||||
*
|
||||
* Requires: namespace list lock be held, or list not be shared
|
||||
*/
|
||||
static void __remove_profile(struct aa_profile *profile)
|
||||
{
|
||||
struct aa_profile *curr, *to_remove;
|
||||
|
||||
AA_BUG(!profile);
|
||||
AA_BUG(!profile->ns);
|
||||
AA_BUG(!mutex_is_locked(&profile->ns->lock));
|
||||
|
||||
/* release any children lists first */
|
||||
__aa_profile_list_release(&profile->base.profiles);
|
||||
if (!list_empty(&profile->base.profiles)) {
|
||||
curr = list_first_entry(&profile->base.profiles, struct aa_profile, base.list);
|
||||
|
||||
while (curr != profile) {
|
||||
|
||||
while (!list_empty(&curr->base.profiles))
|
||||
curr = list_first_entry(&curr->base.profiles,
|
||||
struct aa_profile, base.list);
|
||||
|
||||
to_remove = curr;
|
||||
if (!list_is_last(&to_remove->base.list,
|
||||
&aa_deref_parent(curr)->base.profiles))
|
||||
curr = list_next_entry(to_remove, base.list);
|
||||
else
|
||||
curr = aa_deref_parent(curr);
|
||||
|
||||
/* released by free_profile */
|
||||
aa_label_remove(&to_remove->label);
|
||||
__aafs_profile_rmdir(to_remove);
|
||||
__list_remove_profile(to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
/* released by free_profile */
|
||||
aa_label_remove(&profile->label);
|
||||
__aafs_profile_rmdir(profile);
|
||||
@@ -326,7 +350,7 @@ void aa_free_profile(struct aa_profile *profile)
|
||||
}
|
||||
|
||||
kfree_sensitive(profile->hash);
|
||||
aa_put_loaddata(profile->rawdata);
|
||||
aa_put_profile_loaddata(profile->rawdata);
|
||||
aa_label_destroy(&profile->label);
|
||||
|
||||
kfree_sensitive(profile);
|
||||
@@ -918,17 +942,44 @@ bool aa_current_policy_admin_capable(struct aa_ns *ns)
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool is_subset_of_obj_privilege(const struct cred *cred,
|
||||
struct aa_label *label,
|
||||
const struct cred *ocred)
|
||||
{
|
||||
if (cred == ocred)
|
||||
return true;
|
||||
|
||||
if (!aa_label_is_subset(label, cred_label(ocred)))
|
||||
return false;
|
||||
/* don't allow crossing userns for now */
|
||||
if (cred->user_ns != ocred->user_ns)
|
||||
return false;
|
||||
if (!cap_issubset(cred->cap_inheritable, ocred->cap_inheritable))
|
||||
return false;
|
||||
if (!cap_issubset(cred->cap_permitted, ocred->cap_permitted))
|
||||
return false;
|
||||
if (!cap_issubset(cred->cap_effective, ocred->cap_effective))
|
||||
return false;
|
||||
if (!cap_issubset(cred->cap_bset, ocred->cap_bset))
|
||||
return false;
|
||||
if (!cap_issubset(cred->cap_ambient, ocred->cap_ambient))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* aa_may_manage_policy - can the current task manage policy
|
||||
* @subj_cred: subjects cred
|
||||
* @label: label to check if it can manage policy
|
||||
* @ns: namespace being managed by @label (may be NULL if @label's ns)
|
||||
* @ocred: object cred if request is coming from an open object
|
||||
* @mask: contains the policy manipulation operation being done
|
||||
*
|
||||
* Returns: 0 if the task is allowed to manipulate policy else error
|
||||
*/
|
||||
int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label,
|
||||
struct aa_ns *ns, u32 mask)
|
||||
struct aa_ns *ns, const struct cred *ocred, u32 mask)
|
||||
{
|
||||
const char *op;
|
||||
|
||||
@@ -944,6 +995,11 @@ int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label,
|
||||
return audit_policy(label, op, NULL, NULL, "policy_locked",
|
||||
-EACCES);
|
||||
|
||||
if (ocred && !is_subset_of_obj_privilege(subj_cred, label, ocred))
|
||||
return audit_policy(label, op, NULL, NULL,
|
||||
"not privileged for target profile",
|
||||
-EACCES);
|
||||
|
||||
if (!aa_policy_admin_capable(subj_cred, label, ns))
|
||||
return audit_policy(label, op, NULL, NULL, "not policy admin",
|
||||
-EACCES);
|
||||
@@ -1115,7 +1171,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
LIST_HEAD(lh);
|
||||
|
||||
op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD;
|
||||
aa_get_loaddata(udata);
|
||||
aa_get_profile_loaddata(udata);
|
||||
/* released below */
|
||||
error = aa_unpack(udata, &lh, &ns_name);
|
||||
if (error)
|
||||
@@ -1142,6 +1198,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
goto fail;
|
||||
}
|
||||
ns_name = ent->ns_name;
|
||||
ent->ns_name = NULL;
|
||||
} else
|
||||
count++;
|
||||
}
|
||||
@@ -1166,10 +1223,10 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
if (aa_rawdata_eq(rawdata_ent, udata)) {
|
||||
struct aa_loaddata *tmp;
|
||||
|
||||
tmp = __aa_get_loaddata(rawdata_ent);
|
||||
tmp = aa_get_profile_loaddata(rawdata_ent);
|
||||
/* check we didn't fail the race */
|
||||
if (tmp) {
|
||||
aa_put_loaddata(udata);
|
||||
aa_put_profile_loaddata(udata);
|
||||
udata = tmp;
|
||||
break;
|
||||
}
|
||||
@@ -1182,7 +1239,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
struct aa_profile *p;
|
||||
|
||||
if (aa_g_export_binary)
|
||||
ent->new->rawdata = aa_get_loaddata(udata);
|
||||
ent->new->rawdata = aa_get_profile_loaddata(udata);
|
||||
error = __lookup_replace(ns, ent->new->base.hname,
|
||||
!(mask & AA_MAY_REPLACE_POLICY),
|
||||
&ent->old, &info);
|
||||
@@ -1315,7 +1372,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
|
||||
out:
|
||||
aa_put_ns(ns);
|
||||
aa_put_loaddata(udata);
|
||||
aa_put_profile_loaddata(udata);
|
||||
kfree(ns_name);
|
||||
|
||||
if (error)
|
||||
|
||||
@@ -223,6 +223,8 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name,
|
||||
AA_BUG(!name);
|
||||
AA_BUG(!mutex_is_locked(&parent->lock));
|
||||
|
||||
if (parent->level > MAX_NS_DEPTH)
|
||||
return ERR_PTR(-ENOSPC);
|
||||
ns = alloc_ns(parent->base.hname, name);
|
||||
if (!ns)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -109,22 +109,8 @@ bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r)
|
||||
return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* need to take the ns mutex lock which is NOT safe most places that
|
||||
* put_loaddata is called, so we have to delay freeing it
|
||||
*/
|
||||
static void do_loaddata_free(struct work_struct *work)
|
||||
static void do_loaddata_free(struct aa_loaddata *d)
|
||||
{
|
||||
struct aa_loaddata *d = container_of(work, struct aa_loaddata, work);
|
||||
struct aa_ns *ns = aa_get_ns(d->ns);
|
||||
|
||||
if (ns) {
|
||||
mutex_lock_nested(&ns->lock, ns->level);
|
||||
__aa_fs_remove_rawdata(d);
|
||||
mutex_unlock(&ns->lock);
|
||||
aa_put_ns(ns);
|
||||
}
|
||||
|
||||
kfree_sensitive(d->hash);
|
||||
kfree_sensitive(d->name);
|
||||
kvfree(d->data);
|
||||
@@ -133,10 +119,38 @@ static void do_loaddata_free(struct work_struct *work)
|
||||
|
||||
void aa_loaddata_kref(struct kref *kref)
|
||||
{
|
||||
struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count);
|
||||
struct aa_loaddata *d = container_of(kref, struct aa_loaddata,
|
||||
count.count);
|
||||
|
||||
do_loaddata_free(d);
|
||||
}
|
||||
|
||||
/*
|
||||
* need to take the ns mutex lock which is NOT safe most places that
|
||||
* put_loaddata is called, so we have to delay freeing it
|
||||
*/
|
||||
static void do_ploaddata_rmfs(struct work_struct *work)
|
||||
{
|
||||
struct aa_loaddata *d = container_of(work, struct aa_loaddata, work);
|
||||
struct aa_ns *ns = aa_get_ns(d->ns);
|
||||
|
||||
if (ns) {
|
||||
mutex_lock_nested(&ns->lock, ns->level);
|
||||
/* remove fs ref to loaddata */
|
||||
__aa_fs_remove_rawdata(d);
|
||||
mutex_unlock(&ns->lock);
|
||||
aa_put_ns(ns);
|
||||
}
|
||||
/* called by dropping last pcount, so drop its associated icount */
|
||||
aa_put_i_loaddata(d);
|
||||
}
|
||||
|
||||
void aa_ploaddata_kref(struct kref *kref)
|
||||
{
|
||||
struct aa_loaddata *d = container_of(kref, struct aa_loaddata, pcount);
|
||||
|
||||
if (d) {
|
||||
INIT_WORK(&d->work, do_loaddata_free);
|
||||
INIT_WORK(&d->work, do_ploaddata_rmfs);
|
||||
schedule_work(&d->work);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +167,9 @@ struct aa_loaddata *aa_loaddata_alloc(size_t size)
|
||||
kfree(d);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
kref_init(&d->count);
|
||||
kref_init(&d->count.count);
|
||||
d->count.reftype = REF_RAWDATA;
|
||||
kref_init(&d->pcount);
|
||||
INIT_LIST_HEAD(&d->list);
|
||||
|
||||
return d;
|
||||
@@ -1010,7 +1026,17 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
|
||||
if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) {
|
||||
/* default start state for xmatch and file dfa */
|
||||
pdb->start[AA_CLASS_FILE] = DFA_START;
|
||||
} /* setup class index */
|
||||
}
|
||||
|
||||
size_t state_count = pdb->dfa->tables[YYTD_ID_BASE]->td_lolen;
|
||||
|
||||
if (pdb->start[0] >= state_count ||
|
||||
pdb->start[AA_CLASS_FILE] >= state_count) {
|
||||
*info = "invalid dfa start state";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* setup class index */
|
||||
for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
|
||||
pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
|
||||
i);
|
||||
@@ -1409,7 +1435,6 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
|
||||
{
|
||||
int error = -EPROTONOSUPPORT;
|
||||
const char *name = NULL;
|
||||
*ns = NULL;
|
||||
|
||||
/* get the interface version */
|
||||
if (!aa_unpack_u32(e, &e->version, "version")) {
|
||||
|
||||
Reference in New Issue
Block a user