mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
Merge tag 'apparmor-pr-2026-06-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull apparmor updates from John Johansen:
"Another round of bug fixing and some code cleanups, there are no new
features. The biggest thing to note is Georgia is being added to help
co-maintain apparmor.
Cleanups:
- replace get_zeroed_page() with kzalloc()
- remove unnecessary goto and associated label
- change fn_label_build() to return err on failure instead of NULL or
err
- free rawdata as soon as possible
- use explicit instead of implicit flex array in rawdata_f_data
- use __label_make_stale in __aa_proxy_redirect
- return correct error by propagate -ENOMEM correctly in unpack_table
- aa_label_alloc use aa_label_free on alloc failure
- add a conditional version of get_newest_label
Bug Fixes:
- mediate the implicit connect of TCP fast open sendmsg
- fix C23ism of label immediately before a declaration
- fix kernel-doc warnings
- fix spelling mistakes
- fix use-after-free in rawdata dedup loop
- Fix inverted comparison in cache_hold_inc()
- fix uninitialized pointer passed to audit_log_untrustedstring()
- don't audit files pointing to aa_null.dentry
- put secmark label after secid lookup
- fix aa_getprocattr free procattr leak on format failure
- release exe file resources on path failure
- fail policy unpack on accept2 allocation failure
- Fix return in ns_mkdir_op
- remove or add symlinks to rawdata according to export_binary
- fix NULL pointer dereference in unpack_pdb
- fix potential UAF in aa_replace_profiles
- grab ns lock and refresh when looking up changehat child profiles
- enable differential encoding
- check label build before no_new_privs test
- conditionally compile get_loaddata_common_ref()
- fix unix socket mediation cache update, and leak"
* tag 'apparmor-pr-2026-06-22' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (35 commits)
apparmor: advertise the tcp fast open fix is applied
apparmor: mediate the implicit connect of TCP fast open sendmsg
apparmor: fix label can not be immediately before a declaration
apparmor: fix kernel-doc warnings
apparmor: replace get_zeroed_page() with kzalloc()
security: apparmor: fix two spelling mistakes
apparmor: fix use-after-free in rawdata dedup loop
apparmor: Fix inverted comparison in cache_hold_inc()
apparmor: fix uninitialised pointer passed to audit_log_untrustedstring()
apparmor: don't audit files pointing to aa_null.dentry
apparmor: put secmark label after secid lookup
apparmor: aa_getprocattr free procattr leak on format failure
apparmor: remove unnecessary goto and associated label
apparmor: release exe file resources on path failure
apparmor: fail policy unpack on accept2 allocation failure
apparmor: Fix return in ns_mkdir_op
apparmor: remove or add symlinks to rawdata according to export_binary
apparmor: fix NULL pointer dereference in unpack_pdb
apparmor: make fn_label_build() capable of handling not supported
apparmor: change fn_label_build() call to not return NULL
...
This commit is contained in:
@@ -1987,6 +1987,7 @@ F: include/uapi/linux/apm_bios.h
|
||||
APPARMOR SECURITY MODULE
|
||||
M: John Johansen <john.johansen@canonical.com>
|
||||
M: John Johansen <john@apparmor.net>
|
||||
M: Georgia Garcia <georgia.garcia@canonical.com>
|
||||
L: apparmor@lists.ubuntu.com (moderated for non-subscribers)
|
||||
S: Supported
|
||||
W: apparmor.net
|
||||
|
||||
@@ -615,7 +615,7 @@ static int unix_peer_perm(const struct cred *subj_cred,
|
||||
peer_label, &ad));
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
*
|
||||
* Requires: lock held on both @sk and @peer_sk
|
||||
* called by unix_stream_connect, unix_may_send
|
||||
@@ -674,9 +674,11 @@ static void update_sk_ctx(struct sock *sk, struct aa_label *label,
|
||||
old = rcu_dereference_protected(ctx->peer, lockdep_is_held(&unix_sk(sk)->lock));
|
||||
|
||||
if (old == plabel) {
|
||||
rcu_assign_pointer(ctx->peer_lastupdate, plabel);
|
||||
rcu_assign_pointer(ctx->peer_lastupdate,
|
||||
aa_get_label(plabel));
|
||||
} else if (aa_label_is_subset(plabel, old)) {
|
||||
rcu_assign_pointer(ctx->peer_lastupdate, plabel);
|
||||
rcu_assign_pointer(ctx->peer_lastupdate,
|
||||
aa_get_label(plabel));
|
||||
rcu_assign_pointer(ctx->peer, aa_get_label(plabel));
|
||||
aa_put_label(old);
|
||||
} /* else race or a subset - don't update */
|
||||
@@ -748,42 +750,47 @@ int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
|
||||
if (!peer_sk)
|
||||
goto out;
|
||||
|
||||
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
|
||||
if (!is_sk_fs) {
|
||||
bool is_peer_fs = is_unix_fs(peer_sk);
|
||||
|
||||
struct path peer_path;
|
||||
peer_addr = aa_sunaddr(unix_sk(peer_sk), &peer_addrlen);
|
||||
if (is_peer_fs) {
|
||||
struct path peer_path;
|
||||
|
||||
peer_path = unix_sk(peer_sk)->path;
|
||||
if (!is_sk_fs && is_unix_fs(peer_sk)) {
|
||||
last_error(error,
|
||||
unix_fs_perm(op, request, subj_cred, label,
|
||||
is_unix_fs(peer_sk) ? &peer_path : NULL));
|
||||
} else if (!is_sk_fs) {
|
||||
struct aa_label *plabel;
|
||||
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
|
||||
unix_state_lock(peer_sk);
|
||||
peer_path = unix_sk(peer_sk)->path;
|
||||
if (peer_path.dentry)
|
||||
path_get(&peer_path);
|
||||
unix_state_unlock(peer_sk);
|
||||
|
||||
rcu_read_lock();
|
||||
plabel = aa_get_label_rcu(&pctx->label);
|
||||
rcu_read_unlock();
|
||||
/* no fs check of aa_unix_peer_perm because conditions above
|
||||
* ensure they will never be done
|
||||
*/
|
||||
last_error(error,
|
||||
xcheck(unix_peer_perm(subj_cred, label, op,
|
||||
last_error(error,
|
||||
unix_fs_perm(op, request, subj_cred, label,
|
||||
&peer_path));
|
||||
if (peer_path.dentry)
|
||||
path_put(&peer_path);
|
||||
} else {
|
||||
struct aa_sk_ctx *pctx = aa_sock(peer_sk);
|
||||
|
||||
rcu_read_lock();
|
||||
plabel = aa_get_newest_label(pctx->label);
|
||||
rcu_read_unlock();
|
||||
/* no fs check of aa_unix_peer_perm because conditions
|
||||
* above ensure they will never be done
|
||||
*/
|
||||
last_error(error,
|
||||
xcheck(unix_peer_perm(subj_cred, label, op,
|
||||
MAY_READ | MAY_WRITE, sock->sk,
|
||||
is_sk_fs ? &path : NULL,
|
||||
peer_addr, peer_addrlen,
|
||||
is_unix_fs(peer_sk) ?
|
||||
&peer_path : NULL,
|
||||
plabel),
|
||||
unix_peer_perm(file->f_cred, plabel, op,
|
||||
NULL, plabel),
|
||||
unix_peer_perm(file->f_cred, plabel, op,
|
||||
MAY_READ | MAY_WRITE, peer_sk,
|
||||
is_unix_fs(peer_sk) ?
|
||||
&peer_path : NULL,
|
||||
addr, addrlen,
|
||||
NULL, addr, addrlen,
|
||||
is_sk_fs ? &path : NULL,
|
||||
label)));
|
||||
if (!error && !__aa_subj_label_is_cached(plabel, label))
|
||||
update_peer_ctx(peer_sk, pctx, label);
|
||||
if (!error && !__aa_subj_label_is_cached(plabel, label))
|
||||
update_peer_ctx(peer_sk, pctx, label);
|
||||
}
|
||||
}
|
||||
sock_put(peer_sk);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
@@ -71,10 +72,10 @@
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -174,6 +175,7 @@ static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
if (ref)
|
||||
@@ -181,6 +183,7 @@ static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
|
||||
count));
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void aa_put_common_ref(struct aa_common_ref *ref)
|
||||
{
|
||||
@@ -904,7 +907,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 *
|
||||
@@ -947,7 +950,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);
|
||||
@@ -1434,7 +1437,7 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1467,8 +1470,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;
|
||||
|
||||
@@ -1756,6 +1758,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 */
|
||||
|
||||
/*
|
||||
@@ -1831,31 +1907,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->count, NULL,
|
||||
NULL, &rawdata_link_sha256_iops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
profile->dents[AAFS_PROF_RAW_HASH] = dent;
|
||||
}
|
||||
dent = aafs_create("raw_abi", S_IFLNK | 0444, dir,
|
||||
&profile->label.proxy->count, NULL, NULL,
|
||||
&rawdata_link_abi_iops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
profile->dents[AAFS_PROF_RAW_ABI] = dent;
|
||||
|
||||
dent = aafs_create("raw_data", S_IFLNK | 0444, dir,
|
||||
&profile->label.proxy->count, NULL, NULL,
|
||||
&rawdata_link_data_iops);
|
||||
if (IS_ERR(dent))
|
||||
goto fail;
|
||||
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));
|
||||
@@ -1922,7 +1976,7 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
|
||||
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)
|
||||
@@ -2422,6 +2476,7 @@ 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),
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/xattr.h>
|
||||
@@ -135,7 +136,7 @@ static int label_compound_match(struct aa_profile *profile,
|
||||
struct label_it i;
|
||||
struct path_cond cond = { };
|
||||
|
||||
/* find first subcomponent that is in view and going to be interated with */
|
||||
/* find first subcomponent that is in view and going to be interacted with */
|
||||
label_for_each(i, label, tp) {
|
||||
if (!aa_ns_visible(profile->ns, tp->ns, inview))
|
||||
continue;
|
||||
@@ -863,6 +864,15 @@ static int profile_onexec(const struct cred *subj_cred,
|
||||
}
|
||||
|
||||
/* ensure none ns domain transitions are correctly applied with onexec */
|
||||
static struct aa_label *label_merge_wrap(struct aa_label *a, struct aa_label *b,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct aa_label *label = aa_label_merge(a, b, gfp);
|
||||
|
||||
if (!label)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return label;
|
||||
}
|
||||
|
||||
static struct aa_label *handle_onexec(const struct cred *subj_cred,
|
||||
struct aa_label *label,
|
||||
@@ -890,12 +900,13 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
|
||||
return ERR_PTR(error);
|
||||
|
||||
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
|
||||
stack ? aa_label_merge(&profile->label, onexec,
|
||||
GFP_KERNEL)
|
||||
stack ? label_merge_wrap(&profile->label, onexec,
|
||||
GFP_KERNEL)
|
||||
: aa_get_newest_label(onexec),
|
||||
profile_transition(subj_cred, profile, bprm,
|
||||
buffer, cond, unsafe));
|
||||
if (new)
|
||||
AA_BUG(!new);
|
||||
if (!IS_ERR(new))
|
||||
return new;
|
||||
|
||||
/* TODO: get rid of GLOBAL_ROOT_UID */
|
||||
@@ -904,7 +915,8 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
|
||||
OP_CHANGE_ONEXEC,
|
||||
AA_MAY_ONEXEC, bprm->filename, NULL,
|
||||
onexec, GLOBAL_ROOT_UID,
|
||||
"failed to build target label", -ENOMEM));
|
||||
"failed to build target label",
|
||||
PTR_ERR(new)));
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
@@ -967,14 +979,10 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||
profile_transition(subj_cred, profile, bprm,
|
||||
buffer,
|
||||
&cond, &unsafe));
|
||||
|
||||
AA_BUG(!new);
|
||||
if (IS_ERR(new)) {
|
||||
error = PTR_ERR(new);
|
||||
goto done;
|
||||
} else if (!new) {
|
||||
error = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Policy has specified a domain transitions. If no_new_privs and
|
||||
@@ -1109,6 +1117,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
|
||||
int count, int flags)
|
||||
{
|
||||
struct aa_profile *profile, *root, *hat = NULL;
|
||||
struct aa_ns *ns, *new_ns;
|
||||
struct aa_label *new;
|
||||
struct label_it it;
|
||||
bool sibling = false;
|
||||
@@ -1119,6 +1128,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
|
||||
AA_BUG(!hats);
|
||||
AA_BUG(count < 1);
|
||||
|
||||
/*
|
||||
* Acquire the newest label and then hold the lock until we choose a
|
||||
* hat, so that profile replacement doesn't atomically truncate the
|
||||
* list of potential hats. Because we are getting the namespaces from
|
||||
* the profiles and label, we can rely on the namespaces being live
|
||||
* and avoid incrementing their refcounts while grabbing the lock.
|
||||
*/
|
||||
label = aa_get_label(label);
|
||||
ns = labels_ns(label);
|
||||
|
||||
retry:
|
||||
mutex_lock_nested(&ns->lock, ns->level);
|
||||
if (label_is_stale(label)) {
|
||||
new = aa_get_newest_label(label);
|
||||
new_ns = labels_ns(new);
|
||||
if (new_ns != ns) {
|
||||
aa_put_label(new);
|
||||
mutex_unlock(&ns->lock);
|
||||
ns = new_ns;
|
||||
label = new;
|
||||
goto retry;
|
||||
}
|
||||
aa_put_label(label);
|
||||
label = new;
|
||||
}
|
||||
|
||||
if (PROFILE_IS_HAT(labels_profile(label)))
|
||||
sibling = true;
|
||||
|
||||
@@ -1127,7 +1162,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
|
||||
name = hats[i];
|
||||
label_for_each_in_scope(it, labels_ns(label), label, profile) {
|
||||
if (sibling && PROFILE_IS_HAT(profile)) {
|
||||
root = aa_get_profile_rcu(&profile->parent);
|
||||
root = aa_get_profile(profile->parent);
|
||||
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
|
||||
root = aa_get_profile(profile);
|
||||
} else { /* conflicting change type */
|
||||
@@ -1187,6 +1222,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
|
||||
GLOBAL_ROOT_UID, info, error);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ns->lock);
|
||||
return ERR_PTR(error);
|
||||
|
||||
build:
|
||||
@@ -1194,11 +1230,9 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
|
||||
build_change_hat(subj_cred, profile, name,
|
||||
sibling),
|
||||
aa_get_label(&profile->label));
|
||||
if (!new) {
|
||||
info = "label build failed";
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
} /* else if (IS_ERR) build_change_hat has logged error so return new */
|
||||
mutex_unlock(&ns->lock);
|
||||
AA_BUG(!new);
|
||||
/* return new label or error ptr */
|
||||
|
||||
return new;
|
||||
}
|
||||
@@ -1527,6 +1561,9 @@ int aa_change_profile(const char *fqname, int flags)
|
||||
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
|
||||
aa_get_label(target),
|
||||
aa_get_label(&profile->label));
|
||||
AA_BUG(!new);
|
||||
if (IS_ERR(new))
|
||||
goto build_fail;
|
||||
/*
|
||||
* no new privs prevents domain transitions that would
|
||||
* reduce restrictions.
|
||||
@@ -1545,27 +1582,29 @@ int aa_change_profile(const char *fqname, int flags)
|
||||
/* only transition profiles in the current ns */
|
||||
if (stack)
|
||||
new = aa_label_merge(label, target, GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(new)) {
|
||||
info = "failed to build target label";
|
||||
if (!new)
|
||||
error = -ENOMEM;
|
||||
else
|
||||
error = PTR_ERR(new);
|
||||
new = NULL;
|
||||
perms.allow = 0;
|
||||
goto audit;
|
||||
}
|
||||
if (IS_ERR_OR_NULL(new))
|
||||
goto build_fail;
|
||||
error = aa_replace_current_label(new);
|
||||
} else {
|
||||
if (new) {
|
||||
aa_put_label(new);
|
||||
new = NULL;
|
||||
}
|
||||
/* new will be recomputed so at exec time. So discard */
|
||||
aa_put_label(new);
|
||||
new = NULL;
|
||||
|
||||
/* full transition will be built in exec path */
|
||||
aa_set_current_onexec(target, stack);
|
||||
}
|
||||
|
||||
goto audit;
|
||||
|
||||
build_fail:
|
||||
info = "failed to build target label";
|
||||
if (!new)
|
||||
error = -ENOMEM;
|
||||
else
|
||||
error = PTR_ERR(new);
|
||||
new = NULL;
|
||||
perms.allow = 0;
|
||||
|
||||
audit:
|
||||
error = fn_for_each_in_scope(label, profile,
|
||||
aa_audit_file(subj_cred,
|
||||
|
||||
@@ -157,10 +157,10 @@ static int path_name(const char *op, const struct cred *subj_cred,
|
||||
|
||||
/* don't reaudit files closed during inheritance */
|
||||
if (unlikely(path->dentry == aa_null.dentry))
|
||||
error = -EACCES;
|
||||
else
|
||||
error = aa_path_name(path, flags, buffer, name, &info,
|
||||
labels_profile(label)->disconnected);
|
||||
return -EACCES;
|
||||
|
||||
error = aa_path_name(path, flags, buffer, name, &info,
|
||||
labels_profile(label)->disconnected);
|
||||
if (error) {
|
||||
fn_for_each_confined(label, profile,
|
||||
aa_audit_file(subj_cred,
|
||||
@@ -250,7 +250,7 @@ static int profile_path_perm(const char *op, const struct cred *subj_cred,
|
||||
struct path_cond *cond, int flags,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
const char *name;
|
||||
const char *name = NULL;
|
||||
int error;
|
||||
|
||||
if (profile_unconfined(profile))
|
||||
@@ -328,7 +328,7 @@ static int profile_path_link(const struct cred *subj_cred,
|
||||
struct path_cond *cond)
|
||||
{
|
||||
struct aa_ruleset *rules = profile->label.rules[0];
|
||||
const char *lname, *tname = NULL;
|
||||
const char *lname = NULL, *tname = NULL;
|
||||
struct aa_perms lperms = {}, perms;
|
||||
const char *info = NULL;
|
||||
u32 request = AA_MAY_LINK;
|
||||
|
||||
@@ -120,6 +120,8 @@ struct aa_loaddata;
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata);
|
||||
int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata);
|
||||
void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile);
|
||||
int __aa_create_rawdata_symlink_dents(struct aa_profile *profile);
|
||||
#else
|
||||
static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata)
|
||||
{
|
||||
@@ -131,6 +133,16 @@ static inline int __aa_fs_create_rawdata(struct aa_ns *ns,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile)
|
||||
{
|
||||
/* empty stub */
|
||||
}
|
||||
|
||||
static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
|
||||
|
||||
#endif /* __AA_APPARMORFS_H */
|
||||
|
||||
@@ -423,6 +423,38 @@ static inline struct aa_label *aa_get_newest_label(struct aa_label *l)
|
||||
return aa_get_label(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_get_newest_label_condref - find the newest version of @l
|
||||
* @l: the label to check for newer versions of
|
||||
* @needput: returns whether the reference needs put
|
||||
*
|
||||
* Returns: refcounted newest version of @l taking into account
|
||||
* replacement, renames and removals
|
||||
* return @l.
|
||||
*/
|
||||
static inline struct aa_label *aa_get_newest_label_condref(struct aa_label *l,
|
||||
bool *needput)
|
||||
{
|
||||
if (l && unlikely(label_is_stale(l))) {
|
||||
struct aa_label *tmp;
|
||||
|
||||
AA_BUG(!l->proxy);
|
||||
AA_BUG(!l->proxy->label);
|
||||
/* BUG: only way this can happen is @l ref count and its
|
||||
* replacement count have gone to 0 and are on their way
|
||||
* to destruction. ie. we have a refcounting error
|
||||
*/
|
||||
tmp = aa_get_label_rcu(&l->proxy->label);
|
||||
AA_BUG(!tmp);
|
||||
|
||||
*needput = true;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
*needput = false;
|
||||
return l;
|
||||
}
|
||||
|
||||
static inline void aa_put_label(struct aa_label *l)
|
||||
{
|
||||
if (l)
|
||||
|
||||
@@ -281,15 +281,15 @@ void aa_policy_destroy(struct aa_policy *policy);
|
||||
* @FN: fn to call for each profile transition. @P is set to the profile
|
||||
*
|
||||
* Returns: new label on success
|
||||
* NULL if all callbacks decline to specify a transition
|
||||
* ERR_PTR if build @FN fails
|
||||
* NULL if label_build fails due to low memory conditions
|
||||
*
|
||||
* @FN must return a label or ERR_PTR on failure. NULL is not allowed
|
||||
* @FN must return a label or ERR_PTR on failure.
|
||||
*/
|
||||
#define fn_label_build(L, P, GFP, FN) \
|
||||
({ \
|
||||
__label__ __do_cleanup, __done; \
|
||||
struct aa_label *__new_; \
|
||||
struct aa_label *__new_ = NULL; \
|
||||
\
|
||||
if ((L)->size > 1) { \
|
||||
/* TODO: add cache of transitions already done */ \
|
||||
@@ -298,17 +298,21 @@ void aa_policy_destroy(struct aa_policy *policy);
|
||||
DEFINE_VEC(label, __lvec); \
|
||||
DEFINE_VEC(profile, __pvec); \
|
||||
if (vec_setup(label, __lvec, (L)->size, (GFP))) { \
|
||||
__new_ = NULL; \
|
||||
__new_ = ERR_PTR(-ENOMEM); \
|
||||
goto __done; \
|
||||
} \
|
||||
__j = 0; \
|
||||
label_for_each(__i, (L), (P)) { \
|
||||
__new_ = (FN); \
|
||||
AA_BUG(!__new_); \
|
||||
if (!__new_) \
|
||||
continue; \
|
||||
if (IS_ERR(__new_)) \
|
||||
goto __do_cleanup; \
|
||||
__lvec[__j++] = __new_; \
|
||||
} \
|
||||
if (__j == 0) \
|
||||
/* no components adding to build */ \
|
||||
goto __do_cleanup; \
|
||||
for (__j = __count = 0; __j < (L)->size; __j++) \
|
||||
__count += __lvec[__j]->size; \
|
||||
if (!vec_setup(profile, __pvec, __count, (GFP))) { \
|
||||
@@ -320,14 +324,13 @@ void aa_policy_destroy(struct aa_policy *policy);
|
||||
if (__count > 1) { \
|
||||
__new_ = aa_vec_find_or_create_label(__pvec,\
|
||||
__count, (GFP)); \
|
||||
/* only fails if out of Mem */ \
|
||||
if (!__new_) \
|
||||
__new_ = NULL; \
|
||||
__new_ = ERR_PTR(-ENOMEM); \
|
||||
} else \
|
||||
__new_ = aa_get_label(&__pvec[0]->label); \
|
||||
vec_cleanup(profile, __pvec, __count); \
|
||||
} else \
|
||||
__new_ = NULL; \
|
||||
__new_ = ERR_PTR(-ENOMEM); \
|
||||
__do_cleanup: \
|
||||
vec_cleanup(label, __lvec, (L)->size); \
|
||||
} else { \
|
||||
@@ -335,7 +338,7 @@ __do_cleanup: \
|
||||
__new_ = (FN); \
|
||||
} \
|
||||
__done: \
|
||||
if (!__new_) \
|
||||
if (PTR_ERR(__new_)) \
|
||||
AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \
|
||||
(__new_); \
|
||||
})
|
||||
|
||||
@@ -131,7 +131,7 @@ struct aa_loaddata {
|
||||
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
|
||||
|
||||
/**
|
||||
* aa_get_loaddata - get a reference count from a counted data reference
|
||||
* aa_get_i_loaddata - get a reference count from a counted data reference
|
||||
* @data: reference to get a count on
|
||||
*
|
||||
* Returns: pointer to reference
|
||||
@@ -163,6 +163,25 @@ aa_get_profile_loaddata(struct aa_loaddata *data)
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_get_profile_loaddata_not0 - get a profile reference count if not zero
|
||||
* @data: reference to get a count on
|
||||
*
|
||||
* Like aa_get_profile_loaddata(), but safe to call on an entry that may
|
||||
* be on a list (e.g. ns->rawdata_list) where the last pcount has already
|
||||
* dropped and the deferred cleanup has not yet run.
|
||||
*
|
||||
* Returns: pointer to reference, or %NULL if @data is NULL or its
|
||||
* profile refcount has already reached zero.
|
||||
*/
|
||||
static inline struct aa_loaddata *
|
||||
aa_get_profile_loaddata_not0(struct aa_loaddata *data)
|
||||
{
|
||||
if (data && kref_get_unless_zero(&data->pcount))
|
||||
return data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -83,7 +83,7 @@ void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new)
|
||||
tmp = rcu_dereference_protected(orig->proxy->label,
|
||||
&labels_ns(orig)->lock);
|
||||
rcu_assign_pointer(orig->proxy->label, aa_get_label(new));
|
||||
orig->flags |= FLAG_STALE;
|
||||
__label_make_stale(orig);
|
||||
aa_put_label(tmp);
|
||||
}
|
||||
|
||||
@@ -458,7 +458,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
|
||||
return new;
|
||||
|
||||
fail:
|
||||
kfree(new);
|
||||
aa_label_free(new);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1176,22 +1176,21 @@ static struct aa_label *__label_find_merge(struct aa_labelset *ls,
|
||||
struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b)
|
||||
{
|
||||
struct aa_labelset *ls;
|
||||
struct aa_label *label, *ar = NULL, *br = NULL;
|
||||
struct aa_label *label;
|
||||
unsigned long flags;
|
||||
bool a_needput, b_needput;
|
||||
|
||||
AA_BUG(!a);
|
||||
AA_BUG(!b);
|
||||
|
||||
if (label_is_stale(a))
|
||||
a = ar = aa_get_newest_label(a);
|
||||
if (label_is_stale(b))
|
||||
b = br = aa_get_newest_label(b);
|
||||
a = aa_get_newest_label_condref(a, &a_needput);
|
||||
b = aa_get_newest_label_condref(b, &b_needput);
|
||||
ls = labelset_of_merge(a, b);
|
||||
read_lock_irqsave(&ls->lock, flags);
|
||||
label = __label_find_merge(ls, a, b);
|
||||
read_unlock_irqrestore(&ls->lock, flags);
|
||||
aa_put_label(ar);
|
||||
aa_put_label(br);
|
||||
aa_put_label_condref(a, a_needput);
|
||||
aa_put_label_condref(b, b_needput);
|
||||
|
||||
return label;
|
||||
}
|
||||
@@ -1228,9 +1227,10 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
|
||||
|
||||
if (!label) {
|
||||
struct aa_label *new;
|
||||
bool a_needput, b_needput;
|
||||
|
||||
a = aa_get_newest_label(a);
|
||||
b = aa_get_newest_label(b);
|
||||
a = aa_get_newest_label_condref(a, &a_needput);
|
||||
b = aa_get_newest_label_condref(b, &b_needput);
|
||||
|
||||
/* could use label_merge_len(a, b), but requires double
|
||||
* comparison for small savings
|
||||
@@ -1242,8 +1242,8 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
|
||||
label = label_merge_insert(new, a, b);
|
||||
label_free_or_put_new(label, new);
|
||||
out:
|
||||
aa_put_label(a);
|
||||
aa_put_label(b);
|
||||
aa_put_label_condref(a, a_needput);
|
||||
aa_put_label_condref(b, b_needput);
|
||||
}
|
||||
|
||||
return label;
|
||||
|
||||
@@ -1422,7 +1422,21 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
|
||||
static int apparmor_socket_sendmsg(struct socket *sock,
|
||||
struct msghdr *msg, int size)
|
||||
{
|
||||
return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
|
||||
int error = aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* TCP fast open carries connect() semantics in sendmsg(); mediate
|
||||
* the implicit connect so it cannot bypass the connect permission.
|
||||
*/
|
||||
if ((msg->msg_flags & MSG_FASTOPEN) && msg->msg_name &&
|
||||
(sk_is_tcp(sock->sk) ||
|
||||
(sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM &&
|
||||
sock->sk->sk_protocol == IPPROTO_MPTCP)))
|
||||
error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int apparmor_socket_recvmsg(struct socket *sock,
|
||||
@@ -1493,7 +1507,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
|
||||
*
|
||||
* Note: can not sleep may be called with locks held
|
||||
*
|
||||
* dont want protocol specific in __skb_recv_datagram()
|
||||
* don't want protocol specific in __skb_recv_datagram()
|
||||
* to deny an incoming connection socket_sock_rcv_skb()
|
||||
*/
|
||||
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
@@ -2129,7 +2143,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp)
|
||||
*/
|
||||
static void cache_hold_inc(unsigned int *hold)
|
||||
{
|
||||
if (*hold > MAX_HOLD_COUNT)
|
||||
if (*hold < MAX_HOLD_COUNT)
|
||||
(*hold)++;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
* @blob: data to unpack (NOT NULL)
|
||||
* @bsize: size of blob
|
||||
*
|
||||
* Returns: pointer to table else NULL on failure
|
||||
* Returns: pointer to table else ERR_PTR on failure
|
||||
*
|
||||
* NOTE: must be freed by kvfree (not kfree)
|
||||
*/
|
||||
static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
{
|
||||
struct table_header *table = NULL;
|
||||
struct table_header *table = ERR_PTR(-EPROTO);
|
||||
struct table_header th;
|
||||
size_t tsize;
|
||||
|
||||
@@ -74,20 +74,21 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
else if (th.td_flags == YYTD_DATA32)
|
||||
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
||||
u32, __be32, get_unaligned_be32);
|
||||
else
|
||||
goto fail;
|
||||
else {
|
||||
kvfree(table);
|
||||
table = ERR_PTR(-EPROTO);
|
||||
goto out;
|
||||
}
|
||||
/* if table was vmalloced make sure the page tables are synced
|
||||
* before it is used, as it goes live to all cpus.
|
||||
*/
|
||||
if (is_vmalloc_addr(table))
|
||||
vm_unmap_aliases();
|
||||
}
|
||||
} else
|
||||
table = ERR_PTR(-ENOMEM);
|
||||
|
||||
out:
|
||||
return table;
|
||||
fail:
|
||||
kvfree(table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,8 +360,11 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
|
||||
|
||||
while (size > 0) {
|
||||
table = unpack_table(data, size);
|
||||
if (!table)
|
||||
if (IS_ERR(table)) {
|
||||
error = PTR_ERR(table);
|
||||
table = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (table->td_id) {
|
||||
case YYTD_ID_ACCEPT:
|
||||
|
||||
@@ -735,17 +735,11 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
|
||||
build_pivotroot(subj_cred, profile, new_path,
|
||||
new_buffer,
|
||||
old_path, old_buffer));
|
||||
if (!target) {
|
||||
info = "label build failed";
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
} else if (!IS_ERR(target)) {
|
||||
AA_BUG(!target);
|
||||
if (!IS_ERR(target)) {
|
||||
error = aa_replace_current_label(target);
|
||||
if (error) {
|
||||
/* TODO: audit target */
|
||||
aa_put_label(target);
|
||||
goto out;
|
||||
}
|
||||
if (error)
|
||||
goto fail;
|
||||
aa_put_label(target);
|
||||
} else
|
||||
/* already audited error */
|
||||
@@ -763,7 +757,8 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
|
||||
NULL /*new_name */,
|
||||
NULL /* old_name */,
|
||||
NULL, NULL,
|
||||
0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
|
||||
0, target->hname, AA_MAY_PIVOTROOT, &nullperms, info,
|
||||
error));
|
||||
aa_put_label(target);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
|
||||
struct aa_sfs_entry aa_sfs_entry_network[] = {
|
||||
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
|
||||
AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
|
||||
{ }
|
||||
};
|
||||
|
||||
struct aa_sfs_entry aa_sfs_entry_networkv9[] = {
|
||||
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
|
||||
AA_SFS_FILE_BOOLEAN("af_unix", 1),
|
||||
AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
|
||||
{ }
|
||||
};
|
||||
|
||||
@@ -354,6 +356,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark)
|
||||
return PTR_ERR(label);
|
||||
|
||||
secmark->secid = label->secid;
|
||||
aa_put_label(label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -232,6 +232,13 @@ static void __remove_profile(struct aa_profile *profile)
|
||||
aa_label_remove(&profile->label);
|
||||
__aafs_profile_rmdir(profile);
|
||||
__list_remove_profile(profile);
|
||||
/* rawdata is only ever referenced by fs lookup, that is no
|
||||
* longer possible here, so put the reference to it. This will
|
||||
* enable the rawdata to be freed if for some reason the profile
|
||||
* is pinned and going to live for a while.
|
||||
*/
|
||||
aa_put_profile_loaddata(profile->rawdata);
|
||||
profile->rawdata = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1223,8 +1230,12 @@ 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_profile_loaddata(rawdata_ent);
|
||||
/* check we didn't fail the race */
|
||||
/*
|
||||
* Entries remain on rawdata_list with
|
||||
* pcount == 0 until do_ploaddata_rmfs()
|
||||
* runs; only take a live profile ref.
|
||||
*/
|
||||
tmp = aa_get_profile_loaddata_not0(rawdata_ent);
|
||||
if (tmp) {
|
||||
aa_put_profile_loaddata(udata);
|
||||
udata = tmp;
|
||||
@@ -1342,6 +1353,16 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (!aa_g_export_binary) {
|
||||
if (ent->old && ent->old->rawdata &&
|
||||
ent->old->dents[AAFS_LOADDATA_DIR]) {
|
||||
/* remove rawdata symlinks because the symlink
|
||||
* target will be removed
|
||||
*/
|
||||
__aa_remove_rawdata_symlink_dents(ent->old);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: finer dedup based on profile range in data. Load set
|
||||
* can differ but profile may remain unchanged
|
||||
@@ -1352,6 +1373,11 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
if (ent->old) {
|
||||
share_name(ent->old, ent->new);
|
||||
__replace_profile(ent->old, ent->new);
|
||||
if (aa_g_export_binary) {
|
||||
/* recreate rawdata symlinks */
|
||||
if (!ent->old->rawdata)
|
||||
__aa_create_rawdata_symlink_dents(ent->new);
|
||||
}
|
||||
} else {
|
||||
struct list_head *lh;
|
||||
|
||||
@@ -1372,12 +1398,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
||||
|
||||
out:
|
||||
aa_put_ns(ns);
|
||||
|
||||
ssize_t udata_sz = udata->size;
|
||||
|
||||
aa_put_profile_loaddata(udata);
|
||||
kfree(ns_name);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
return udata->size;
|
||||
return udata_sz;
|
||||
|
||||
fail_lock:
|
||||
mutex_unlock(&ns->lock);
|
||||
|
||||
@@ -1045,7 +1045,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
|
||||
}
|
||||
|
||||
/* accept2 is in some cases being allocated, even with perms */
|
||||
if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
|
||||
if (pdb->dfa && pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
|
||||
/* add dfa flags table missing in v2 */
|
||||
u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen;
|
||||
u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags;
|
||||
@@ -1054,7 +1054,8 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
|
||||
pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL);
|
||||
if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) {
|
||||
*info = "failed to alloc dfa flags table";
|
||||
goto out;
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents;
|
||||
pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags;
|
||||
@@ -1079,7 +1080,6 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
|
||||
* - move free of unneeded trans table here, has to be done
|
||||
* after perm mapping.
|
||||
*/
|
||||
out:
|
||||
*policy = pdb;
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ int aa_getprocattr(struct aa_label *label, char **string, bool newline)
|
||||
FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
|
||||
FLAG_HIDDEN_UNCONFINED);
|
||||
if (len < 0) {
|
||||
kfree(*string);
|
||||
*string = NULL;
|
||||
aa_put_ns(current_ns);
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ static const char *get_current_exe_path(char *buffer, int buffer_size)
|
||||
path_get(&p);
|
||||
|
||||
if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
path_str = ERR_PTR(-ENOMEM);
|
||||
|
||||
fput(exe_file);
|
||||
path_put(&p);
|
||||
|
||||
Reference in New Issue
Block a user