summaryrefslogtreecommitdiff
path: root/security/apparmor/apparmorfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor/apparmorfs.c')
-rw-r--r--security/apparmor/apparmorfs.c655
1 files changed, 444 insertions, 211 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 2f84bd23edb6..5b42140e12e8 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -9,6 +9,7 @@
*/
#include <linux/ctype.h>
+#include <linux/slab.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
@@ -22,6 +23,7 @@
#include <linux/fs_context.h>
#include <linux/poll.h>
#include <linux/zstd.h>
+#include <linux/string.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>
@@ -32,6 +34,8 @@
#include "include/crypto.h"
#include "include/ipc.h"
#include "include/label.h"
+#include "include/net.h"
+#include "include/lib.h"
#include "include/policy.h"
#include "include/policy_ns.h"
#include "include/resource.h"
@@ -62,6 +66,7 @@
* securityfs and apparmorfs filesystems.
*/
+#define IREF_POISON 101
/*
* support fns
@@ -69,17 +74,17 @@
struct rawdata_f_data {
struct aa_loaddata *loaddata;
+ DECLARE_FLEX_ARRAY(char, data);
};
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
-#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
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);
}
@@ -149,10 +154,77 @@ static int aafs_count;
static int aafs_show_path(struct seq_file *seq, struct dentry *dentry)
{
- seq_printf(seq, "%s:[%lu]", AAFS_NAME, d_inode(dentry)->i_ino);
+ seq_printf(seq, "%s:[%llu]", AAFS_NAME, d_inode(dentry)->i_ino);
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;
+}
+
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
+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;
+}
+#endif
+
+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 +234,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 +335,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)
{
@@ -282,32 +356,24 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
dir = d_inode(parent);
- inode_lock(dir);
- dentry = lookup_noperm(&QSTR(name), parent);
+ dentry = simple_start_creating(parent, name);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
- goto fail_lock;
- }
-
- if (d_really_is_positive(dentry)) {
- error = -EEXIST;
- goto fail_dentry;
+ goto fail;
}
error = __aafs_setup_d_inode(dir, dentry, mode, data, link, fops, iops);
+ simple_done_creating(dentry);
if (error)
- goto fail_dentry;
- inode_unlock(dir);
+ goto fail;
- return dentry;
+ if (data)
+ aa_get_common_ref(data);
-fail_dentry:
- dput(dentry);
+ return dentry;
-fail_lock:
- inode_unlock(dir);
+fail:
simple_release_fs(&aafs_mnt, &aafs_count);
-
return ERR_PTR(error);
}
@@ -323,7 +389,8 @@ fail_lock:
* 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,37 +476,209 @@ 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);
}
return data;
}
+#if defined(CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY) || \
+ defined(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY)
+static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
+{
+ if (slen < dlen) {
+ const size_t wksp_len = zstd_dctx_workspace_bound();
+ zstd_dctx *ctx;
+ void *wksp;
+ size_t out_len;
+ int ret = 0;
+
+ wksp = kvzalloc(wksp_len, GFP_KERNEL);
+ if (!wksp) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ ctx = zstd_init_dctx(wksp, wksp_len);
+ if (ctx == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
+ if (zstd_is_error(out_len)) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+cleanup:
+ kvfree(wksp);
+ return ret;
+ }
+
+ if (dlen < slen)
+ return -EINVAL;
+ memcpy(dst, src, slen);
+ return 0;
+}
+#endif
+
+
+#ifdef CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY
+/**
+ * aa_get_data_from_compressed - common routine for getting compressed policy
+ * from user and get both compressed and uncompressed version.
+ * @userbuf: user buffer to copy data from (NOT NULL)
+ * @buffer_size: size of user buffer
+ * @pos: position write is at in the file (NOT NULL)
+ * @compressed_data: Ptr on compressed data. *compressed_data is allocated there
+ *
+ * Returns: kernel buffer containing copy of user buffer data or an
+ * ERR_PTR on failure.
+ */
+static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbuf,
+ size_t buffer_size,
+ loff_t *pos,
+ char **compressed_data)
+{
+ struct aa_loaddata *data;
+ zstd_frame_header header;
+ int error;
+
+ if (!userbuf || !pos)
+ return ERR_PTR(-EINVAL);
+ if (*pos)
+ return ERR_PTR(-ESPIPE);
+
+ *compressed_data = kvmalloc(buffer_size, GFP_KERNEL);
+ if (!*compressed_data)
+ return ERR_PTR(-ENOMEM);
+ if (copy_from_user(*compressed_data, userbuf, buffer_size)) {
+ error = -EFAULT;
+ goto fail;
+ }
+ error = zstd_get_frame_header(&header, *compressed_data, buffer_size);
+ if (error || header.frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN ||
+ header.frameContentSize == ZSTD_CONTENTSIZE_ERROR) {
+ error = -EINVAL;
+ goto fail;
+ }
+
+ data = aa_loaddata_alloc(header.frameContentSize);
+ if (IS_ERR(data)) {
+ error = PTR_ERR(data);
+ goto fail;
+ }
+
+ // We then decompress the data
+ error = decompress_zstd(*compressed_data, buffer_size, data->data,
+ header.frameContentSize);
+ if (error)
+ goto fail_decompress;
+
+ data->size = header.frameContentSize;
+ return data;
+
+fail_decompress:
+ aa_put_i_loaddata(data);
+fail:
+ kvfree(*compressed_data);
+ return ERR_PTR(error);
+
+}
+#else
+static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbuf __always_unused,
+ size_t buffer_size __always_unused,
+ loff_t *pos __always_unused,
+ char **compressed_data __always_unused)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif /* CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY */
+
+struct aa_user_hdr {
+ uint8_t version;
+ uint8_t compress_level;
+ uint8_t padding[6]; /* force 8-byte alignment */
+} __packed __aligned(8);
+
+#define aa_hdr_magic "\x04\x08\x00\x76\x65\x72\x73\x69\x6f\x6e\x00\x02"
+#define aa_hdr_magic_size 12
+
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;
ssize_t error;
+ char *compressed_data = NULL;
+ __le32 magic_le;
+ bool is_compressed;
+ u8 aahdr[aa_hdr_magic_size];
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
/* 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;
- data = aa_simple_write_to_buffer(buf, size, size, pos);
- error = PTR_ERR(data);
+ /* If the policy is userspace compressed we start by decompressing it
+ * to make the required checks (computing hash, verifying profile, ...)
+ *
+ * Getting a userspace-compressed version then decompressing it in the
+ * kernel actually makes sense since zstd decompression is ~3.5x faster
+ * than compression. This also allow to increase the compression level.
+ */
+
+ if (size >= sizeof(__le32) &&
+ !copy_from_user(&magic_le, buf, sizeof(magic_le)) &&
+ le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) {
+ is_compressed = true;
+ } else if (size >= sizeof(struct aa_user_hdr) + sizeof(__le32) &&
+ !copy_from_user(&magic_le,
+ buf + sizeof(struct aa_user_hdr),
+ sizeof(magic_le)) &&
+ le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) {
+ is_compressed = true;
+ /* skip the userspace header if present */
+ buf += sizeof(struct aa_user_hdr);
+ size -= sizeof(struct aa_user_hdr);
+ } else if (size >= sizeof(struct aa_user_hdr) +
+ aa_hdr_magic_size &&
+ !copy_from_user(&aahdr,
+ buf + sizeof(struct aa_user_hdr),
+ aa_hdr_magic_size) &&
+ memcmp(&aahdr, aa_hdr_magic, aa_hdr_magic_size) == 0) {
+ /* uncompressed blob with user hdr */
+ buf += sizeof(struct aa_user_hdr);
+ size -= sizeof(struct aa_user_hdr);
+ is_compressed = false;
+ } else {
+ is_compressed = false;
+ }
+ if (is_compressed) {
+
+ data = aa_get_data_from_compressed(buf, size, pos, &compressed_data);
+ error = PTR_ERR(data);
+ } else {
+ data = aa_simple_write_to_buffer(buf, size, size, pos);
+ error = PTR_ERR(data);
+ }
+
if (!IS_ERR(data)) {
- error = aa_replace_profiles(ns, label, mask, data);
- aa_put_loaddata(data);
+ error = aa_replace_profiles(ns, label, mask, data,
+ compressed_data, size);
+ /* 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);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -448,8 +687,9 @@ end_section:
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 +705,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;
@@ -478,6 +718,7 @@ static const struct file_operations aa_fs_profile_replace = {
.llseek = default_llseek,
};
+
/* .remove file hook fn to remove loaded policy */
static ssize_t profile_remove(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
@@ -485,14 +726,15 @@ 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);
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
/* 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,10 +748,10 @@ 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);
+ end_current_label_crit_section(label, needput);
aa_put_ns(ns);
return error;
}
@@ -575,7 +817,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;
@@ -614,7 +856,8 @@ static const struct file_operations aa_fs_ns_revision_fops = {
.release = ns_revision_release,
};
-static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
+static void profile_query_cb(const struct aa_profile *profile,
+ struct aa_perms *perms,
const char *match_str, size_t match_len)
{
struct aa_ruleset *rules = profile->label.rules[0];
@@ -684,6 +927,7 @@ static ssize_t query_data(char *buf, size_t buf_len,
struct aa_data *data;
u32 bytes, blocks;
__le32 outle32;
+ bool needput;
if (!query_len)
return -EINVAL; /* need a query */
@@ -697,9 +941,9 @@ static ssize_t query_data(char *buf, size_t buf_len,
if (buf_len < sizeof(bytes) + sizeof(blocks))
return -EINVAL; /* not enough space */
- curr = begin_current_label_crit_section();
+ curr = begin_current_label_crit_section(&needput);
label = aa_label_parse(curr, query, GFP_KERNEL, false, false);
- end_current_label_crit_section(curr);
+ end_current_label_crit_section(curr, needput);
if (IS_ERR(label))
return PTR_ERR(label);
@@ -775,6 +1019,7 @@ static ssize_t query_label(char *buf, size_t buf_len,
size_t label_name_len, match_len;
struct aa_perms perms;
struct label_it i;
+ bool needput;
if (!query_len)
return -EINVAL;
@@ -793,9 +1038,9 @@ static ssize_t query_label(char *buf, size_t buf_len,
match_str = label_name + label_name_len + 1;
match_len = query_len - label_name_len - 1;
- curr = begin_current_label_crit_section();
+ curr = begin_current_label_crit_section(&needput);
label = aa_label_parse(curr, label_name, GFP_KERNEL, false, false);
- end_current_label_crit_section(curr);
+ end_current_label_crit_section(curr, needput);
if (IS_ERR(label))
return PTR_ERR(label);
@@ -836,7 +1081,7 @@ static void multi_transaction_kref(struct kref *kref)
struct multi_transaction *t;
t = container_of(kref, struct multi_transaction, count);
- free_page((unsigned long) t);
+ kfree(t);
}
static struct multi_transaction *
@@ -879,7 +1124,7 @@ static struct multi_transaction *multi_transaction_new(struct file *file,
if (size > MULTI_TRANSACTION_LIMIT - 1)
return ERR_PTR(-EFBIG);
- t = (struct multi_transaction *)get_zeroed_page(GFP_KERNEL);
+ t = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!t)
return ERR_PTR(-ENOMEM);
kref_init(&t->count);
@@ -1061,7 +1306,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) {
@@ -1163,10 +1408,11 @@ static const struct file_operations seq_ns_ ##NAME ##_fops = { \
static int seq_ns_stacked_show(struct seq_file *seq, void *v)
{
struct aa_label *label;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
seq_printf(seq, "%s\n", str_yes_no(label->size > 1));
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
@@ -1177,8 +1423,9 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
struct aa_profile *profile;
struct label_it it;
int count = 1;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (label->size > 1) {
label_for_each(it, label, profile)
@@ -1189,7 +1436,7 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
}
seq_printf(seq, "%s\n", str_yes_no(count > 1));
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
@@ -1197,23 +1444,32 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
static int seq_ns_level_show(struct seq_file *seq, void *v)
{
struct aa_label *label;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
seq_printf(seq, "%d\n", labels_ns(label)->level);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
static int seq_ns_name_show(struct seq_file *seq, void *v)
{
- struct aa_label *label = begin_current_label_crit_section();
+ bool needput;
+ struct aa_label *label = begin_current_label_crit_section(&needput);
+
seq_printf(seq, "%s\n", labels_ns(label)->base.name);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
+SEQ_NS_FOPS(stacked);
+SEQ_NS_FOPS(nsstacked);
+SEQ_NS_FOPS(level);
+SEQ_NS_FOPS(name);
+
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
static int seq_ns_compress_min_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "%d\n", AA_MIN_CLEVEL);
@@ -1226,16 +1482,11 @@ static int seq_ns_compress_max_show(struct seq_file *seq, void *v)
return 0;
}
-SEQ_NS_FOPS(stacked);
-SEQ_NS_FOPS(nsstacked);
-SEQ_NS_FOPS(level);
-SEQ_NS_FOPS(name);
SEQ_NS_FOPS(compress_min);
SEQ_NS_FOPS(compress_max);
/* policy/raw_data/ * file ops */
-#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define SEQ_RAWDATA_FOPS(NAME) \
static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
{ \
@@ -1253,18 +1504,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 +1525,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);
}
@@ -1326,48 +1576,13 @@ SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size);
-static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
-{
- if (slen < dlen) {
- const size_t wksp_len = zstd_dctx_workspace_bound();
- zstd_dctx *ctx;
- void *wksp;
- size_t out_len;
- int ret = 0;
-
- wksp = kvzalloc(wksp_len, GFP_KERNEL);
- if (!wksp) {
- ret = -ENOMEM;
- goto cleanup;
- }
- ctx = zstd_init_dctx(wksp, wksp_len);
- if (ctx == NULL) {
- ret = -ENOMEM;
- goto cleanup;
- }
- out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
- if (zstd_is_error(out_len)) {
- ret = -EINVAL;
- goto cleanup;
- }
-cleanup:
- kvfree(wksp);
- return ret;
- }
-
- if (dlen < slen)
- return -EINVAL;
- memcpy(dst, src, slen);
- return 0;
-}
-
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
struct rawdata_f_data *private = file->private_data;
return simple_read_from_buffer(buf, size, ppos,
- RAWDATA_F_DATA_BUF(private),
+ private->data,
private->loaddata->size);
}
@@ -1387,9 +1602,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);
@@ -1401,8 +1615,7 @@ static int rawdata_open(struct inode *inode, struct file *file)
private->loaddata = loaddata;
error = decompress_zstd(loaddata->data, loaddata->compressed_size,
- RAWDATA_F_DATA_BUF(private),
- loaddata->size);
+ private->data, loaddata->size);
if (error)
goto fail_decompress;
@@ -1414,7 +1627,7 @@ fail_decompress:
return error;
fail_private_alloc:
- aa_put_loaddata(loaddata);
+ aa_put_i_loaddata(loaddata);
return error;
}
@@ -1431,7 +1644,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 +1686,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 +1724,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 +1752,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 +1789,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 +1839,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;
@@ -1700,6 +1903,80 @@ static const struct inode_operations rawdata_link_abi_iops = {
static const struct inode_operations rawdata_link_data_iops = {
.get_link = rawdata_get_link_data,
};
+
+/*
+ * Requires: @profile->ns->lock held
+ */
+void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile)
+{
+ aafs_remove(profile->dents[AAFS_PROF_RAW_HASH]);
+ profile->dents[AAFS_PROF_RAW_HASH] = NULL;
+ aafs_remove(profile->dents[AAFS_PROF_RAW_ABI]);
+ profile->dents[AAFS_PROF_RAW_ABI] = NULL;
+ aafs_remove(profile->dents[AAFS_PROF_RAW_DATA]);
+ profile->dents[AAFS_PROF_RAW_DATA] = NULL;
+}
+
+static inline int create_symlink_dent(struct aa_profile *profile,
+ const char *name,
+ enum aafs_prof_type type,
+ const struct inode_operations *iops)
+{
+ struct dentry *dent = NULL;
+ struct dentry *dir = prof_dir(profile);
+
+ if (profile->dents[type])
+ return 0;
+
+ dent = aafs_create(name, S_IFLNK | 0444, dir,
+ &profile->label.proxy->count, NULL, NULL, iops);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ profile->dents[type] = dent;
+ return 0;
+}
+
+/*
+ * Requires: @profile->ns->lock held
+ */
+int __aa_create_rawdata_symlink_dents(struct aa_profile *profile)
+{
+ int error;
+
+ if (!profile ||
+ (profile->dents[AAFS_PROF_RAW_HASH] &&
+ profile->dents[AAFS_PROF_RAW_ABI] &&
+ profile->dents[AAFS_PROF_RAW_DATA]))
+ return 0;
+
+ if (!profile->rawdata)
+ return 0;
+
+ if (aa_g_hash_policy) {
+ error = create_symlink_dent(profile, "raw_sha256",
+ AAFS_PROF_RAW_HASH,
+ &rawdata_link_sha256_iops);
+ if (error)
+ return error;
+ }
+
+ error = create_symlink_dent(profile, "raw_abi",
+ AAFS_PROF_RAW_ABI,
+ &rawdata_link_abi_iops);
+ if (error)
+ return error;
+
+
+ error = create_symlink_dent(profile, "raw_data",
+ AAFS_PROF_RAW_DATA,
+ &rawdata_link_data_iops);
+ if (error)
+ return error;
+
+ return 0;
+}
+
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
/*
@@ -1775,34 +2052,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
profile->dents[AAFS_PROF_HASH] = dent;
}
-#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
- 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);
- 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,
- &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,
- &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 */
+ error = __aa_create_rawdata_symlink_dents(profile);
+ if (error)
+ goto fail2;
list_for_each_entry(child, &profile->base.profiles, base.list) {
error = __aafs_profile_mkdir(child, prof_child_dir(profile));
@@ -1828,15 +2080,16 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
/* TODO: improve permission check */
struct aa_label *label;
int error;
+ bool needput;
- label = begin_current_label_crit_section();
- error = aa_may_manage_policy(current_cred(), label, NULL,
+ label = begin_current_label_crit_section(&needput);
+ error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
AA_MAY_LOAD_POLICY);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
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
@@ -1869,7 +2122,7 @@ out:
mutex_unlock(&parent->lock);
aa_put_ns(parent);
- return ERR_PTR(error);
+ return error ? ERR_PTR(error) : NULL;
}
static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
@@ -1877,16 +2130,17 @@ static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
struct aa_ns *ns, *parent;
/* TODO: improve permission check */
struct aa_label *label;
+ bool needput;
int error;
- label = begin_current_label_crit_section();
- error = aa_may_manage_policy(current_cred(), label, NULL,
+ label = begin_current_label_crit_section(&needput);
+ error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
AA_MAY_LOAD_POLICY);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
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 +2210,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 +2234,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;
@@ -2390,12 +2623,17 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = {
static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_DIR("versions", aa_sfs_entry_versions),
AA_SFS_FILE_BOOLEAN("set_load", 1),
+ AA_SFS_FILE_BOOLEAN("diff-encode", 1),
/* number of out of band transitions supported */
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
AA_SFS_FILE_U64("permstable32_version", 3),
AA_SFS_FILE_STRING("permstable32", PERMS32STR),
AA_SFS_FILE_U64("state32", 1),
AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
+#ifdef CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY
+ AA_SFS_FILE_BOOLEAN("compressed_load", 1),
+ AA_SFS_FILE_BOOLEAN("extended_policy_header", 1),
+#endif
{ }
};
@@ -2460,8 +2698,10 @@ static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
AA_SFS_FILE_FOPS("raw_data_compression_level_min", 0444, &seq_ns_compress_min_fops),
AA_SFS_FILE_FOPS("raw_data_compression_level_max", 0444, &seq_ns_compress_max_fops),
+#endif
AA_SFS_DIR("features", aa_sfs_entry_features),
{ }
};
@@ -2585,8 +2825,7 @@ static int aa_mk_null_file(struct dentry *parent)
if (error)
return error;
- inode_lock(d_inode(parent));
- dentry = lookup_noperm(&QSTR(NULL_FILE_NAME), parent);
+ dentry = simple_start_creating(parent, NULL_FILE_NAME);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out;
@@ -2594,7 +2833,7 @@ static int aa_mk_null_file(struct dentry *parent)
inode = new_inode(parent->d_inode->i_sb);
if (!inode) {
error = -ENOMEM;
- goto out1;
+ goto out;
}
inode->i_ino = get_next_ino();
@@ -2606,18 +2845,12 @@ static int aa_mk_null_file(struct dentry *parent)
aa_null.dentry = dget(dentry);
aa_null.mnt = mntget(mount);
- error = 0;
-
-out1:
- dput(dentry);
out:
- inode_unlock(d_inode(parent));
+ simple_done_creating(dentry);
simple_release_fs(&mount, &count);
return error;
}
-
-
static const char *policy_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
@@ -2644,7 +2877,7 @@ static int policy_readlink(struct dentry *dentry, char __user *buffer,
char name[32];
int res;
- res = snprintf(name, sizeof(name), "%s:[%lu]", AAFS_NAME,
+ res = snprintf(name, sizeof(name), "%s:[%llu]", AAFS_NAME,
d_inode(dentry)->i_ino);
if (res > 0 && res < sizeof(name))
res = readlink_copy(buffer, buflen, name, strlen(name));