summaryrefslogtreecommitdiff
path: root/security/apparmor
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor')
-rw-r--r--security/apparmor/apparmorfs.c19
-rw-r--r--security/apparmor/audit.c10
-rw-r--r--security/apparmor/capability.c19
-rw-r--r--security/apparmor/domain.c67
-rw-r--r--security/apparmor/file.c13
-rw-r--r--security/apparmor/include/audit.h4
-rw-r--r--security/apparmor/include/cred.h20
-rw-r--r--security/apparmor/include/label.h28
-rw-r--r--security/apparmor/include/lib.h1
-rw-r--r--security/apparmor/include/match.h8
-rw-r--r--security/apparmor/include/net.h3
-rw-r--r--security/apparmor/include/perms.h3
-rw-r--r--security/apparmor/include/policy.h1
-rw-r--r--security/apparmor/include/secid.h6
-rw-r--r--security/apparmor/label.c33
-rw-r--r--security/apparmor/lib.c84
-rw-r--r--security/apparmor/lsm.c53
-rw-r--r--security/apparmor/match.c99
-rw-r--r--security/apparmor/mount.c2
-rw-r--r--security/apparmor/net.c2
-rw-r--r--security/apparmor/path.c2
-rw-r--r--security/apparmor/policy.c11
-rw-r--r--security/apparmor/policy_unpack.c50
-rw-r--r--security/apparmor/policy_unpack_test.c15
-rw-r--r--security/apparmor/secid.c55
25 files changed, 270 insertions, 338 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index bcfea073e3f2..0aef34b9609b 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -283,7 +283,7 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
dir = d_inode(parent);
inode_lock(dir);
- dentry = lookup_one_len(name, parent, strlen(name));
+ dentry = lookup_noperm(&QSTR(name), parent);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto fail_lock;
@@ -1692,6 +1692,10 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
struct aa_profile *p;
p = aa_deref_parent(profile);
dent = prof_dir(p);
+ if (!dent) {
+ error = -ENOENT;
+ goto fail2;
+ }
/* adding to parent that previously didn't have children */
dent = aafs_create_dir("profiles", dent);
if (IS_ERR(dent))
@@ -1791,8 +1795,8 @@ fail2:
return error;
}
-static int ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
- struct dentry *dentry, umode_t mode)
+static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode)
{
struct aa_ns *ns, *parent;
/* TODO: improve permission check */
@@ -1804,7 +1808,7 @@ static int ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
AA_MAY_LOAD_POLICY);
end_current_label_crit_section(label);
if (error)
- return error;
+ return ERR_PTR(error);
parent = aa_get_ns(dir->i_private);
AA_BUG(d_inode(ns_subns_dir(parent)) != dir);
@@ -1839,7 +1843,7 @@ out:
mutex_unlock(&parent->lock);
aa_put_ns(parent);
- return error;
+ return ERR_PTR(error);
}
static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
@@ -2362,6 +2366,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
AA_SFS_FILE_U64("permstable32_version", 1),
AA_SFS_FILE_STRING("permstable32", PERMS32STR),
+ AA_SFS_FILE_U64("state32", 1),
AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
{ }
};
@@ -2546,7 +2551,7 @@ static int aa_mk_null_file(struct dentry *parent)
return error;
inode_lock(d_inode(parent));
- dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME));
+ dentry = lookup_noperm(&QSTR(NULL_FILE_NAME), parent);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out;
@@ -2607,7 +2612,7 @@ static int policy_readlink(struct dentry *dentry, char __user *buffer,
res = snprintf(name, sizeof(name), "%s:[%lu]", AAFS_NAME,
d_inode(dentry)->i_ino);
if (res > 0 && res < sizeof(name))
- res = readlink_copy(buffer, buflen, name);
+ res = readlink_copy(buffer, buflen, name, strlen(name));
else
res = -ENOENT;
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 45beb1c5f747..73087d76f649 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -217,7 +217,7 @@ void aa_audit_rule_free(void *vrule)
}
}
-int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule, gfp_t gfp)
{
struct aa_audit_rule *rule;
@@ -230,14 +230,14 @@ int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
return -EINVAL;
}
- rule = kzalloc(sizeof(struct aa_audit_rule), GFP_KERNEL);
+ rule = kzalloc(sizeof(struct aa_audit_rule), gfp);
if (!rule)
return -ENOMEM;
/* Currently rules are treated as coming from the root ns */
rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr,
- GFP_KERNEL, true, false);
+ gfp, true, false);
if (IS_ERR(rule->label)) {
int err = PTR_ERR(rule->label);
aa_audit_rule_free(rule);
@@ -264,13 +264,13 @@ int aa_audit_rule_known(struct audit_krule *rule)
return 0;
}
-int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
+int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule)
{
struct aa_audit_rule *rule = vrule;
struct aa_label *label;
int found = 0;
- label = aa_secid_to_label(sid);
+ label = prop->apparmor.label;
if (!label)
return -ENOENT;
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index 9934df16c843..7ca489ee1054 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/security.h>
+#include <linux/timekeeping.h>
#include "include/apparmor.h"
#include "include/capability.h"
@@ -30,8 +31,9 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = {
};
struct audit_cache {
- struct aa_profile *profile;
- kernel_cap_t caps;
+ const struct cred *ad_subj_cred;
+ /* Capabilities go from 0 to CAP_LAST_CAP */
+ u64 ktime_ns_expiration[CAP_LAST_CAP+1];
};
static DEFINE_PER_CPU(struct audit_cache, audit_cache);
@@ -64,6 +66,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile,
int cap, int error)
{
+ const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */
+
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct audit_cache *ent;
@@ -89,15 +93,16 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
/* Do simple duplicate message elimination */
ent = &get_cpu_var(audit_cache);
- if (profile == ent->profile && cap_raised(ent->caps, cap)) {
+ /* If the capability was never raised the timestamp check would also catch that */
+ if (ad->subj_cred == ent->ad_subj_cred && ktime_get_ns() <= ent->ktime_ns_expiration[cap]) {
put_cpu_var(audit_cache);
if (COMPLAIN_MODE(profile))
return complain_error(error);
return error;
} else {
- aa_put_profile(ent->profile);
- ent->profile = aa_get_profile(profile);
- cap_raise(ent->caps, cap);
+ put_cred(ent->ad_subj_cred);
+ ent->ad_subj_cred = get_cred(ad->subj_cred);
+ ent->ktime_ns_expiration[cap] = ktime_get_ns() + AUDIT_CACHE_TIMEOUT_NS;
}
put_cpu_var(audit_cache);
@@ -109,7 +114,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
* @profile: profile being enforced (NOT NULL, NOT unconfined)
* @cap: capability to test if allowed
* @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
- * @ad: audit data (MAY BE NULL indicating no auditing)
+ * @ad: audit data (NOT NULL)
*
* Returns: 0 if allowed else -EPERM
*/
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 571158ec6188..5939bd9a9b9b 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -9,7 +9,6 @@
*/
#include <linux/errno.h>
-#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
@@ -637,6 +636,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new = NULL;
+ struct aa_profile *new_profile = NULL;
const char *info = NULL, *name = NULL, *target = NULL;
aa_state_t state = rules->file->start[AA_CLASS_FILE];
struct aa_perms perms = {};
@@ -681,15 +681,18 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
/* hack ix fallback - improve how this is detected */
goto audit;
} else if (!new) {
- error = -EACCES;
info = "profile transition not found";
- /* remove MAY_EXEC to audit as failure */
+ /* remove MAY_EXEC to audit as failure or complaint */
perms.allow &= ~MAY_EXEC;
+ if (COMPLAIN_MODE(profile)) {
+ /* create null profile instead of failing */
+ goto create_learning_profile;
+ }
+ error = -EACCES;
}
} else if (COMPLAIN_MODE(profile)) {
+create_learning_profile:
/* no exec permission - learning mode */
- struct aa_profile *new_profile = NULL;
-
new_profile = aa_new_learning_profile(profile, false, name,
GFP_KERNEL);
if (!new_profile) {
@@ -710,8 +713,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
if (!(perms.xindex & AA_X_UNSAFE)) {
if (DEBUG_ON) {
- dbg_printk("apparmor: scrubbing environment variables"
- " for %s profile=", name);
+ dbg_printk("apparmor: setting AT_SECURE for %s profile=",
+ name);
aa_label_printk(new, GFP_KERNEL);
dbg_printk("\n");
}
@@ -790,8 +793,8 @@ static int profile_onexec(const struct cred *subj_cred,
if (!(perms.xindex & AA_X_UNSAFE)) {
if (DEBUG_ON) {
- dbg_printk("apparmor: scrubbing environment "
- "variables for %s label=", xname);
+ dbg_printk("apparmor: setting AT_SECURE for %s label=",
+ xname);
aa_label_printk(onexec, GFP_KERNEL);
dbg_printk("\n");
}
@@ -822,33 +825,19 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
AA_BUG(!bprm);
AA_BUG(!buffer);
- if (!stack) {
- error = fn_for_each_in_ns(label, profile,
- profile_onexec(subj_cred, profile, onexec, stack,
- bprm, buffer, cond, unsafe));
- if (error)
- return ERR_PTR(error);
- new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
- aa_get_newest_label(onexec),
- profile_transition(subj_cred, profile, bprm,
- buffer,
- cond, unsafe));
-
- } else {
- /* TODO: determine how much we want to loosen this */
- error = fn_for_each_in_ns(label, profile,
- profile_onexec(subj_cred, profile, onexec, stack, bprm,
- buffer, cond, unsafe));
- if (error)
- return ERR_PTR(error);
- new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
- aa_label_merge(&profile->label, onexec,
- GFP_KERNEL),
- profile_transition(subj_cred, profile, bprm,
- buffer,
- cond, unsafe));
- }
+ /* TODO: determine how much we want to loosen this */
+ error = fn_for_each_in_ns(label, profile,
+ profile_onexec(subj_cred, profile, onexec, stack,
+ bprm, buffer, cond, unsafe));
+ if (error)
+ return ERR_PTR(error);
+ new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
+ stack ? aa_label_merge(&profile->label, onexec,
+ GFP_KERNEL)
+ : aa_get_newest_label(onexec),
+ profile_transition(subj_cred, profile, bprm,
+ buffer, cond, unsafe));
if (new)
return new;
@@ -961,8 +950,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
if (unsafe) {
if (DEBUG_ON) {
- dbg_printk("scrubbing environment variables for %s "
- "label=", bprm->filename);
+ dbg_printk("setting AT_SECURE for %s label=",
+ bprm->filename);
aa_label_printk(new, GFP_KERNEL);
dbg_printk("\n");
}
@@ -972,8 +961,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
if (label->proxy != new->proxy) {
/* when transitioning clear unsafe personality bits */
if (DEBUG_ON) {
- dbg_printk("apparmor: clearing unsafe personality "
- "bits. %s label=", bprm->filename);
+ dbg_printk("apparmor: clearing unsafe personality bits. %s label=",
+ bprm->filename);
aa_label_printk(new, GFP_KERNEL);
dbg_printk("\n");
}
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index c03eb7c19f16..d52a5b14dad4 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -144,19 +144,6 @@ int aa_audit_file(const struct cred *subj_cred,
return aa_audit(type, profile, &ad, file_audit_cb);
}
-/**
- * is_deleted - test if a file has been completely unlinked
- * @dentry: dentry of file to test for deletion (NOT NULL)
- *
- * Returns: true if deleted else false
- */
-static inline bool is_deleted(struct dentry *dentry)
-{
- if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
- return true;
- return false;
-}
-
static int path_name(const char *op, const struct cred *subj_cred,
struct aa_label *label,
const struct path *path, int flags, char *buffer,
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index acbb03b9bd25..e27229349abb 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -200,8 +200,8 @@ static inline int complain_error(int error)
}
void aa_audit_rule_free(void *vrule);
-int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule);
+int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule, gfp_t gfp);
int aa_audit_rule_known(struct audit_krule *rule);
-int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule);
+int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule);
#endif /* __AA_AUDIT_H */
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index 58fdc72af664..7265d2f81dd5 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -63,6 +63,26 @@ static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
return aa_get_newest_label(aa_cred_raw_label(cred));
}
+static inline struct aa_label *aa_get_newest_cred_label_condref(const struct cred *cred,
+ bool *needput)
+{
+ struct aa_label *l = aa_cred_raw_label(cred);
+
+ if (unlikely(label_is_stale(l))) {
+ *needput = true;
+ return aa_get_newest_label(l);
+ }
+
+ *needput = false;
+ return l;
+}
+
+static inline void aa_put_label_condref(struct aa_label *l, bool needput)
+{
+ if (unlikely(needput))
+ aa_put_label(l);
+}
+
/**
* aa_current_raw_label - find the current tasks confining label
*
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index 2a72e6b17d68..93290ae300bb 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -160,31 +160,7 @@ int aa_label_next_confined(struct aa_label *l, int i);
#define label_for_each_cont(I, L, P) \
for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i))
-#define next_comb(I, L1, L2) \
-do { \
- (I).j++; \
- if ((I).j >= (L2)->size) { \
- (I).i++; \
- (I).j = 0; \
- } \
-} while (0)
-
-/* for each combination of P1 in L1, and P2 in L2 */
-#define label_for_each_comb(I, L1, L2, P1, P2) \
-for ((I).i = (I).j = 0; \
- ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \
- (I) = next_comb(I, L1, L2))
-
-#define fn_for_each_comb(L1, L2, P1, P2, FN) \
-({ \
- struct label_it i; \
- int __E = 0; \
- label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \
- last_error(__E, (FN)); \
- } \
- __E; \
-})
/* for each profile that is enforcing confinement in a label */
#define label_for_each_confined(I, L, P) \
@@ -291,8 +267,6 @@ bool aa_label_replace(struct aa_label *old, struct aa_label *new);
bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old,
struct aa_label *new);
-struct aa_label *aa_label_find(struct aa_label *l);
-
struct aa_profile *aa_label_next_in_merge(struct label_it *I,
struct aa_label *a,
struct aa_label *b);
@@ -320,8 +294,6 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
struct aa_label *label, int flags, gfp_t gfp);
void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
gfp_t gfp);
-void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp);
-void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp);
void aa_label_printk(struct aa_label *label, gfp_t gfp);
struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index d7a894b1031f..f11a0db7f51d 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -59,7 +59,6 @@ extern int apparmor_initialized;
/* fn's in lib */
const char *skipn_spaces(const char *str, size_t n);
-char *aa_split_fqname(char *args, char **ns_name);
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
size_t *ns_len);
void aa_info_message(const char *str);
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index 4bb0405c9190..536ce3abd598 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -87,10 +87,12 @@ struct table_header {
char td_data[];
};
-#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data))
+#define TABLE_DATAU16(TABLE) ((u16 *)((TABLE)->td_data))
+#define TABLE_DATAU32(TABLE) ((u32 *)((TABLE)->td_data))
+#define DEFAULT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_DEF]->td_data))
#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data))
-#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data))
-#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data))
+#define NEXT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_NXT]->td_data))
+#define CHECK_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_CHK]->td_data))
#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data))
#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data))
#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data))
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index 67bf888c3bd6..c42ed8a73f1c 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -51,10 +51,9 @@ struct aa_sk_ctx {
struct aa_label *peer;
};
-#define SK_CTX(X) ((X)->sk_security)
static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
{
- return sk->sk_security;
+ return sk->sk_security + apparmor_blob_sizes.lbs_sock;
}
#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
index 0f7e913c3fc2..bbaa7d39a39a 100644
--- a/security/apparmor/include/perms.h
+++ b/security/apparmor/include/perms.h
@@ -213,9 +213,6 @@ void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
void aa_profile_match_label(struct aa_profile *profile,
struct aa_ruleset *rules, struct aa_label *label,
int type, u32 request, struct aa_perms *perms);
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct apparmor_audit_data *ad);
int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
u32 request, struct apparmor_audit_data *ad,
void (*cb)(struct audit_buffer *, void *));
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 75088cc310b6..757e3c232c57 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -264,7 +264,6 @@ void aa_free_profile(struct aa_profile *profile);
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
size_t n);
-struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
const char *fqname, size_t n);
diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h
index a912a5d5d04f..6025d3849cf8 100644
--- a/security/apparmor/include/secid.h
+++ b/security/apparmor/include/secid.h
@@ -25,13 +25,13 @@ struct aa_label;
extern int apparmor_display_secid_mode;
struct aa_label *aa_secid_to_label(u32 secid);
-int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
+int apparmor_secid_to_secctx(u32 secid, struct lsm_context *cp);
+int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp);
int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
-void apparmor_release_secctx(char *secdata, u32 seclen);
+void apparmor_release_secctx(struct lsm_context *cp);
int aa_alloc_secid(struct aa_label *label, gfp_t gfp);
void aa_free_secid(u32 secid);
-void aa_secid_update(u32 secid, struct aa_label *label);
#endif /* __AA_SECID_H */
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index c71e4615dd46..91483ecacc16 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -899,23 +899,6 @@ struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
return vec_create_and_insert_label(vec, len, gfp);
}
-/**
- * aa_label_find - find label @label in label set
- * @label: label to find (NOT NULL)
- *
- * Requires: caller to hold a valid ref on l
- *
- * Returns: refcounted @label if @label is in tree
- * refcounted label that is equiv to @label in tree
- * else NULL if @label or equiv is not in tree
- */
-struct aa_label *aa_label_find(struct aa_label *label)
-{
- AA_BUG(!label);
-
- return vec_find(label->vec, label->size);
-}
-
/**
* aa_label_insert - insert label @label into @ls or return existing label
@@ -1811,22 +1794,6 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
pr_info("%s", label->hname);
}
-void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
-void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
void aa_label_printk(struct aa_label *label, gfp_t gfp)
{
struct aa_ns *ns = aa_get_current_ns();
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index cd569fbbfe36..7db62213e352 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -46,44 +46,6 @@ void aa_free_str_table(struct aa_str_table *t)
}
/**
- * aa_split_fqname - split a fqname into a profile and namespace name
- * @fqname: a full qualified name in namespace profile format (NOT NULL)
- * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
- *
- * Returns: profile name or NULL if one is not specified
- *
- * Split a namespace name from a profile name (see policy.c for naming
- * description). If a portion of the name is missing it returns NULL for
- * that portion.
- *
- * NOTE: may modify the @fqname string. The pointers returned point
- * into the @fqname string.
- */
-char *aa_split_fqname(char *fqname, char **ns_name)
-{
- char *name = strim(fqname);
-
- *ns_name = NULL;
- if (name[0] == ':') {
- char *split = strchr(&name[1], ':');
- *ns_name = skip_spaces(&name[1]);
- if (split) {
- /* overwrite ':' with \0 */
- *split++ = 0;
- if (strncmp(split, "//", 2) == 0)
- split += 2;
- name = skip_spaces(split);
- } else
- /* a ns name without a following profile is allowed */
- name = NULL;
- }
- if (name && *name == 0)
- name = NULL;
-
- return name;
-}
-
-/**
* skipn_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
* @n: length of str to parse, will stop at \0 if encountered before n
@@ -276,33 +238,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
}
/**
- * aa_audit_perms_cb - generic callback fn for auditing perms
- * @ab: audit buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
- */
-static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
-{
- struct common_audit_data *sa = va;
- struct apparmor_audit_data *ad = aad(sa);
-
- if (ad->request) {
- audit_log_format(ab, " requested_mask=");
- aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- if (ad->denied) {
- audit_log_format(ab, "denied_mask=");
- aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
- FLAGS_NONE, GFP_ATOMIC);
-}
-
-/**
* aa_apply_modes_to_perms - apply namespace and profile flags to perms
* @profile: that perms where computed from
* @perms: perms to apply mode modifiers to
@@ -349,25 +284,6 @@ void aa_profile_match_label(struct aa_profile *profile,
}
-/* currently unused */
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct apparmor_audit_data *ad)
-{
- struct aa_ruleset *rules = list_first_entry(&profile->rules,
- typeof(*rules), list);
- struct aa_perms perms;
-
- ad->peer = &target->label;
- ad->request = request;
-
- aa_profile_match_label(profile, rules, &target->label, type, request,
- &perms);
- aa_apply_modes_to_perms(profile, &perms);
- *deny |= request & perms.deny;
- return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb);
-}
-
/**
* aa_check_perms - do audit mode selection based on perms set
* @profile: profile being checked
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index cef8c466af80..9b6c2f157f83 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -461,6 +461,7 @@ static int apparmor_file_open(struct file *file)
struct aa_file_ctx *fctx = file_ctx(file);
struct aa_label *label;
int error = 0;
+ bool needput;
if (!path_mediated_fs(file->f_path.dentry))
return 0;
@@ -477,7 +478,7 @@ static int apparmor_file_open(struct file *file)
return 0;
}
- label = aa_get_newest_cred_label(file->f_cred);
+ label = aa_get_newest_cred_label_condref(file->f_cred, &needput);
if (!unconfined(label)) {
struct mnt_idmap *idmap = file_mnt_idmap(file);
struct inode *inode = file_inode(file);
@@ -494,7 +495,7 @@ static int apparmor_file_open(struct file *file)
/* todo cache full allowed permissions set and state */
fctx->allow = aa_map_file_to_perms(file);
}
- aa_put_label(label);
+ aa_put_label_condref(label, needput);
return error;
}
@@ -981,17 +982,20 @@ static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm)
return;
}
-static void apparmor_current_getsecid_subj(u32 *secid)
+static void apparmor_current_getlsmprop_subj(struct lsm_prop *prop)
{
struct aa_label *label = __begin_current_label_crit_section();
- *secid = label->secid;
+
+ prop->apparmor.label = label;
__end_current_label_crit_section(label);
}
-static void apparmor_task_getsecid_obj(struct task_struct *p, u32 *secid)
+static void apparmor_task_getlsmprop_obj(struct task_struct *p,
+ struct lsm_prop *prop)
{
struct aa_label *label = aa_get_task_label(p);
- *secid = label->secid;
+
+ prop->apparmor.label = label;
aa_put_label(label);
}
@@ -1057,27 +1061,12 @@ static int apparmor_userns_create(const struct cred *cred)
return error;
}
-static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
-{
- struct aa_sk_ctx *ctx;
-
- ctx = kzalloc(sizeof(*ctx), flags);
- if (!ctx)
- return -ENOMEM;
-
- sk->sk_security = ctx;
-
- return 0;
-}
-
static void apparmor_sk_free_security(struct sock *sk)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
- sk->sk_security = NULL;
aa_put_label(ctx->label);
aa_put_label(ctx->peer);
- kfree(ctx);
}
/**
@@ -1124,7 +1113,7 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern)
* @sock: socket that is being setup
* @family: family of socket being created
* @type: type of the socket
- * @ptotocol: protocol of the socket
+ * @protocol: protocol of the socket
* @kern: socket is a special kernel socket
*
* Note:
@@ -1304,6 +1293,13 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (!skb->secmark)
return 0;
+ /*
+ * If reach here before socket_post_create hook is called, in which
+ * case label is null, drop the packet.
+ */
+ if (!ctx->label)
+ return -EACCES;
+
return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE,
skb->secmark, sk);
}
@@ -1425,6 +1421,7 @@ struct lsm_blob_sizes apparmor_blob_sizes __ro_after_init = {
.lbs_cred = sizeof(struct aa_label *),
.lbs_file = sizeof(struct aa_file_ctx),
.lbs_task = sizeof(struct aa_task_ctx),
+ .lbs_sock = sizeof(struct aa_sk_ctx),
};
static const struct lsm_id apparmor_lsmid = {
@@ -1470,7 +1467,6 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
- LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
@@ -1510,8 +1506,9 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(task_free, apparmor_task_free),
LSM_HOOK_INIT(task_alloc, apparmor_task_alloc),
- LSM_HOOK_INIT(current_getsecid_subj, apparmor_current_getsecid_subj),
- LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid_obj),
+ LSM_HOOK_INIT(current_getlsmprop_subj,
+ apparmor_current_getlsmprop_subj),
+ LSM_HOOK_INIT(task_getlsmprop_obj, apparmor_task_getlsmprop_obj),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
LSM_HOOK_INIT(task_kill, apparmor_task_kill),
LSM_HOOK_INIT(userns_create, apparmor_userns_create),
@@ -1524,6 +1521,7 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
#endif
LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx),
+ LSM_HOOK_INIT(lsmprop_to_secctx, apparmor_lsmprop_to_secctx),
LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid),
LSM_HOOK_INIT(release_secctx, apparmor_release_secctx),
@@ -2029,7 +2027,7 @@ static int __init alloc_buffers(void)
}
#ifdef CONFIG_SYSCTL
-static int apparmor_dointvec(struct ctl_table *table, int write,
+static int apparmor_dointvec(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
if (!aa_current_policy_admin_capable(NULL))
@@ -2040,7 +2038,7 @@ static int apparmor_dointvec(struct ctl_table *table, int write,
return proc_dointvec(table, write, buffer, lenp, ppos);
}
-static struct ctl_table apparmor_sysctl_table[] = {
+static const struct ctl_table apparmor_sysctl_table[] = {
#ifdef CONFIG_USER_NS
{
.procname = "unprivileged_userns_apparmor_policy",
@@ -2064,7 +2062,6 @@ static struct ctl_table apparmor_sysctl_table[] = {
.mode = 0600,
.proc_handler = apparmor_dointvec,
},
- { }
};
static int __init apparmor_init_sysctl(void)
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 517d77d3c34c..f2d9c57f8794 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -247,6 +247,42 @@ void aa_dfa_free_kref(struct kref *kref)
dfa_free(dfa);
}
+
+
+/**
+ * remap_data16_to_data32 - remap u16 @old table to a u32 based table
+ * @old: table to remap
+ *
+ * Returns: new table with u32 entries instead of u16.
+ *
+ * Note: will free @old so caller does not have to
+ */
+static struct table_header *remap_data16_to_data32(struct table_header *old)
+{
+ struct table_header *new;
+ size_t tsize;
+ u32 i;
+
+ tsize = table_size(old->td_lolen, YYTD_DATA32);
+ new = kvzalloc(tsize, GFP_KERNEL);
+ if (!new) {
+ kvfree(old);
+ return NULL;
+ }
+ new->td_id = old->td_id;
+ new->td_flags = YYTD_DATA32;
+ new->td_lolen = old->td_lolen;
+
+ for (i = 0; i < old->td_lolen; i++)
+ TABLE_DATAU32(new)[i] = (u32) TABLE_DATAU16(old)[i];
+
+ kvfree(old);
+ if (is_vmalloc_addr(new))
+ vm_unmap_aliases();
+
+ return new;
+}
+
/**
* aa_dfa_unpack - unpack the binary tables of a serialized dfa
* @blob: aligned serialized stream of data to unpack (NOT NULL)
@@ -326,8 +362,10 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
case YYTD_ID_DEF:
case YYTD_ID_NXT:
case YYTD_ID_CHK:
- if (table->td_flags != YYTD_DATA16)
+ if (!(table->td_flags == YYTD_DATA16 ||
+ table->td_flags == YYTD_DATA32)) {
goto fail;
+ }
break;
case YYTD_ID_EC:
if (table->td_flags != YYTD_DATA8)
@@ -342,6 +380,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
dfa->tables[table->td_id] = table;
data += table_size(table->td_lolen, table->td_flags);
size -= table_size(table->td_lolen, table->td_flags);
+
+ /*
+ * this remapping has to be done after incrementing data above
+ * for now straight remap, later have dfa support both
+ */
+ switch (table->td_id) {
+ case YYTD_ID_DEF:
+ case YYTD_ID_NXT:
+ case YYTD_ID_CHK:
+ if (table->td_flags == YYTD_DATA16) {
+ table = remap_data16_to_data32(table);
+ if (!table)
+ goto fail;
+ }
+ dfa->tables[table->td_id] = table;
+ break;
+ }
table = NULL;
}
error = verify_table_headers(dfa->tables, flags);
@@ -395,10 +450,10 @@ do { \
aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
const char *str, int len)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
aa_state_t state = start;
if (state == DFA_NOMATCH)
@@ -434,10 +489,10 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
*/
aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
aa_state_t state = start;
if (state == DFA_NOMATCH)
@@ -472,10 +527,10 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
*/
aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) {
@@ -490,10 +545,10 @@ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
u32 b = (base)[(state)];
if (!(b & MATCH_FLAG_OOB_TRANSITION))
@@ -521,10 +576,10 @@ aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, const char **retpos)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
u32 *accept = ACCEPT_TABLE(dfa);
aa_state_t state = start, pos;
@@ -582,10 +637,10 @@ aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, int n, const char **retpos)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
u32 *accept = ACCEPT_TABLE(dfa);
aa_state_t state = start, pos;
@@ -658,10 +713,10 @@ static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,
const char *str, struct match_workbuf *wb,
unsigned int *count)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
aa_state_t state = start, pos;
AA_BUG(!dfa);
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index 49fe8da6fea4..bf8863253e07 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -44,6 +44,8 @@ static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
audit_log_format(ab, ", mand");
if (flags & MS_DIRSYNC)
audit_log_format(ab, ", dirsync");
+ if (flags & MS_NOSYMFOLLOW)
+ audit_log_format(ab, ", nosymfollow");
if (flags & MS_NOATIME)
audit_log_format(ab, ", noatime");
if (flags & MS_NODIRATIME)
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index 87e934b2b548..77413a519117 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -151,7 +151,7 @@ static int aa_label_sk_perm(const struct cred *subj_cred,
const char *op, u32 request,
struct sock *sk)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
int error = 0;
AA_BUG(!label);
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 45ec994b558d..d6c74c357ffd 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -130,7 +130,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
/* handle error conditions - and still allow a partial path to
* be returned.
*/
- if (!res || IS_ERR(res)) {
+ if (IS_ERR_OR_NULL(res)) {
if (PTR_ERR(res) == -ENAMETOOLONG) {
error = -ENAMETOOLONG;
*name = buf;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 957654d253dd..d0244fab0653 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -103,8 +103,7 @@ static void aa_free_pdb(struct aa_policydb *pdb)
{
if (pdb) {
aa_put_dfa(pdb->dfa);
- if (pdb->perms)
- kvfree(pdb->perms);
+ kvfree(pdb->perms);
aa_free_str_table(&pdb->trans);
kfree(pdb);
}
@@ -225,7 +224,7 @@ static void aa_free_data(void *ptr, void *arg)
{
struct aa_data *data = ptr;
- kfree_sensitive(data->data);
+ kvfree_sensitive(data->data, data->size);
kfree_sensitive(data->key);
kfree_sensitive(data);
}
@@ -580,11 +579,6 @@ struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
return profile;
}
-struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname)
-{
- return aa_lookupn_profile(ns, hname, strlen(hname));
-}
-
struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
const char *fqname, size_t n)
{
@@ -626,6 +620,7 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
/* TODO: ideally we should inherit abi from parent */
profile->label.flags |= FLAG_NULL;
+ profile->attach.xmatch = aa_get_pdb(nullpdb);
rules = list_first_entry(&profile->rules, typeof(*rules), list);
rules->file = aa_get_pdb(nullpdb);
rules->policy = aa_get_pdb(nullpdb);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 5e578ef0ddff..992b74c50d64 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -13,7 +13,7 @@
* All policy is validated before it is used.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <kunit/visibility.h>
#include <linux/ctype.h>
#include <linux/errno.h>
@@ -645,10 +645,13 @@ fail:
static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm)
{
+ u32 reserved;
+
if (version != 1)
return false;
- return aa_unpack_u32(e, &perm->allow, NULL) &&
+ /* reserved entry is for later expansion, discard for now */
+ return aa_unpack_u32(e, &reserved, NULL) &&
aa_unpack_u32(e, &perm->allow, NULL) &&
aa_unpack_u32(e, &perm->deny, NULL) &&
aa_unpack_u32(e, &perm->subtree, NULL) &&
@@ -747,34 +750,42 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
*info = "missing required dfa";
goto fail;
}
- goto out;
+ } else {
+ /*
+ * only unpack the following if a dfa is present
+ *
+ * sadly start was given different names for file and policydb
+ * but since it is optional we can try both
+ */
+ if (!aa_unpack_u32(e, &pdb->start[0], "start"))
+ /* default start state */
+ pdb->start[0] = DFA_START;
+ 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 */
+ for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
+ pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
+ i);
+ }
}
/*
- * only unpack the following if a dfa is present
- *
- * sadly start was given different names for file and policydb
- * but since it is optional we can try both
+ * Unfortunately due to a bug in earlier userspaces, a
+ * transition table may be present even when the dfa is
+ * not. For compatibility reasons unpack and discard.
*/
- if (!aa_unpack_u32(e, &pdb->start[0], "start"))
- /* default start state */
- pdb->start[0] = DFA_START;
- 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 */
- for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
- pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
- i);
- }
if (!unpack_trans_table(e, &pdb->trans) && required_trans) {
*info = "failed to unpack profile transition table";
goto fail;
}
+ if (!pdb->dfa && pdb->trans.table)
+ aa_free_str_table(&pdb->trans);
+
/* TODO: move compat mapping here, requires dfa merging first */
/* TODO: move verify here, it has to be done after compat mappings */
-out:
+
*policy = pdb;
return 0;
@@ -1071,6 +1082,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (rhashtable_insert_fast(profile->data, &data->head,
profile->data->p)) {
+ kvfree_sensitive(data->data, data->size);
kfree_sensitive(data->key);
kfree_sensitive(data);
info = "failed to insert data to table";
diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c
index 5c9bde25e56d..5b2ba88ae9e2 100644
--- a/security/apparmor/policy_unpack_test.c
+++ b/security/apparmor/policy_unpack_test.c
@@ -44,7 +44,7 @@
#define TEST_ARRAY_BUF_OFFSET \
(TEST_NAMED_ARRAY_BUF_OFFSET + 3 + strlen(TEST_ARRAY_NAME) + 1)
-MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
struct policy_unpack_fixture {
struct aa_ext *e;
@@ -80,14 +80,14 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
*(buf + 1) = strlen(TEST_U32_NAME) + 1;
strscpy(buf + 3, TEST_U32_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32;
- *((u32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = TEST_U32_DATA;
+ *((__le32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = cpu_to_le32(TEST_U32_DATA);
buf = e->start + TEST_NAMED_U64_BUF_OFFSET;
*buf = AA_NAME;
*(buf + 1) = strlen(TEST_U64_NAME) + 1;
strscpy(buf + 3, TEST_U64_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_U64_NAME) + 1) = AA_U64;
- *((u64 *)(buf + 3 + strlen(TEST_U64_NAME) + 2)) = TEST_U64_DATA;
+ *((__le64 *)(buf + 3 + strlen(TEST_U64_NAME) + 2)) = cpu_to_le64(TEST_U64_DATA);
buf = e->start + TEST_NAMED_BLOB_BUF_OFFSET;
*buf = AA_NAME;
@@ -103,7 +103,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
*(buf + 1) = strlen(TEST_ARRAY_NAME) + 1;
strscpy(buf + 3, TEST_ARRAY_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY;
- *((u16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = TEST_ARRAY_SIZE;
+ *((__le16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = cpu_to_le16(TEST_ARRAY_SIZE);
return e;
}
@@ -281,6 +281,8 @@ static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test)
((uintptr_t)puf->e->start <= (uintptr_t)string)
&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA);
+
+ kfree(string);
}
static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test)
@@ -296,6 +298,8 @@ static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test)
((uintptr_t)puf->e->start <= (uintptr_t)string)
&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA);
+
+ kfree(string);
}
static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test)
@@ -313,6 +317,8 @@ static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test)
KUNIT_EXPECT_EQ(test, size, 0);
KUNIT_EXPECT_NULL(test, string);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start);
+
+ kfree(string);
}
static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test)
@@ -604,4 +610,5 @@ static struct kunit_suite apparmor_policy_unpack_test_module = {
kunit_test_suite(apparmor_policy_unpack_test_module);
+MODULE_DESCRIPTION("KUnit tests for AppArmor's policy unpack");
MODULE_LICENSE("GPL");
diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c
index 83d3d1e6d9dc..28caf66b9033 100644
--- a/security/apparmor/secid.c
+++ b/security/apparmor/secid.c
@@ -39,20 +39,6 @@ int apparmor_display_secid_mode;
* TODO: use secid_update in label replace
*/
-/**
- * aa_secid_update - update a secid mapping to a new label
- * @secid: secid to update
- * @label: label the secid will now map to
- */
-void aa_secid_update(u32 secid, struct aa_label *label)
-{
- unsigned long flags;
-
- xa_lock_irqsave(&aa_secids, flags);
- __xa_store(&aa_secids, secid, label, 0);
- xa_unlock_irqrestore(&aa_secids, flags);
-}
-
/*
* see label for inverse aa_label_to_secid
*/
@@ -61,23 +47,21 @@ struct aa_label *aa_secid_to_label(u32 secid)
return xa_load(&aa_secids, secid);
}
-int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+static int apparmor_label_to_secctx(struct aa_label *label,
+ struct lsm_context *cp)
{
/* TODO: cache secctx and ref count so we don't have to recreate */
- struct aa_label *label = aa_secid_to_label(secid);
int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT;
int len;
- AA_BUG(!seclen);
-
if (!label)
return -EINVAL;
if (apparmor_display_secid_mode)
flags |= FLAG_SHOW_MODE;
- if (secdata)
- len = aa_label_asxprint(secdata, root_ns, label,
+ if (cp)
+ len = aa_label_asxprint(&cp->context, root_ns, label,
flags, GFP_ATOMIC);
else
len = aa_label_snxprint(NULL, 0, root_ns, label, flags);
@@ -85,9 +69,28 @@ int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
if (len < 0)
return -ENOMEM;
- *seclen = len;
+ if (cp) {
+ cp->len = len;
+ cp->id = LSM_ID_APPARMOR;
+ }
- return 0;
+ return len;
+}
+
+int apparmor_secid_to_secctx(u32 secid, struct lsm_context *cp)
+{
+ struct aa_label *label = aa_secid_to_label(secid);
+
+ return apparmor_label_to_secctx(label, cp);
+}
+
+int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp)
+{
+ struct aa_label *label;
+
+ label = prop->apparmor.label;
+
+ return apparmor_label_to_secctx(label, cp);
}
int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
@@ -103,9 +106,13 @@ int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
return 0;
}
-void apparmor_release_secctx(char *secdata, u32 seclen)
+void apparmor_release_secctx(struct lsm_context *cp)
{
- kfree(secdata);
+ if (cp->id == LSM_ID_APPARMOR) {
+ kfree(cp->context);
+ cp->context = NULL;
+ cp->id = LSM_ID_UNDEF;
+ }
}
/**