You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Highlights:
- TPM core and driver updates/fixes
- IPv6 security labeling (CALIPSO)
- Lots of Apparmor fixes
- Seccomp: remove 2-phase API, close hole where ptrace can change
syscall #"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (156 commits)
apparmor: fix SECURITY_APPARMOR_HASH_DEFAULT parameter handling
tpm: Add TPM 2.0 support to the Nuvoton i2c driver (NPCT6xx family)
tpm: Factor out common startup code
tpm: use devm_add_action_or_reset
tpm2_i2c_nuvoton: add irq validity check
tpm: read burstcount from TPM_STS in one 32-bit transaction
tpm: fix byte-order for the value read by tpm2_get_tpm_pt
tpm_tis_core: convert max timeouts from msec to jiffies
apparmor: fix arg_size computation for when setprocattr is null terminated
apparmor: fix oops, validate buffer size in apparmor_setprocattr()
apparmor: do not expose kernel stack
apparmor: fix module parameters can be changed after policy is locked
apparmor: fix oops in profile_unpack() when policy_db is not present
apparmor: don't check for vmalloc_addr if kvzalloc() failed
apparmor: add missing id bounds check on dfa verification
apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task
apparmor: use list_next_entry instead of list_entry_next
apparmor: fix refcount race when finding a child profile
apparmor: fix ref count leak when profile sha1 hash is read
apparmor: check that xindex is in trans_table bounds
...
This commit is contained in:
@@ -31,13 +31,26 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
|
||||
If you are unsure how to answer this question, answer 1.
|
||||
|
||||
config SECURITY_APPARMOR_HASH
|
||||
bool "SHA1 hash of loaded profiles"
|
||||
bool "Enable introspection of sha1 hashes for loaded profiles"
|
||||
depends on SECURITY_APPARMOR
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA1
|
||||
default y
|
||||
|
||||
help
|
||||
This option selects whether sha1 hashing is done against loaded
|
||||
profiles and exported for inspection to user space via the apparmor
|
||||
filesystem.
|
||||
This option selects whether introspection of loaded policy
|
||||
is available to userspace via the apparmor filesystem.
|
||||
|
||||
config SECURITY_APPARMOR_HASH_DEFAULT
|
||||
bool "Enable policy hash introspection by default"
|
||||
depends on SECURITY_APPARMOR_HASH
|
||||
default y
|
||||
|
||||
help
|
||||
This option selects whether sha1 hashing of loaded policy
|
||||
is enabled by default. The generation of sha1 hashes for
|
||||
loaded policy provide system administrators a quick way
|
||||
to verify that policy in the kernel matches what is expected,
|
||||
however it can slow down policy load on some devices. In
|
||||
these cases policy hashing can be disabled by default and
|
||||
enabled only if needed.
|
||||
|
||||
@@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v)
|
||||
seq_printf(seq, "%.2x", profile->hash[i]);
|
||||
seq_puts(seq, "\n");
|
||||
}
|
||||
aa_put_profile(profile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -379,6 +380,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old,
|
||||
|
||||
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;
|
||||
old->dents[i] = NULL;
|
||||
}
|
||||
}
|
||||
@@ -550,8 +553,6 @@ fail2:
|
||||
}
|
||||
|
||||
|
||||
#define list_entry_next(pos, member) \
|
||||
list_entry(pos->member.next, typeof(*pos), member)
|
||||
#define list_entry_is_head(pos, head, member) (&pos->member == (head))
|
||||
|
||||
/**
|
||||
@@ -582,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root,
|
||||
parent = ns->parent;
|
||||
while (ns != root) {
|
||||
mutex_unlock(&ns->lock);
|
||||
next = list_entry_next(ns, base.list);
|
||||
next = list_next_entry(ns, base.list);
|
||||
if (!list_entry_is_head(next, &parent->sub_ns, base.list)) {
|
||||
mutex_lock(&next->lock);
|
||||
return next;
|
||||
@@ -636,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
|
||||
parent = rcu_dereference_protected(p->parent,
|
||||
mutex_is_locked(&p->ns->lock));
|
||||
while (parent) {
|
||||
p = list_entry_next(p, base.list);
|
||||
p = list_next_entry(p, base.list);
|
||||
if (!list_entry_is_head(p, &parent->base.profiles, base.list))
|
||||
return p;
|
||||
p = parent;
|
||||
@@ -645,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
|
||||
}
|
||||
|
||||
/* is next another profile in the namespace */
|
||||
p = list_entry_next(p, base.list);
|
||||
p = list_next_entry(p, base.list);
|
||||
if (!list_entry_is_head(p, &ns->base.profiles, base.list))
|
||||
return p;
|
||||
|
||||
|
||||
@@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
|
||||
|
||||
if (sa->aad->type == AUDIT_APPARMOR_KILL)
|
||||
(void)send_sig_info(SIGKILL, NULL,
|
||||
sa->u.tsk ? sa->u.tsk : current);
|
||||
sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ?
|
||||
sa->u.tsk : current);
|
||||
|
||||
if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
|
||||
return complain_error(sa->aad->error);
|
||||
|
||||
@@ -39,6 +39,9 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
|
||||
int error = -ENOMEM;
|
||||
u32 le32_version = cpu_to_le32(version);
|
||||
|
||||
if (!aa_g_hash_policy)
|
||||
return 0;
|
||||
|
||||
if (!apparmor_tfm)
|
||||
return 0;
|
||||
|
||||
|
||||
+10
-12
@@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
file_inode(bprm->file)->i_uid,
|
||||
file_inode(bprm->file)->i_mode
|
||||
};
|
||||
const char *name = NULL, *target = NULL, *info = NULL;
|
||||
const char *name = NULL, *info = NULL;
|
||||
int error = 0;
|
||||
|
||||
if (bprm->cred_prepared)
|
||||
@@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
if (cxt->onexec) {
|
||||
struct file_perms cp;
|
||||
info = "change_profile onexec";
|
||||
new_profile = aa_get_newest_profile(cxt->onexec);
|
||||
if (!(perms.allow & AA_MAY_ONEXEC))
|
||||
goto audit;
|
||||
|
||||
@@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
|
||||
if (!(cp.allow & AA_MAY_ONEXEC))
|
||||
goto audit;
|
||||
new_profile = aa_get_newest_profile(cxt->onexec);
|
||||
goto apply;
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
new_profile = aa_get_newest_profile(ns->unconfined);
|
||||
info = "ux fallback";
|
||||
} else {
|
||||
error = -ENOENT;
|
||||
error = -EACCES;
|
||||
info = "profile not found";
|
||||
/* remove MAY_EXEC to audit as failure */
|
||||
perms.allow &= ~MAY_EXEC;
|
||||
@@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
if (!new_profile) {
|
||||
error = -ENOMEM;
|
||||
info = "could not create null profile";
|
||||
} else {
|
||||
} else
|
||||
error = -EACCES;
|
||||
target = new_profile->base.hname;
|
||||
}
|
||||
perms.xindex |= AA_X_UNSAFE;
|
||||
} else
|
||||
/* fail exec */
|
||||
@@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
* fail the exec.
|
||||
*/
|
||||
if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
|
||||
aa_put_profile(new_profile);
|
||||
error = -EPERM;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
|
||||
if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
|
||||
error = may_change_ptraced_domain(new_profile);
|
||||
if (error) {
|
||||
aa_put_profile(new_profile);
|
||||
if (error)
|
||||
goto audit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if secure exec is needed.
|
||||
@@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
bprm->unsafe |= AA_SECURE_X_NEEDED;
|
||||
}
|
||||
apply:
|
||||
target = new_profile->base.hname;
|
||||
/* when transitioning profiles clear unsafe personality bits */
|
||||
bprm->per_clear |= PER_CLEAR_ON_SETID;
|
||||
|
||||
@@ -506,15 +500,19 @@ x_clear:
|
||||
aa_put_profile(cxt->profile);
|
||||
/* transfer new profile reference will be released when cxt is freed */
|
||||
cxt->profile = new_profile;
|
||||
new_profile = NULL;
|
||||
|
||||
/* clear out all temporary/transitional state from the context */
|
||||
aa_clear_task_cxt_trans(cxt);
|
||||
|
||||
audit:
|
||||
error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
|
||||
name, target, cond.uid, info, error);
|
||||
name,
|
||||
new_profile ? new_profile->base.hname : NULL,
|
||||
cond.uid, info, error);
|
||||
|
||||
cleanup:
|
||||
aa_put_profile(new_profile);
|
||||
aa_put_profile(profile);
|
||||
kfree(buffer);
|
||||
|
||||
|
||||
@@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
|
||||
int type = AUDIT_APPARMOR_AUTO;
|
||||
struct common_audit_data sa;
|
||||
struct apparmor_audit_data aad = {0,};
|
||||
sa.type = LSM_AUDIT_DATA_NONE;
|
||||
sa.type = LSM_AUDIT_DATA_TASK;
|
||||
sa.u.tsk = NULL;
|
||||
sa.aad = &aad;
|
||||
aad.op = op,
|
||||
aad.fs.request = request;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
extern enum audit_mode aa_g_audit;
|
||||
extern bool aa_g_audit_header;
|
||||
extern bool aa_g_debug;
|
||||
extern bool aa_g_hash_policy;
|
||||
extern bool aa_g_lock_policy;
|
||||
extern bool aa_g_logsyscall;
|
||||
extern bool aa_g_paranoid_load;
|
||||
|
||||
@@ -62,6 +62,7 @@ struct table_set_header {
|
||||
#define YYTD_ID_ACCEPT2 6
|
||||
#define YYTD_ID_NXT 7
|
||||
#define YYTD_ID_TSIZE 8
|
||||
#define YYTD_ID_MAX 8
|
||||
|
||||
#define YYTD_DATA8 1
|
||||
#define YYTD_DATA16 2
|
||||
|
||||
@@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile)
|
||||
return profile->audit;
|
||||
}
|
||||
|
||||
bool policy_view_capable(void);
|
||||
bool policy_admin_capable(void);
|
||||
bool aa_may_manage_policy(int op);
|
||||
|
||||
#endif /* __AA_POLICY_H */
|
||||
|
||||
+17
-13
@@ -529,7 +529,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
|
||||
if (!*args)
|
||||
goto out;
|
||||
|
||||
arg_size = size - (args - (char *) value);
|
||||
arg_size = size - (args - (largs ? largs : (char *) value));
|
||||
if (strcmp(name, "current") == 0) {
|
||||
if (strcmp(command, "changehat") == 0) {
|
||||
error = aa_setprocattr_changehat(args, arg_size,
|
||||
@@ -671,6 +671,12 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE;
|
||||
module_param_call(mode, param_set_mode, param_get_mode,
|
||||
&aa_g_profile_mode, S_IRUSR | S_IWUSR);
|
||||
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_HASH
|
||||
/* whether policy verification hashing is enabled */
|
||||
bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
|
||||
module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
|
||||
#endif
|
||||
|
||||
/* Debug mode */
|
||||
bool aa_g_debug;
|
||||
module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR);
|
||||
@@ -728,51 +734,49 @@ __setup("apparmor=", apparmor_enabled_setup);
|
||||
/* set global flag turning off the ability to load policy */
|
||||
static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_admin_capable())
|
||||
return -EPERM;
|
||||
if (aa_g_lock_policy)
|
||||
return -EACCES;
|
||||
return param_set_bool(val, kp);
|
||||
}
|
||||
|
||||
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_view_capable())
|
||||
return -EPERM;
|
||||
return param_get_bool(buffer, kp);
|
||||
}
|
||||
|
||||
static int param_set_aabool(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_admin_capable())
|
||||
return -EPERM;
|
||||
return param_set_bool(val, kp);
|
||||
}
|
||||
|
||||
static int param_get_aabool(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_view_capable())
|
||||
return -EPERM;
|
||||
return param_get_bool(buffer, kp);
|
||||
}
|
||||
|
||||
static int param_set_aauint(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_admin_capable())
|
||||
return -EPERM;
|
||||
return param_set_uint(val, kp);
|
||||
}
|
||||
|
||||
static int param_get_aauint(char *buffer, const struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_view_capable())
|
||||
return -EPERM;
|
||||
return param_get_uint(buffer, kp);
|
||||
}
|
||||
|
||||
static int param_get_audit(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_view_capable())
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
@@ -784,7 +788,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp)
|
||||
static int param_set_audit(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
int i;
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_admin_capable())
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
@@ -805,7 +809,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp)
|
||||
|
||||
static int param_get_mode(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_admin_capable())
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
@@ -817,7 +821,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp)
|
||||
static int param_set_mode(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
int i;
|
||||
if (!capable(CAP_MAC_ADMIN))
|
||||
if (!policy_admin_capable())
|
||||
return -EPERM;
|
||||
|
||||
if (!apparmor_enabled)
|
||||
|
||||
@@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
* it every time we use td_id as an index
|
||||
*/
|
||||
th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1;
|
||||
if (th.td_id > YYTD_ID_MAX)
|
||||
goto out;
|
||||
th.td_flags = be16_to_cpu(*(u16 *) (blob + 2));
|
||||
th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8));
|
||||
blob += sizeof(struct table_header);
|
||||
@@ -61,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
|
||||
table = kvzalloc(tsize);
|
||||
if (table) {
|
||||
*table = th;
|
||||
table->td_id = th.td_id;
|
||||
table->td_flags = th.td_flags;
|
||||
table->td_lolen = th.td_lolen;
|
||||
if (th.td_flags == YYTD_DATA8)
|
||||
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
|
||||
u8, byte_to_byte);
|
||||
@@ -73,14 +77,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
|
||||
u32, be32_to_cpu);
|
||||
else
|
||||
goto fail;
|
||||
/* 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();
|
||||
}
|
||||
|
||||
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();
|
||||
return table;
|
||||
fail:
|
||||
kvfree(table);
|
||||
|
||||
+36
-25
@@ -25,7 +25,6 @@
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
|
||||
|
||||
/* modified from dcache.c */
|
||||
static int prepend(char **buffer, int buflen, const char *str, int namelen)
|
||||
{
|
||||
@@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen)
|
||||
|
||||
#define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT)
|
||||
|
||||
/* If the path is not connected to the expected root,
|
||||
* check if it is a sysctl and handle specially else remove any
|
||||
* leading / that __d_path may have returned.
|
||||
* Unless
|
||||
* specifically directed to connect the path,
|
||||
* OR
|
||||
* if in a chroot and doing chroot relative paths and the path
|
||||
* resolves to the namespace root (would be connected outside
|
||||
* of chroot) and specifically directed to connect paths to
|
||||
* namespace root.
|
||||
*/
|
||||
static int disconnect(const struct path *path, char *buf, char **name,
|
||||
int flags)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (!(flags & PATH_CONNECT_PATH) &&
|
||||
!(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
|
||||
our_mnt(path->mnt))) {
|
||||
/* disconnected path, don't return pathname starting
|
||||
* with '/'
|
||||
*/
|
||||
error = -EACCES;
|
||||
if (**name == '/')
|
||||
*name = *name + 1;
|
||||
} else if (**name != '/')
|
||||
/* CONNECT_PATH with missing root */
|
||||
error = prepend(name, *name - buf, "/", 1);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* d_namespace_path - lookup a name associated with a given path
|
||||
* @path: path to lookup (NOT NULL)
|
||||
@@ -74,7 +105,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen,
|
||||
* control instead of hard coded /proc
|
||||
*/
|
||||
return prepend(name, *name - buf, "/proc", 5);
|
||||
}
|
||||
} else
|
||||
return disconnect(path, buf, name, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -120,29 +152,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the path is not connected to the expected root,
|
||||
* check if it is a sysctl and handle specially else remove any
|
||||
* leading / that __d_path may have returned.
|
||||
* Unless
|
||||
* specifically directed to connect the path,
|
||||
* OR
|
||||
* if in a chroot and doing chroot relative paths and the path
|
||||
* resolves to the namespace root (would be connected outside
|
||||
* of chroot) and specifically directed to connect paths to
|
||||
* namespace root.
|
||||
*/
|
||||
if (!connected) {
|
||||
if (!(flags & PATH_CONNECT_PATH) &&
|
||||
!(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) &&
|
||||
our_mnt(path->mnt))) {
|
||||
/* disconnected path, don't return pathname starting
|
||||
* with '/'
|
||||
*/
|
||||
error = -EACCES;
|
||||
if (*res == '/')
|
||||
*name = res + 1;
|
||||
}
|
||||
}
|
||||
if (!connected)
|
||||
error = disconnect(path, buf, name, flags);
|
||||
|
||||
out:
|
||||
return error;
|
||||
|
||||
+44
-17
@@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name)
|
||||
struct aa_profile *profile;
|
||||
|
||||
rcu_read_lock();
|
||||
profile = aa_get_profile(__find_child(&parent->base.profiles, name));
|
||||
do {
|
||||
profile = __find_child(&parent->base.profiles, name);
|
||||
} while (profile && !aa_get_profile_not0(profile));
|
||||
rcu_read_unlock();
|
||||
|
||||
/* refcount released by caller */
|
||||
@@ -916,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info,
|
||||
&sa, NULL);
|
||||
}
|
||||
|
||||
bool policy_view_capable(void)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
bool response = false;
|
||||
|
||||
if (ns_capable(user_ns, CAP_MAC_ADMIN))
|
||||
response = true;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
bool policy_admin_capable(void)
|
||||
{
|
||||
return policy_view_capable() && !aa_g_lock_policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_may_manage_policy - can the current task manage policy
|
||||
* @op: the policy manipulation operation being done
|
||||
@@ -930,7 +948,7 @@ bool aa_may_manage_policy(int op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!capable(CAP_MAC_ADMIN)) {
|
||||
if (!policy_admin_capable()) {
|
||||
audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES);
|
||||
return 0;
|
||||
}
|
||||
@@ -1067,7 +1085,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname,
|
||||
*/
|
||||
ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
|
||||
{
|
||||
const char *ns_name, *name = NULL, *info = NULL;
|
||||
const char *ns_name, *info = NULL;
|
||||
struct aa_namespace *ns = NULL;
|
||||
struct aa_load_ent *ent, *tmp;
|
||||
int op = OP_PROF_REPL;
|
||||
@@ -1082,18 +1100,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
|
||||
/* released below */
|
||||
ns = aa_prepare_namespace(ns_name);
|
||||
if (!ns) {
|
||||
info = "failed to prepare namespace";
|
||||
error = -ENOMEM;
|
||||
name = ns_name;
|
||||
goto fail;
|
||||
error = audit_policy(op, GFP_KERNEL, ns_name,
|
||||
"failed to prepare namespace", -ENOMEM);
|
||||
goto free;
|
||||
}
|
||||
|
||||
mutex_lock(&ns->lock);
|
||||
/* setup parent and ns info */
|
||||
list_for_each_entry(ent, &lh, list) {
|
||||
struct aa_policy *policy;
|
||||
|
||||
name = ent->new->base.hname;
|
||||
error = __lookup_replace(ns, ent->new->base.hname, noreplace,
|
||||
&ent->old, &info);
|
||||
if (error)
|
||||
@@ -1121,7 +1136,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
|
||||
if (!p) {
|
||||
error = -ENOENT;
|
||||
info = "parent does not exist";
|
||||
name = ent->new->base.hname;
|
||||
goto fail_lock;
|
||||
}
|
||||
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
|
||||
@@ -1163,7 +1177,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
|
||||
list_del_init(&ent->list);
|
||||
op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
|
||||
audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error);
|
||||
audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error);
|
||||
|
||||
if (ent->old) {
|
||||
__replace_profile(ent->old, ent->new, 1);
|
||||
@@ -1187,14 +1201,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
|
||||
/* parent replaced in this atomic set? */
|
||||
if (newest != parent) {
|
||||
aa_get_profile(newest);
|
||||
aa_put_profile(parent);
|
||||
rcu_assign_pointer(ent->new->parent, newest);
|
||||
} else
|
||||
aa_put_profile(newest);
|
||||
aa_put_profile(parent);
|
||||
}
|
||||
/* aafs interface uses replacedby */
|
||||
rcu_assign_pointer(ent->new->replacedby->profile,
|
||||
aa_get_profile(ent->new));
|
||||
__list_add_profile(&parent->base.profiles, ent->new);
|
||||
__list_add_profile(&newest->base.profiles, ent->new);
|
||||
aa_put_profile(newest);
|
||||
} else {
|
||||
/* aafs interface uses replacedby */
|
||||
rcu_assign_pointer(ent->new->replacedby->profile,
|
||||
@@ -1214,9 +1228,22 @@ out:
|
||||
|
||||
fail_lock:
|
||||
mutex_unlock(&ns->lock);
|
||||
fail:
|
||||
error = audit_policy(op, GFP_KERNEL, name, info, error);
|
||||
|
||||
/* audit cause of failure */
|
||||
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error);
|
||||
/* audit status that rest of profiles in the atomic set failed too */
|
||||
info = "valid profile in failed atomic policy load";
|
||||
list_for_each_entry(tmp, &lh, list) {
|
||||
if (tmp == ent) {
|
||||
info = "unchecked profile in failed atomic policy load";
|
||||
/* skip entry that caused failure */
|
||||
continue;
|
||||
}
|
||||
op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
|
||||
audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error);
|
||||
}
|
||||
free:
|
||||
list_for_each_entry_safe(ent, tmp, &lh, list) {
|
||||
list_del_init(&ent->list);
|
||||
aa_load_ent_free(ent);
|
||||
|
||||
@@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
|
||||
error = PTR_ERR(profile->policy.dfa);
|
||||
profile->policy.dfa = NULL;
|
||||
goto fail;
|
||||
} else if (!profile->policy.dfa) {
|
||||
error = -EPROTO;
|
||||
goto fail;
|
||||
}
|
||||
if (!unpack_u32(e, &profile->policy.start[0], "start"))
|
||||
/* default start state */
|
||||
@@ -676,7 +679,7 @@ static bool verify_xindex(int xindex, int table_size)
|
||||
int index, xtype;
|
||||
xtype = xindex & AA_X_TYPE_MASK;
|
||||
index = xindex & AA_X_INDEX_MASK;
|
||||
if (xtype == AA_X_TABLE && index > table_size)
|
||||
if (xtype == AA_X_TABLE && index >= table_size)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -776,7 +779,7 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns)
|
||||
goto fail_profile;
|
||||
|
||||
error = aa_calc_profile_hash(profile, e.version, start,
|
||||
e.pos - start);
|
||||
e.pos - start);
|
||||
if (error)
|
||||
goto fail_profile;
|
||||
|
||||
|
||||
@@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task,
|
||||
/* TODO: extend resource control to handle other (non current)
|
||||
* profiles. AppArmor rules currently have the implicit assumption
|
||||
* that the task is setting the resource of a task confined with
|
||||
* the same profile.
|
||||
* the same profile or that the task setting the resource of another
|
||||
* task has CAP_SYS_RESOURCE.
|
||||
*/
|
||||
if (profile != task_profile ||
|
||||
if ((profile != task_profile &&
|
||||
aa_capable(profile, CAP_SYS_RESOURCE, 1)) ||
|
||||
(profile->rlimits.mask & (1 << resource) &&
|
||||
new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
|
||||
error = -EACCES;
|
||||
|
||||
@@ -79,6 +79,7 @@ static void iint_free(struct integrity_iint_cache *iint)
|
||||
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
|
||||
iint->ima_read_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
iint->measured_pcrs = 0;
|
||||
kmem_cache_free(iint_cache, iint);
|
||||
}
|
||||
|
||||
@@ -159,6 +160,7 @@ static void init_once(void *foo)
|
||||
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
|
||||
iint->ima_read_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
iint->measured_pcrs = 0;
|
||||
}
|
||||
|
||||
static int __init integrity_iintcache_init(void)
|
||||
|
||||
@@ -88,6 +88,7 @@ struct ima_template_desc {
|
||||
};
|
||||
|
||||
struct ima_template_entry {
|
||||
int pcr;
|
||||
u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
|
||||
struct ima_template_desc *template_desc; /* template descriptor */
|
||||
u32 template_data_len;
|
||||
@@ -154,7 +155,8 @@ enum ima_hooks {
|
||||
};
|
||||
|
||||
/* LIM API function definitions */
|
||||
int ima_get_action(struct inode *inode, int mask, enum ima_hooks func);
|
||||
int ima_get_action(struct inode *inode, int mask,
|
||||
enum ima_hooks func, int *pcr);
|
||||
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, void *buf, loff_t size,
|
||||
@@ -162,19 +164,20 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len);
|
||||
int xattr_len, int pcr);
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename);
|
||||
int ima_alloc_init_template(struct ima_event_data *event_data,
|
||||
struct ima_template_entry **entry);
|
||||
int ima_store_template(struct ima_template_entry *entry, int violation,
|
||||
struct inode *inode, const unsigned char *filename);
|
||||
struct inode *inode,
|
||||
const unsigned char *filename, int pcr);
|
||||
void ima_free_template_entry(struct ima_template_entry *entry);
|
||||
const char *ima_d_path(const struct path *path, char **pathbuf);
|
||||
|
||||
/* IMA policy related functions */
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags);
|
||||
int flags, int *pcr);
|
||||
void ima_init_policy(void);
|
||||
void ima_update_policy(void);
|
||||
void ima_update_policy_flag(void);
|
||||
|
||||
@@ -87,7 +87,7 @@ out:
|
||||
*/
|
||||
int ima_store_template(struct ima_template_entry *entry,
|
||||
int violation, struct inode *inode,
|
||||
const unsigned char *filename)
|
||||
const unsigned char *filename, int pcr)
|
||||
{
|
||||
static const char op[] = "add_template_measure";
|
||||
static const char audit_cause[] = "hashing_error";
|
||||
@@ -114,6 +114,7 @@ int ima_store_template(struct ima_template_entry *entry,
|
||||
}
|
||||
memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
|
||||
}
|
||||
entry->pcr = pcr;
|
||||
result = ima_add_template_entry(entry, violation, op, inode, filename);
|
||||
return result;
|
||||
}
|
||||
@@ -144,7 +145,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
|
||||
result = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
result = ima_store_template(entry, violation, inode, filename);
|
||||
result = ima_store_template(entry, violation, inode,
|
||||
filename, CONFIG_IMA_MEASURE_PCR_IDX);
|
||||
if (result < 0)
|
||||
ima_free_template_entry(entry);
|
||||
err_out:
|
||||
@@ -157,6 +159,7 @@ err_out:
|
||||
* @inode: pointer to inode to measure
|
||||
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
|
||||
* @func: caller identifier
|
||||
* @pcr: pointer filled in if matched measure policy sets pcr=
|
||||
*
|
||||
* The policy is defined in terms of keypairs:
|
||||
* subj=, obj=, type=, func=, mask=, fsmagic=
|
||||
@@ -168,13 +171,13 @@ err_out:
|
||||
* Returns IMA_MEASURE, IMA_APPRAISE mask.
|
||||
*
|
||||
*/
|
||||
int ima_get_action(struct inode *inode, int mask, enum ima_hooks func)
|
||||
int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
|
||||
{
|
||||
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
|
||||
|
||||
flags &= ima_policy_flag;
|
||||
|
||||
return ima_match_policy(inode, func, mask, flags);
|
||||
return ima_match_policy(inode, func, mask, flags, pcr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -252,7 +255,7 @@ out:
|
||||
void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len)
|
||||
int xattr_len, int pcr)
|
||||
{
|
||||
static const char op[] = "add_template_measure";
|
||||
static const char audit_cause[] = "ENOMEM";
|
||||
@@ -263,7 +266,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
xattr_len, NULL};
|
||||
int violation = 0;
|
||||
|
||||
if (iint->flags & IMA_MEASURED)
|
||||
if (iint->measured_pcrs & (0x1 << pcr))
|
||||
return;
|
||||
|
||||
result = ima_alloc_init_template(&event_data, &entry);
|
||||
@@ -273,9 +276,11 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
return;
|
||||
}
|
||||
|
||||
result = ima_store_template(entry, violation, inode, filename);
|
||||
if (!result || result == -EEXIST)
|
||||
result = ima_store_template(entry, violation, inode, filename, pcr);
|
||||
if (!result || result == -EEXIST) {
|
||||
iint->flags |= IMA_MEASURED;
|
||||
iint->measured_pcrs |= (0x1 << pcr);
|
||||
}
|
||||
if (result < 0)
|
||||
ima_free_template_entry(entry);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
|
||||
return ima_match_policy(inode, func, mask, IMA_APPRAISE);
|
||||
return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL);
|
||||
}
|
||||
|
||||
static int ima_fix_xattr(struct dentry *dentry,
|
||||
@@ -370,6 +370,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
|
||||
return;
|
||||
|
||||
iint->flags &= ~IMA_DONE_MASK;
|
||||
iint->measured_pcrs = 0;
|
||||
if (digsig)
|
||||
iint->flags |= IMA_DIGSIG;
|
||||
return;
|
||||
|
||||
@@ -123,7 +123,6 @@ static int ima_measurements_show(struct seq_file *m, void *v)
|
||||
struct ima_template_entry *e;
|
||||
char *template_name;
|
||||
int namelen;
|
||||
u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
|
||||
bool is_ima_template = false;
|
||||
int i;
|
||||
|
||||
@@ -137,10 +136,10 @@ static int ima_measurements_show(struct seq_file *m, void *v)
|
||||
|
||||
/*
|
||||
* 1st: PCRIndex
|
||||
* PCR used is always the same (config option) in
|
||||
* little-endian format
|
||||
* PCR used defaults to the same (config option) in
|
||||
* little-endian format, unless set in policy
|
||||
*/
|
||||
ima_putc(m, &pcr, sizeof(pcr));
|
||||
ima_putc(m, &e->pcr, sizeof(e->pcr));
|
||||
|
||||
/* 2nd: template digest */
|
||||
ima_putc(m, e->digest, TPM_DIGEST_SIZE);
|
||||
@@ -219,7 +218,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
|
||||
e->template_desc->name : e->template_desc->fmt;
|
||||
|
||||
/* 1st: PCR used (config option) */
|
||||
seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
|
||||
seq_printf(m, "%2d ", e->pcr);
|
||||
|
||||
/* 2nd: SHA1 template hash */
|
||||
ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user