inode->i_ino = get_next_ino();
inode->i_mode = mode;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
inode->i_private = data;
if (S_ISDIR(mode)) {
inode->i_op = iops ? iops : &simple_dir_inode_operations;
/* high level check about policy management - fine grained in
* below after unpack
*/
- error = aa_may_manage_policy(label, ns, mask);
+ error = aa_may_manage_policy(current_cred(), label, ns, mask);
if (error)
goto end_section;
/* high level check about policy management - fine grained in
* below after unpack
*/
- error = aa_may_manage_policy(label, ns, AA_MAY_REMOVE_POLICY);
+ error = aa_may_manage_policy(current_cred(), label, ns,
+ AA_MAY_REMOVE_POLICY);
if (error)
goto out;
if (profile_unconfined(profile))
return;
- if (rules->file.dfa && *match_str == AA_CLASS_FILE) {
- state = aa_dfa_match_len(rules->file.dfa,
- rules->file.start[AA_CLASS_FILE],
+ if (rules->file->dfa && *match_str == AA_CLASS_FILE) {
+ state = aa_dfa_match_len(rules->file->dfa,
+ rules->file->start[AA_CLASS_FILE],
match_str + 1, match_len - 1);
if (state) {
struct path_cond cond = { };
- tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
+ tmp = *(aa_lookup_fperms(rules->file, state, &cond));
}
- } else if (rules->policy.dfa) {
+ } else if (rules->policy->dfa) {
if (!RULE_MEDIATES(rules, *match_str))
return; /* no change to current perms */
- state = aa_dfa_match_len(rules->policy.dfa,
- rules->policy.start[0],
+ state = aa_dfa_match_len(rules->policy->dfa,
+ rules->policy->start[0],
match_str, match_len);
if (state)
- tmp = *aa_lookup_perms(&rules->policy, state);
+ tmp = *aa_lookup_perms(rules->policy, state);
}
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum_raw(perms, &tmp);
struct aa_profile *profile = labels_profile(label);
if (profile->attach.xmatch_str)
seq_printf(seq, "%s\n", profile->attach.xmatch_str);
- else if (profile->attach.xmatch.dfa)
+ else if (profile->attach.xmatch->dfa)
seq_puts(seq, "<unknown>\n");
else
seq_printf(seq, "%s\n", profile->base.name);
static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
{
- #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
if (slen < dlen) {
const size_t wksp_len = zstd_dctx_workspace_bound();
zstd_dctx *ctx;
kvfree(wksp);
return ret;
}
- #endif
if (dlen < slen)
return -EINVAL;
for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
new->dents[i] = old->dents[i];
- if (new->dents[i])
- new->dents[i]->d_inode->i_mtime = current_time(new->dents[i]->d_inode);
+ if (new->dents[i]) {
+ struct inode *inode = d_inode(new->dents[i]);
+
+ inode_set_mtime_to_ts(inode,
+ inode_set_ctime_current(inode));
+ }
old->dents[i] = NULL;
}
}
int error;
label = begin_current_label_crit_section();
- error = aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY);
+ error = aa_may_manage_policy(current_cred(), label, NULL,
+ AA_MAY_LOAD_POLICY);
end_current_label_crit_section(label);
if (error)
return error;
int error;
label = begin_current_label_crit_section();
- error = aa_may_manage_policy(label, NULL, AA_MAY_LOAD_POLICY);
+ error = aa_may_manage_policy(current_cred(), label, NULL,
+ AA_MAY_LOAD_POLICY);
end_current_label_crit_section(label);
if (error)
return error;
AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1),
AA_SFS_FILE_BOOLEAN("computed_longest_left", 1),
AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach),
+ AA_SFS_FILE_BOOLEAN("disconnected.path", 1),
AA_SFS_FILE_STRING("version", "1.2"),
{ }
};
+ static struct aa_sfs_entry aa_sfs_entry_unconfined[] = {
+ AA_SFS_FILE_BOOLEAN("change_profile", 1),
+ { }
+ };
+
static struct aa_sfs_entry aa_sfs_entry_versions[] = {
AA_SFS_FILE_BOOLEAN("v5", 1),
AA_SFS_FILE_BOOLEAN("v6", 1),
{ }
};
+ #define PERMS32STR "allow deny subtree cond kill complain prompt audit quiet hide xindex tag label"
static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_DIR("versions", aa_sfs_entry_versions),
AA_SFS_FILE_BOOLEAN("set_load", 1),
/* number of out of band transitions supported */
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
+ AA_SFS_FILE_U64("permstable32_version", 1),
+ AA_SFS_FILE_STRING("permstable32", PERMS32STR),
+ AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
{ }
};
static struct aa_sfs_entry aa_sfs_entry_ns[] = {
AA_SFS_FILE_BOOLEAN("profile", 1),
AA_SFS_FILE_BOOLEAN("pivot_root", 0),
+ AA_SFS_FILE_STRING("mask", "userns_create"),
{ }
};
AA_SFS_DIR("label", aa_sfs_entry_query_label),
{ }
};
+
+ static struct aa_sfs_entry aa_sfs_entry_io_uring[] = {
+ AA_SFS_FILE_STRING("mask", "sqpoll override_creds"),
+ { }
+ };
+
static struct aa_sfs_entry aa_sfs_entry_features[] = {
AA_SFS_DIR("policy", aa_sfs_entry_policy),
AA_SFS_DIR("domain", aa_sfs_entry_domain),
AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
AA_SFS_DIR("signal", aa_sfs_entry_signal),
AA_SFS_DIR("query", aa_sfs_entry_query),
+ AA_SFS_DIR("io_uring", aa_sfs_entry_io_uring),
{ }
};
inode->i_ino = get_next_ino();
inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO;
- inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+ simple_inode_init_ts(inode);
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO,
MKDEV(MEM_MAJOR, 3));
d_instantiate(dentry, inode);
DECLARE_FLEX_ARRAY(char, buffer);
};
+ struct aa_local_cache {
+ unsigned int hold;
+ unsigned int count;
+ struct list_head head;
+ };
+
#define RESERVE_COUNT 2
static int reserve_count = RESERVE_COUNT;
static int buffer_count;
static LIST_HEAD(aa_global_buffers);
static DEFINE_SPINLOCK(aa_buffers_lock);
+ static DEFINE_PER_CPU(struct aa_local_cache, aa_local_buffers);
/*
* LSM hook functions
unsigned int mode)
{
struct aa_label *tracer, *tracee;
+ const struct cred *cred;
int error;
+ cred = get_task_cred(child);
+ tracee = cred_label(cred); /* ref count on cred */
tracer = __begin_current_label_crit_section();
- tracee = aa_get_task_label(child);
- error = aa_may_ptrace(tracer, tracee,
+ error = aa_may_ptrace(current_cred(), tracer, cred, tracee,
(mode & PTRACE_MODE_READ) ? AA_PTRACE_READ
: AA_PTRACE_TRACE);
- aa_put_label(tracee);
__end_current_label_crit_section(tracer);
+ put_cred(cred);
return error;
}
static int apparmor_ptrace_traceme(struct task_struct *parent)
{
struct aa_label *tracer, *tracee;
+ const struct cred *cred;
int error;
tracee = __begin_current_label_crit_section();
- tracer = aa_get_task_label(parent);
- error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE);
- aa_put_label(tracer);
+ cred = get_task_cred(parent);
+ tracer = cred_label(cred); /* ref count on cred */
+ error = aa_may_ptrace(cred, tracer, current_cred(), tracee,
+ AA_PTRACE_TRACE);
+ put_cred(cred);
__end_current_label_crit_section(tracee);
return error;
}
/* Derived from security/commoncap.c:cap_capget */
-static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
+static int apparmor_capget(const struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
struct aa_label *label;
label = aa_get_newest_cred_label(cred);
if (!unconfined(label))
- error = aa_capable(label, cap, opts);
+ error = aa_capable(cred, label, cap, opts);
aa_put_label(label);
return error;
label = __begin_current_label_crit_section();
if (!unconfined(label))
- error = aa_path_perm(op, label, path, 0, mask, cond);
+ error = aa_path_perm(op, current_cred(), label, path, 0, mask,
+ cond);
__end_current_label_crit_section(label);
return error;
label = begin_current_label_crit_section();
if (!unconfined(label))
- error = aa_path_link(label, old_dentry, new_dir, new_dentry);
+ error = aa_path_link(current_cred(), label, old_dentry, new_dir,
+ new_dentry);
end_current_label_crit_section(label);
return error;
vfsuid = i_uid_into_vfsuid(idmap, d_backing_inode(old_dentry));
cond_exchange.uid = vfsuid_into_kuid(vfsuid);
- error = aa_path_perm(OP_RENAME_SRC, label, &new_path, 0,
+ error = aa_path_perm(OP_RENAME_SRC, current_cred(),
+ label, &new_path, 0,
MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
AA_MAY_SETATTR | AA_MAY_DELETE,
&cond_exchange);
if (!error)
- error = aa_path_perm(OP_RENAME_DEST, label, &old_path,
+ error = aa_path_perm(OP_RENAME_DEST, current_cred(),
+ label, &old_path,
0, MAY_WRITE | AA_MAY_SETATTR |
AA_MAY_CREATE, &cond_exchange);
}
if (!error)
- error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
+ error = aa_path_perm(OP_RENAME_SRC, current_cred(),
+ label, &old_path, 0,
MAY_READ | AA_MAY_GETATTR | MAY_WRITE |
AA_MAY_SETATTR | AA_MAY_DELETE,
&cond);
if (!error)
- error = aa_path_perm(OP_RENAME_DEST, label, &new_path,
+ error = aa_path_perm(OP_RENAME_DEST, current_cred(),
+ label, &new_path,
0, MAY_WRITE | AA_MAY_SETATTR |
AA_MAY_CREATE, &cond);
vfsuid = i_uid_into_vfsuid(idmap, inode);
cond.uid = vfsuid_into_kuid(vfsuid);
- error = aa_path_perm(OP_OPEN, label, &file->f_path, 0,
+ error = aa_path_perm(OP_OPEN, file->f_cred,
+ label, &file->f_path, 0,
aa_map_file_to_perms(file), &cond);
/* todo cache full allowed permissions set and state */
fctx->allow = aa_map_file_to_perms(file);
return -EACCES;
label = __begin_current_label_crit_section();
- error = aa_file_perm(op, label, file, mask, in_atomic);
+ error = aa_file_perm(op, current_cred(), label, file, mask, in_atomic);
__end_current_label_crit_section(label);
return error;
false);
}
+ #ifdef CONFIG_IO_URING
+ static const char *audit_uring_mask(u32 mask)
+ {
+ if (mask & AA_MAY_CREATE_SQPOLL)
+ return "sqpoll";
+ if (mask & AA_MAY_OVERRIDE_CRED)
+ return "override_creds";
+ return "";
+ }
+
+ static void audit_uring_cb(struct audit_buffer *ab, void *va)
+ {
+ struct apparmor_audit_data *ad = aad_of_va(va);
+
+ if (ad->request & AA_URING_PERM_MASK) {
+ audit_log_format(ab, " requested=\"%s\"",
+ audit_uring_mask(ad->request));
+ if (ad->denied & AA_URING_PERM_MASK) {
+ audit_log_format(ab, " denied=\"%s\"",
+ audit_uring_mask(ad->denied));
+ }
+ }
+ if (ad->uring.target) {
+ audit_log_format(ab, " tcontext=");
+ aa_label_xaudit(ab, labels_ns(ad->subj_label),
+ ad->uring.target,
+ FLAGS_NONE, GFP_ATOMIC);
+ }
+ }
+
+ static int profile_uring(struct aa_profile *profile, u32 request,
+ struct aa_label *new, int cap,
+ struct apparmor_audit_data *ad)
+ {
+ unsigned int state;
+ struct aa_ruleset *rules;
+ int error = 0;
+
+ AA_BUG(!profile);
+
+ rules = list_first_entry(&profile->rules, typeof(*rules), list);
+ state = RULE_MEDIATES(rules, AA_CLASS_IO_URING);
+ if (state) {
+ struct aa_perms perms = { };
+
+ if (new) {
+ aa_label_match(profile, rules, new, state,
+ false, request, &perms);
+ } else {
+ perms = *aa_lookup_perms(rules->policy, state);
+ }
+ aa_apply_modes_to_perms(profile, &perms);
+ error = aa_check_perms(profile, &perms, request, ad,
+ audit_uring_cb);
+ }
+
+ return error;
+ }
+
+ /**
+ * apparmor_uring_override_creds - check the requested cred override
+ * @new: the target creds
+ *
+ * Check to see if the current task is allowed to override it's credentials
+ * to service an io_uring operation.
+ */
+ static int apparmor_uring_override_creds(const struct cred *new)
+ {
+ struct aa_profile *profile;
+ struct aa_label *label;
+ int error;
+ DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING,
+ OP_URING_OVERRIDE);
+
+ ad.uring.target = cred_label(new);
+ label = __begin_current_label_crit_section();
+ error = fn_for_each(label, profile,
+ profile_uring(profile, AA_MAY_OVERRIDE_CRED,
+ cred_label(new), CAP_SYS_ADMIN, &ad));
+ __end_current_label_crit_section(label);
+
+ return error;
+ }
+
+ /**
+ * apparmor_uring_sqpoll - check if a io_uring polling thread can be created
+ *
+ * Check to see if the current task is allowed to create a new io_uring
+ * kernel polling thread.
+ */
+ static int apparmor_uring_sqpoll(void)
+ {
+ struct aa_profile *profile;
+ struct aa_label *label;
+ int error;
+ DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_IO_URING,
+ OP_URING_SQPOLL);
+
+ label = __begin_current_label_crit_section();
+ error = fn_for_each(label, profile,
+ profile_uring(profile, AA_MAY_CREATE_SQPOLL,
+ NULL, CAP_SYS_ADMIN, &ad));
+ __end_current_label_crit_section(label);
+
+ return error;
+ }
+ #endif /* CONFIG_IO_URING */
+
static int apparmor_sb_mount(const char *dev_name, const struct path *path,
const char *type, unsigned long flags, void *data)
{
label = __begin_current_label_crit_section();
if (!unconfined(label)) {
if (flags & MS_REMOUNT)
- error = aa_remount(label, path, flags, data);
+ error = aa_remount(current_cred(), label, path, flags,
+ data);
else if (flags & MS_BIND)
- error = aa_bind_mount(label, path, dev_name, flags);
+ error = aa_bind_mount(current_cred(), label, path,
+ dev_name, flags);
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
MS_UNBINDABLE))
- error = aa_mount_change_type(label, path, flags);
+ error = aa_mount_change_type(current_cred(), label,
+ path, flags);
else if (flags & MS_MOVE)
- error = aa_move_mount(label, path, dev_name);
+ error = aa_move_mount_old(current_cred(), label, path,
+ dev_name);
else
- error = aa_new_mount(label, dev_name, path, type,
- flags, data);
+ error = aa_new_mount(current_cred(), label, dev_name,
+ path, type, flags, data);
}
__end_current_label_crit_section(label);
return error;
}
+ static int apparmor_move_mount(const struct path *from_path,
+ const struct path *to_path)
+ {
+ struct aa_label *label;
+ int error = 0;
+
+ label = __begin_current_label_crit_section();
+ if (!unconfined(label))
+ error = aa_move_mount(current_cred(), label, from_path,
+ to_path);
+ __end_current_label_crit_section(label);
+
+ return error;
+ }
+
static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
{
struct aa_label *label;
label = __begin_current_label_crit_section();
if (!unconfined(label))
- error = aa_umount(label, mnt, flags);
+ error = aa_umount(current_cred(), label, mnt, flags);
__end_current_label_crit_section(label);
return error;
label = aa_get_current_label();
if (!unconfined(label))
- error = aa_pivotroot(label, old_path, new_path);
+ error = aa_pivotroot(current_cred(), label, old_path, new_path);
aa_put_label(label);
return error;
char *command, *largs = NULL, *args = value;
size_t arg_size;
int error;
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE,
+ DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE,
OP_SETPROCATTR);
if (size == 0)
return error;
fail:
- aad(&sa)->label = begin_current_label_crit_section();
- aad(&sa)->info = name;
- aad(&sa)->error = error = -EINVAL;
- aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
- end_current_label_crit_section(aad(&sa)->label);
+ ad.subj_label = begin_current_label_crit_section();
+ ad.info = name;
+ ad.error = error = -EINVAL;
+ aa_audit_msg(AUDIT_APPARMOR_DENIED, &ad, NULL);
+ end_current_label_crit_section(ad.subj_label);
goto out;
}
* apparmor_bprm_committing_creds - do task cleanup on committing new creds
* @bprm: binprm for the exec (NOT NULL)
*/
-static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
+static void apparmor_bprm_committing_creds(const struct linux_binprm *bprm)
{
struct aa_label *label = aa_current_raw_label();
struct aa_label *new_label = cred_label(bprm->cred);
* apparmor_bprm_committed_creds() - do cleanup after new creds committed
* @bprm: binprm for the exec (NOT NULL)
*/
-static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
+static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm)
{
/* clear out temporary/transitional state from the context */
aa_clear_task_ctx_trans(task_ctx(current));
static void apparmor_current_getsecid_subj(u32 *secid)
{
- struct aa_label *label = aa_get_current_label();
+ struct aa_label *label = __begin_current_label_crit_section();
*secid = label->secid;
- aa_put_label(label);
+ __end_current_label_crit_section(label);
}
static void apparmor_task_getsecid_obj(struct task_struct *p, u32 *secid)
int error = 0;
if (!unconfined(label))
- error = aa_task_setrlimit(label, task, resource, new_rlim);
+ error = aa_task_setrlimit(current_cred(), label, task,
+ resource, new_rlim);
__end_current_label_crit_section(label);
return error;
static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo *info,
int sig, const struct cred *cred)
{
+ const struct cred *tc;
struct aa_label *cl, *tl;
int error;
+ tc = get_task_cred(target);
+ tl = aa_get_newest_cred_label(tc);
if (cred) {
/*
* Dealing with USB IO specific behavior
*/
cl = aa_get_newest_cred_label(cred);
- tl = aa_get_task_label(target);
- error = aa_may_signal(cl, tl, sig);
+ error = aa_may_signal(cred, cl, tc, tl, sig);
aa_put_label(cl);
- aa_put_label(tl);
return error;
+ } else {
+ cl = __begin_current_label_crit_section();
+ error = aa_may_signal(current_cred(), cl, tc, tl, sig);
+ __end_current_label_crit_section(cl);
}
-
- cl = __begin_current_label_crit_section();
- tl = aa_get_task_label(target);
- error = aa_may_signal(cl, tl, sig);
aa_put_label(tl);
- __end_current_label_crit_section(cl);
+ put_cred(tc);
+
+ return error;
+ }
+
+ static int apparmor_userns_create(const struct cred *cred)
+ {
+ struct aa_label *label;
+ struct aa_profile *profile;
+ int error = 0;
+ DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_NS,
+ OP_USERNS_CREATE);
+
+ ad.subj_cred = current_cred();
+
+ label = begin_current_label_crit_section();
+ if (!unconfined(label)) {
+ error = fn_for_each(label, profile,
+ aa_profile_ns_perm(profile, &ad,
+ AA_USERNS_CREATE));
+ }
+ end_current_label_crit_section(label);
return error;
}
if (!ctx)
return -ENOMEM;
- SK_CTX(sk) = ctx;
+ sk->sk_security = ctx;
return 0;
}
*/
static void apparmor_sk_free_security(struct sock *sk)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
- SK_CTX(sk) = NULL;
+ sk->sk_security = NULL;
aa_put_label(ctx->label);
aa_put_label(ctx->peer);
kfree(ctx);
static void apparmor_sk_clone_security(const struct sock *sk,
struct sock *newsk)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
- struct aa_sk_ctx *new = SK_CTX(newsk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
+ struct aa_sk_ctx *new = aa_sock(newsk);
if (new->label)
aa_put_label(new->label);
if (!(kern || unconfined(label)))
error = af_select(family,
create_perm(label, family, type, protocol),
- aa_af_perm(label, OP_CREATE, AA_MAY_CREATE,
+ aa_af_perm(current_cred(), label,
+ OP_CREATE, AA_MAY_CREATE,
family, type, protocol));
end_current_label_crit_section(label);
label = aa_get_current_label();
if (sock->sk) {
- struct aa_sk_ctx *ctx = SK_CTX(sock->sk);
+ struct aa_sk_ctx *ctx = aa_sock(sock->sk);
aa_put_label(ctx->label);
ctx->label = aa_get_label(label);
*/
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
if (!skb->secmark)
return 0;
static struct aa_label *sk_peer_label(struct sock *sk)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
if (ctx->peer)
return ctx->peer;
*/
static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
if (!ctx->label)
ctx->label = aa_get_current_label();
static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
if (!skb->secmark)
return 0;
LSM_HOOK_INIT(capget, apparmor_capget),
LSM_HOOK_INIT(capable, apparmor_capable),
+ LSM_HOOK_INIT(move_mount, apparmor_move_mount),
LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid_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),
#ifdef CONFIG_AUDIT
LSM_HOOK_INIT(audit_rule_init, aa_audit_rule_init),
LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx),
LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid),
LSM_HOOK_INIT(release_secctx, apparmor_release_secctx),
+
+ #ifdef CONFIG_IO_URING
+ LSM_HOOK_INIT(uring_override_creds, apparmor_uring_override_creds),
+ LSM_HOOK_INIT(uring_sqpoll, apparmor_uring_sqpoll),
+ #endif
};
/*
char *aa_get_buffer(bool in_atomic)
{
union aa_buffer *aa_buf;
+ struct aa_local_cache *cache;
bool try_again = true;
gfp_t flags = (GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
+ /* use per cpu cached buffers first */
+ cache = get_cpu_ptr(&aa_local_buffers);
+ if (!list_empty(&cache->head)) {
+ aa_buf = list_first_entry(&cache->head, union aa_buffer, list);
+ list_del(&aa_buf->list);
+ cache->hold--;
+ cache->count--;
+ put_cpu_ptr(&aa_local_buffers);
+ return &aa_buf->buffer[0];
+ }
+ put_cpu_ptr(&aa_local_buffers);
+
+ if (!spin_trylock(&aa_buffers_lock)) {
+ cache = get_cpu_ptr(&aa_local_buffers);
+ cache->hold += 1;
+ put_cpu_ptr(&aa_local_buffers);
+ spin_lock(&aa_buffers_lock);
+ } else {
+ cache = get_cpu_ptr(&aa_local_buffers);
+ put_cpu_ptr(&aa_local_buffers);
+ }
retry:
- spin_lock(&aa_buffers_lock);
if (buffer_count > reserve_count ||
(in_atomic && !list_empty(&aa_global_buffers))) {
aa_buf = list_first_entry(&aa_global_buffers, union aa_buffer,
if (!aa_buf) {
if (try_again) {
try_again = false;
+ spin_lock(&aa_buffers_lock);
goto retry;
}
pr_warn_once("AppArmor: Failed to allocate a memory buffer.\n");
void aa_put_buffer(char *buf)
{
union aa_buffer *aa_buf;
+ struct aa_local_cache *cache;
if (!buf)
return;
aa_buf = container_of(buf, union aa_buffer, buffer[0]);
- spin_lock(&aa_buffers_lock);
- list_add(&aa_buf->list, &aa_global_buffers);
- buffer_count++;
- spin_unlock(&aa_buffers_lock);
+ cache = get_cpu_ptr(&aa_local_buffers);
+ if (!cache->hold) {
+ put_cpu_ptr(&aa_local_buffers);
+
+ if (spin_trylock(&aa_buffers_lock)) {
+ /* put back on global list */
+ list_add(&aa_buf->list, &aa_global_buffers);
+ buffer_count++;
+ spin_unlock(&aa_buffers_lock);
+ cache = get_cpu_ptr(&aa_local_buffers);
+ put_cpu_ptr(&aa_local_buffers);
+ return;
+ }
+ /* contention on global list, fallback to percpu */
+ cache = get_cpu_ptr(&aa_local_buffers);
+ cache->hold += 1;
+ }
+
+ /* cache in percpu list */
+ list_add(&aa_buf->list, &cache->head);
+ cache->count++;
+ put_cpu_ptr(&aa_local_buffers);
}
/*
union aa_buffer *aa_buf;
int i, num;
+ /*
+ * per cpu set of cached allocated buffers used to help reduce
+ * lock contention
+ */
+ for_each_possible_cpu(i) {
+ per_cpu(aa_local_buffers, i).hold = 0;
+ per_cpu(aa_local_buffers, i).count = 0;
+ INIT_LIST_HEAD(&per_cpu(aa_local_buffers, i).head);
+ }
/*
* A function may require two buffers at once. Usually the buffers are
* used for a short period of time and are shared. On UP kernel buffers
}
static struct ctl_table apparmor_sysctl_table[] = {
+ #ifdef CONFIG_USER_NS
{
.procname = "unprivileged_userns_apparmor_policy",
.data = &unprivileged_userns_apparmor_policy,
.mode = 0600,
.proc_handler = apparmor_dointvec,
},
+ #endif /* CONFIG_USER_NS */
{
.procname = "apparmor_display_secid_mode",
.data = &apparmor_display_secid_mode,
.mode = 0600,
.proc_handler = apparmor_dointvec,
},
-
+ {
+ .procname = "apparmor_restrict_unprivileged_unconfined",
+ .data = &aa_unprivileged_unconfined_restricted,
+ .maxlen = sizeof(int),
+ .mode = 0600,
+ .proc_handler = apparmor_dointvec,
+ },
{ }
};
if (sk == NULL)
return NF_ACCEPT;
- ctx = SK_CTX(sk);
+ ctx = aa_sock(sk);
if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND,
skb->secmark, sk))
return NF_ACCEPT;
__initcall(apparmor_nf_ip_init);
#endif
+ static char nulldfa_src[] = {
+ #include "nulldfa.in"
+ };
+ struct aa_dfa *nulldfa;
+
+ static char stacksplitdfa_src[] = {
+ #include "stacksplitdfa.in"
+ };
+ struct aa_dfa *stacksplitdfa;
+ struct aa_policydb *nullpdb;
+
+ static int __init aa_setup_dfa_engine(void)
+ {
+ int error = -ENOMEM;
+
+ nullpdb = aa_alloc_pdb(GFP_KERNEL);
+ if (!nullpdb)
+ return -ENOMEM;
+
+ nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
+ TO_ACCEPT1_FLAG(YYTD_DATA32) |
+ TO_ACCEPT2_FLAG(YYTD_DATA32));
+ if (IS_ERR(nulldfa)) {
+ error = PTR_ERR(nulldfa);
+ goto fail;
+ }
+ nullpdb->dfa = aa_get_dfa(nulldfa);
+ nullpdb->perms = kcalloc(2, sizeof(struct aa_perms), GFP_KERNEL);
+ if (!nullpdb->perms)
+ goto fail;
+ nullpdb->size = 2;
+
+ stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src,
+ sizeof(stacksplitdfa_src),
+ TO_ACCEPT1_FLAG(YYTD_DATA32) |
+ TO_ACCEPT2_FLAG(YYTD_DATA32));
+ if (IS_ERR(stacksplitdfa)) {
+ error = PTR_ERR(stacksplitdfa);
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ aa_put_pdb(nullpdb);
+ aa_put_dfa(nulldfa);
+ nullpdb = NULL;
+ nulldfa = NULL;
+ stacksplitdfa = NULL;
+
+ return error;
+ }
+
+ static void __init aa_teardown_dfa_engine(void)
+ {
+ aa_put_dfa(stacksplitdfa);
+ aa_put_dfa(nulldfa);
+ aa_put_pdb(nullpdb);
+ nullpdb = NULL;
+ stacksplitdfa = NULL;
+ nulldfa = NULL;
+ }
+
static int __init apparmor_init(void)
{
int error;
static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
+ struct apparmor_audit_data *ad = aad(sa);
- if (aad(sa)->iface.ns) {
+ if (ad->iface.ns) {
audit_log_format(ab, " ns=");
- audit_log_untrustedstring(ab, aad(sa)->iface.ns);
+ audit_log_untrustedstring(ab, ad->iface.ns);
}
- if (aad(sa)->name) {
+ if (ad->name) {
audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab, aad(sa)->name);
+ audit_log_untrustedstring(ab, ad->name);
}
- if (aad(sa)->iface.pos)
- audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
+ if (ad->iface.pos)
+ audit_log_format(ab, " offset=%ld", ad->iface.pos);
}
/**
int error)
{
struct aa_profile *profile = labels_profile(aa_current_raw_label());
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
+ DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
if (e)
- aad(&sa)->iface.pos = e->pos - e->start;
- aad(&sa)->iface.ns = ns_name;
+ ad.iface.pos = e->pos - e->start;
+ ad.iface.ns = ns_name;
if (new)
- aad(&sa)->name = new->base.hname;
+ ad.name = new->base.hname;
else
- aad(&sa)->name = name;
- aad(&sa)->info = info;
- aad(&sa)->error = error;
+ ad.name = name;
+ ad.info = info;
+ ad.error = error;
- return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb);
+ return aa_audit(AUDIT_APPARMOR_STATUS, profile, &ad, audit_cb);
}
void __aa_loaddata_update(struct aa_loaddata *data, long revision)
data->revision = revision;
if ((data->dents[AAFS_LOADDATA_REVISION])) {
- d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime =
- current_time(d_inode(data->dents[AAFS_LOADDATA_DIR]));
- d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime =
- current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION]));
+ struct inode *inode;
+
+ inode = d_inode(data->dents[AAFS_LOADDATA_DIR]);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+
+ inode = d_inode(data->dents[AAFS_LOADDATA_REVISION]);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
}
}
return -EPROTO;
}
- static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
+ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
bool required_dfa, bool required_trans,
const char **info)
{
+ struct aa_policydb *pdb;
void *pos = e->pos;
int i, flags, error = -EPROTO;
ssize_t size;
- size = unpack_perms_table(e, &policy->perms);
+ pdb = aa_alloc_pdb(GFP_KERNEL);
+ if (!pdb)
+ return -ENOMEM;
+
+ size = unpack_perms_table(e, &pdb->perms);
if (size < 0) {
error = size;
- policy->perms = NULL;
+ pdb->perms = NULL;
*info = "failed to unpack - perms";
goto fail;
}
- policy->size = size;
+ pdb->size = size;
- if (policy->perms) {
+ if (pdb->perms) {
/* perms table present accept is index */
flags = TO_ACCEPT1_FLAG(YYTD_DATA32);
} else {
TO_ACCEPT2_FLAG(YYTD_DATA32);
}
- policy->dfa = unpack_dfa(e, flags);
- if (IS_ERR(policy->dfa)) {
- error = PTR_ERR(policy->dfa);
- policy->dfa = NULL;
+ pdb->dfa = unpack_dfa(e, flags);
+ if (IS_ERR(pdb->dfa)) {
+ error = PTR_ERR(pdb->dfa);
+ pdb->dfa = NULL;
*info = "failed to unpack - dfa";
goto fail;
- } else if (!policy->dfa) {
+ } else if (!pdb->dfa) {
if (required_dfa) {
*info = "missing required dfa";
goto fail;
* sadly start was given different names for file and policydb
* but since it is optional we can try both
*/
- if (!aa_unpack_u32(e, &policy->start[0], "start"))
+ if (!aa_unpack_u32(e, &pdb->start[0], "start"))
/* default start state */
- policy->start[0] = DFA_START;
- if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) {
+ 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 */
- policy->start[AA_CLASS_FILE] = DFA_START;
+ pdb->start[AA_CLASS_FILE] = DFA_START;
} /* setup class index */
for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
- policy->start[i] = aa_dfa_next(policy->dfa, policy->start[0],
+ pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
i);
}
- if (!unpack_trans_table(e, &policy->trans) && required_trans) {
+ if (!unpack_trans_table(e, &pdb->trans) && required_trans) {
*info = "failed to unpack profile transition table";
goto fail;
}
/* 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;
fail:
+ aa_put_pdb(pdb);
e->pos = pos;
return error;
}
const char *info = "failed to unpack profile";
size_t ns_len;
struct rhashtable_params params = { 0 };
- char *key = NULL;
+ char *key = NULL, *disconnected = NULL;
struct aa_data *data;
int error = -EPROTO;
kernel_cap_t tmpcap;
}
/* neither xmatch_len not xmatch_perms are optional if xmatch is set */
- if (profile->attach.xmatch.dfa) {
+ if (profile->attach.xmatch->dfa) {
if (!aa_unpack_u32(e, &tmp, NULL)) {
info = "missing xmatch len";
goto fail;
}
profile->attach.xmatch_len = tmp;
- profile->attach.xmatch.start[AA_CLASS_XMATCH] = DFA_START;
- if (!profile->attach.xmatch.perms) {
- error = aa_compat_map_xmatch(&profile->attach.xmatch);
+ profile->attach.xmatch->start[AA_CLASS_XMATCH] = DFA_START;
+ if (!profile->attach.xmatch->perms) {
+ error = aa_compat_map_xmatch(profile->attach.xmatch);
if (error) {
info = "failed to convert xmatch permission table";
goto fail;
}
/* disconnected attachment string is optional */
- (void) aa_unpack_str(e, &profile->disconnected, "disconnected");
+ (void) aa_unpack_strdup(e, &disconnected, "disconnected");
+ profile->disconnected = disconnected;
/* per profile debug flags (complain, audit) */
if (!aa_unpack_nameX(e, AA_STRUCT, "flags")) {
if (error)
goto fail;
/* Fixup: drop when we get rid of start array */
- if (aa_dfa_next(rules->policy.dfa, rules->policy.start[0],
+ if (aa_dfa_next(rules->policy->dfa, rules->policy->start[0],
AA_CLASS_FILE))
- rules->policy.start[AA_CLASS_FILE] =
- aa_dfa_next(rules->policy.dfa,
- rules->policy.start[0],
+ rules->policy->start[AA_CLASS_FILE] =
+ aa_dfa_next(rules->policy->dfa,
+ rules->policy->start[0],
AA_CLASS_FILE);
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
- if (!rules->policy.perms) {
- error = aa_compat_map_policy(&rules->policy,
+ if (!rules->policy->perms) {
+ error = aa_compat_map_policy(rules->policy,
e->version);
if (error) {
info = "failed to remap policydb permission table";
}
}
} else {
- rules->policy.dfa = aa_get_dfa(nulldfa);
- rules->policy.perms = kcalloc(2, sizeof(struct aa_perms),
- GFP_KERNEL);
- if (!rules->policy.perms)
- goto fail;
- rules->policy.size = 2;
+ rules->policy = aa_get_pdb(nullpdb);
}
/* get file rules */
error = unpack_pdb(e, &rules->file, false, true, &info);
if (error) {
goto fail;
- } else if (rules->file.dfa) {
- if (!rules->file.perms) {
- error = aa_compat_map_file(&rules->file);
+ } else if (rules->file->dfa) {
+ if (!rules->file->perms) {
+ error = aa_compat_map_file(rules->file);
if (error) {
info = "failed to remap file permission table";
goto fail;
}
}
- } else if (rules->policy.dfa &&
- rules->policy.start[AA_CLASS_FILE]) {
- rules->file.dfa = aa_get_dfa(rules->policy.dfa);
- rules->file.start[AA_CLASS_FILE] = rules->policy.start[AA_CLASS_FILE];
- rules->file.perms = kcalloc(rules->policy.size,
- sizeof(struct aa_perms),
- GFP_KERNEL);
- if (!rules->file.perms)
- goto fail;
- memcpy(rules->file.perms, rules->policy.perms,
- rules->policy.size * sizeof(struct aa_perms));
- rules->file.size = rules->policy.size;
+ } else if (rules->policy->dfa &&
+ rules->policy->start[AA_CLASS_FILE]) {
+ rules->file = aa_get_pdb(rules->policy);
} else {
- rules->file.dfa = aa_get_dfa(nulldfa);
- rules->file.perms = kcalloc(2, sizeof(struct aa_perms),
- GFP_KERNEL);
- if (!rules->file.perms)
- goto fail;
- rules->file.size = 2;
+ rules->file = aa_get_pdb(nullpdb);
}
error = -EPROTO;
if (aa_unpack_nameX(e, AA_STRUCT, "data")) {
/**
* verify_dfa_accept_index - verify accept indexes are in range of perms table
* @dfa: the dfa to check accept indexes are in range
- * table_size: the permission table size the indexes should be within
+ * @table_size: the permission table size the indexes should be within
*/
static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size)
{
if (!rules)
return 0;
- if ((rules->file.dfa && !verify_dfa_accept_index(rules->file.dfa,
- rules->file.size)) ||
- (rules->policy.dfa &&
- !verify_dfa_accept_index(rules->policy.dfa, rules->policy.size))) {
+ if (rules->file->dfa && !verify_dfa_accept_index(rules->file->dfa,
+ rules->file->size)) {
+ audit_iface(profile, NULL, NULL,
+ "Unpack: file Invalid named transition", NULL,
+ -EPROTO);
+ return -EPROTO;
+ }
+ if (rules->policy->dfa &&
+ !verify_dfa_accept_index(rules->policy->dfa, rules->policy->size)) {
audit_iface(profile, NULL, NULL,
- "Unpack: Invalid named transition", NULL, -EPROTO);
+ "Unpack: policy Invalid named transition", NULL,
+ -EPROTO);
return -EPROTO;
}
- if (!verify_perms(&rules->file)) {
+ if (!verify_perms(rules->file)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;
}
- if (!verify_perms(&rules->policy)) {
+ if (!verify_perms(rules->policy)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;
}
- if (!verify_perms(&profile->attach.xmatch)) {
+ if (!verify_perms(profile->attach.xmatch)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;