mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-25 06:03:36 -04:00
Merge branch 'master' into for-next
Sync up with Linus' tree to be able to apply Cesar's patch against newer version of the code. Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
commit
3bd7bf1f0f
13374 changed files with 701046 additions and 382099 deletions
|
|
@ -57,9 +57,9 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \
|
|||
|
||||
$(obj)/capability.o : $(obj)/capability_names.h
|
||||
$(obj)/resource.o : $(obj)/rlim_names.h
|
||||
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h \
|
||||
$(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \
|
||||
$(src)/Makefile
|
||||
$(call cmd,make-caps)
|
||||
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h \
|
||||
$(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \
|
||||
$(src)/Makefile
|
||||
$(call cmd,make-rlim)
|
||||
|
|
|
|||
|
|
@ -721,7 +721,7 @@ audit:
|
|||
if (!permtest)
|
||||
error = aa_audit_file(profile, &perms, GFP_KERNEL,
|
||||
OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL,
|
||||
target, 0, info, error);
|
||||
target, GLOBAL_ROOT_UID, info, error);
|
||||
|
||||
out:
|
||||
aa_put_profile(hat);
|
||||
|
|
@ -848,7 +848,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
|||
audit:
|
||||
if (!permtest)
|
||||
error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
|
||||
name, hname, 0, info, error);
|
||||
name, hname, GLOBAL_ROOT_UID, info, error);
|
||||
|
||||
aa_put_namespace(ns);
|
||||
aa_put_profile(target);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ static void audit_file_mask(struct audit_buffer *ab, u32 mask)
|
|||
static void file_audit_cb(struct audit_buffer *ab, void *va)
|
||||
{
|
||||
struct common_audit_data *sa = va;
|
||||
uid_t fsuid = current_fsuid();
|
||||
kuid_t fsuid = current_fsuid();
|
||||
|
||||
if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
|
||||
audit_log_format(ab, " requested_mask=");
|
||||
|
|
@ -76,8 +76,10 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
|
|||
audit_file_mask(ab, sa->aad->fs.denied);
|
||||
}
|
||||
if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
|
||||
audit_log_format(ab, " fsuid=%d", fsuid);
|
||||
audit_log_format(ab, " ouid=%d", sa->aad->fs.ouid);
|
||||
audit_log_format(ab, " fsuid=%d",
|
||||
from_kuid(&init_user_ns, fsuid));
|
||||
audit_log_format(ab, " ouid=%d",
|
||||
from_kuid(&init_user_ns, sa->aad->fs.ouid));
|
||||
}
|
||||
|
||||
if (sa->aad->fs.target) {
|
||||
|
|
@ -103,7 +105,7 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
|
|||
*/
|
||||
int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
|
||||
gfp_t gfp, int op, u32 request, const char *name,
|
||||
const char *target, uid_t ouid, const char *info, int error)
|
||||
const char *target, kuid_t ouid, const char *info, int error)
|
||||
{
|
||||
int type = AUDIT_APPARMOR_AUTO;
|
||||
struct common_audit_data sa;
|
||||
|
|
@ -201,7 +203,7 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state,
|
|||
*/
|
||||
perms.kill = 0;
|
||||
|
||||
if (current_fsuid() == cond->uid) {
|
||||
if (uid_eq(current_fsuid(), cond->uid)) {
|
||||
perms.allow = map_old_perms(dfa_user_allow(dfa, state));
|
||||
perms.audit = map_old_perms(dfa_user_audit(dfa, state));
|
||||
perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ struct apparmor_audit_data {
|
|||
const char *target;
|
||||
u32 request;
|
||||
u32 denied;
|
||||
uid_t ouid;
|
||||
kuid_t ouid;
|
||||
} fs;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ struct path;
|
|||
|
||||
/* need to make conditional which ones are being set */
|
||||
struct path_cond {
|
||||
uid_t uid;
|
||||
kuid_t uid;
|
||||
umode_t mode;
|
||||
};
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ static inline u16 dfa_map_xindex(u16 mask)
|
|||
|
||||
int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
|
||||
gfp_t gfp, int op, u32 request, const char *name,
|
||||
const char *target, uid_t ouid, const char *info, int error);
|
||||
const char *target, kuid_t ouid, const char *info, int error);
|
||||
|
||||
/**
|
||||
* struct aa_file_rules - components used for file rule permissions
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ static int apparmor_path_chmod(struct path *path, umode_t mode)
|
|||
return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD);
|
||||
}
|
||||
|
||||
static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid)
|
||||
static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
struct path_cond cond = { path->dentry->d_inode->i_uid,
|
||||
path->dentry->d_inode->i_mode
|
||||
|
|
|
|||
|
|
@ -724,6 +724,8 @@ fail:
|
|||
*/
|
||||
static void free_profile(struct aa_profile *profile)
|
||||
{
|
||||
struct aa_profile *p;
|
||||
|
||||
AA_DEBUG("%s(%p)\n", __func__, profile);
|
||||
|
||||
if (!profile)
|
||||
|
|
@ -751,7 +753,27 @@ static void free_profile(struct aa_profile *profile)
|
|||
aa_put_dfa(profile->xmatch);
|
||||
aa_put_dfa(profile->policy.dfa);
|
||||
|
||||
aa_put_profile(profile->replacedby);
|
||||
/* put the profile reference for replacedby, but not via
|
||||
* put_profile(kref_put).
|
||||
* replacedby can form a long chain that can result in cascading
|
||||
* frees that blows the stack because kref_put makes a nested fn
|
||||
* call (it looks like recursion, with free_profile calling
|
||||
* free_profile) for each profile in the chain lp#1056078.
|
||||
*/
|
||||
for (p = profile->replacedby; p; ) {
|
||||
if (atomic_dec_and_test(&p->base.count.refcount)) {
|
||||
/* no more refs on p, grab its replacedby */
|
||||
struct aa_profile *next = p->replacedby;
|
||||
/* break the chain */
|
||||
p->replacedby = NULL;
|
||||
/* now free p, chain is broken */
|
||||
free_profile(p);
|
||||
|
||||
/* follow up with next profile in the chain */
|
||||
p = next;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
kzfree(profile);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ static int cap_sb_statfs(struct dentry *dentry)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cap_sb_mount(char *dev_name, struct path *path, char *type,
|
||||
unsigned long flags, void *data)
|
||||
static int cap_sb_mount(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -284,7 +284,7 @@ static int cap_path_chmod(struct path *path, umode_t mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cap_path_chown(struct path *path, uid_t uid, gid_t gid)
|
||||
static int cap_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@
|
|||
static DEFINE_MUTEX(devcgroup_mutex);
|
||||
|
||||
/*
|
||||
* whitelist locking rules:
|
||||
* exception list locking rules:
|
||||
* hold devcgroup_mutex for update/read.
|
||||
* hold rcu_read_lock() for read.
|
||||
*/
|
||||
|
||||
struct dev_whitelist_item {
|
||||
struct dev_exception_item {
|
||||
u32 major, minor;
|
||||
short type;
|
||||
short access;
|
||||
|
|
@ -41,7 +41,11 @@ struct dev_whitelist_item {
|
|||
|
||||
struct dev_cgroup {
|
||||
struct cgroup_subsys_state css;
|
||||
struct list_head whitelist;
|
||||
struct list_head exceptions;
|
||||
enum {
|
||||
DEVCG_DEFAULT_ALLOW,
|
||||
DEVCG_DEFAULT_DENY,
|
||||
} behavior;
|
||||
};
|
||||
|
||||
static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
|
||||
|
|
@ -74,12 +78,12 @@ static int devcgroup_can_attach(struct cgroup *new_cgrp,
|
|||
/*
|
||||
* called under devcgroup_mutex
|
||||
*/
|
||||
static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
|
||||
static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
|
||||
{
|
||||
struct dev_whitelist_item *wh, *tmp, *new;
|
||||
struct dev_exception_item *ex, *tmp, *new;
|
||||
|
||||
list_for_each_entry(wh, orig, list) {
|
||||
new = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
|
||||
list_for_each_entry(ex, orig, list) {
|
||||
new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
|
||||
if (!new)
|
||||
goto free_and_exit;
|
||||
list_add_tail(&new->list, dest);
|
||||
|
|
@ -88,64 +92,60 @@ static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
|
|||
return 0;
|
||||
|
||||
free_and_exit:
|
||||
list_for_each_entry_safe(wh, tmp, dest, list) {
|
||||
list_del(&wh->list);
|
||||
kfree(wh);
|
||||
list_for_each_entry_safe(ex, tmp, dest, list) {
|
||||
list_del(&ex->list);
|
||||
kfree(ex);
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Stupid prototype - don't bother combining existing entries */
|
||||
/*
|
||||
* called under devcgroup_mutex
|
||||
*/
|
||||
static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
|
||||
struct dev_whitelist_item *wh)
|
||||
static int dev_exception_add(struct dev_cgroup *dev_cgroup,
|
||||
struct dev_exception_item *ex)
|
||||
{
|
||||
struct dev_whitelist_item *whcopy, *walk;
|
||||
struct dev_exception_item *excopy, *walk;
|
||||
|
||||
whcopy = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
|
||||
if (!whcopy)
|
||||
excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
|
||||
if (!excopy)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry(walk, &dev_cgroup->whitelist, list) {
|
||||
if (walk->type != wh->type)
|
||||
list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
|
||||
if (walk->type != ex->type)
|
||||
continue;
|
||||
if (walk->major != wh->major)
|
||||
if (walk->major != ex->major)
|
||||
continue;
|
||||
if (walk->minor != wh->minor)
|
||||
if (walk->minor != ex->minor)
|
||||
continue;
|
||||
|
||||
walk->access |= wh->access;
|
||||
kfree(whcopy);
|
||||
whcopy = NULL;
|
||||
walk->access |= ex->access;
|
||||
kfree(excopy);
|
||||
excopy = NULL;
|
||||
}
|
||||
|
||||
if (whcopy != NULL)
|
||||
list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist);
|
||||
if (excopy != NULL)
|
||||
list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* called under devcgroup_mutex
|
||||
*/
|
||||
static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
|
||||
struct dev_whitelist_item *wh)
|
||||
static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
|
||||
struct dev_exception_item *ex)
|
||||
{
|
||||
struct dev_whitelist_item *walk, *tmp;
|
||||
struct dev_exception_item *walk, *tmp;
|
||||
|
||||
list_for_each_entry_safe(walk, tmp, &dev_cgroup->whitelist, list) {
|
||||
if (walk->type == DEV_ALL)
|
||||
goto remove;
|
||||
if (walk->type != wh->type)
|
||||
list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
|
||||
if (walk->type != ex->type)
|
||||
continue;
|
||||
if (walk->major != ~0 && walk->major != wh->major)
|
||||
if (walk->major != ex->major)
|
||||
continue;
|
||||
if (walk->minor != ~0 && walk->minor != wh->minor)
|
||||
if (walk->minor != ex->minor)
|
||||
continue;
|
||||
|
||||
remove:
|
||||
walk->access &= ~wh->access;
|
||||
walk->access &= ~ex->access;
|
||||
if (!walk->access) {
|
||||
list_del_rcu(&walk->list);
|
||||
kfree_rcu(walk, rcu);
|
||||
|
|
@ -153,6 +153,22 @@ remove:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_exception_clean - frees all entries of the exception list
|
||||
* @dev_cgroup: dev_cgroup with the exception list to be cleaned
|
||||
*
|
||||
* called under devcgroup_mutex
|
||||
*/
|
||||
static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
|
||||
{
|
||||
struct dev_exception_item *ex, *tmp;
|
||||
|
||||
list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
|
||||
list_del(&ex->list);
|
||||
kfree(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called from kernel/cgroup.c with cgroup_lock() held.
|
||||
*/
|
||||
|
|
@ -165,25 +181,17 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup)
|
|||
dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
|
||||
if (!dev_cgroup)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
INIT_LIST_HEAD(&dev_cgroup->whitelist);
|
||||
INIT_LIST_HEAD(&dev_cgroup->exceptions);
|
||||
parent_cgroup = cgroup->parent;
|
||||
|
||||
if (parent_cgroup == NULL) {
|
||||
struct dev_whitelist_item *wh;
|
||||
wh = kmalloc(sizeof(*wh), GFP_KERNEL);
|
||||
if (!wh) {
|
||||
kfree(dev_cgroup);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
wh->minor = wh->major = ~0;
|
||||
wh->type = DEV_ALL;
|
||||
wh->access = ACC_MASK;
|
||||
list_add(&wh->list, &dev_cgroup->whitelist);
|
||||
} else {
|
||||
if (parent_cgroup == NULL)
|
||||
dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
|
||||
else {
|
||||
parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
|
||||
mutex_lock(&devcgroup_mutex);
|
||||
ret = dev_whitelist_copy(&dev_cgroup->whitelist,
|
||||
&parent_dev_cgroup->whitelist);
|
||||
ret = dev_exceptions_copy(&dev_cgroup->exceptions,
|
||||
&parent_dev_cgroup->exceptions);
|
||||
dev_cgroup->behavior = parent_dev_cgroup->behavior;
|
||||
mutex_unlock(&devcgroup_mutex);
|
||||
if (ret) {
|
||||
kfree(dev_cgroup);
|
||||
|
|
@ -197,13 +205,9 @@ static struct cgroup_subsys_state *devcgroup_create(struct cgroup *cgroup)
|
|||
static void devcgroup_destroy(struct cgroup *cgroup)
|
||||
{
|
||||
struct dev_cgroup *dev_cgroup;
|
||||
struct dev_whitelist_item *wh, *tmp;
|
||||
|
||||
dev_cgroup = cgroup_to_devcgroup(cgroup);
|
||||
list_for_each_entry_safe(wh, tmp, &dev_cgroup->whitelist, list) {
|
||||
list_del(&wh->list);
|
||||
kfree(wh);
|
||||
}
|
||||
dev_exception_clean(dev_cgroup);
|
||||
kfree(dev_cgroup);
|
||||
}
|
||||
|
||||
|
|
@ -249,59 +253,87 @@ static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
|
|||
struct seq_file *m)
|
||||
{
|
||||
struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup);
|
||||
struct dev_whitelist_item *wh;
|
||||
struct dev_exception_item *ex;
|
||||
char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) {
|
||||
set_access(acc, wh->access);
|
||||
set_majmin(maj, wh->major);
|
||||
set_majmin(min, wh->minor);
|
||||
seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
|
||||
/*
|
||||
* To preserve the compatibility:
|
||||
* - Only show the "all devices" when the default policy is to allow
|
||||
* - List the exceptions in case the default policy is to deny
|
||||
* This way, the file remains as a "whitelist of devices"
|
||||
*/
|
||||
if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
|
||||
set_access(acc, ACC_MASK);
|
||||
set_majmin(maj, ~0);
|
||||
set_majmin(min, ~0);
|
||||
seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL),
|
||||
maj, min, acc);
|
||||
} else {
|
||||
list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
|
||||
set_access(acc, ex->access);
|
||||
set_majmin(maj, ex->major);
|
||||
set_majmin(min, ex->minor);
|
||||
seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
|
||||
maj, min, acc);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* may_access_whitelist:
|
||||
* does the access granted to dev_cgroup c contain the access
|
||||
* requested in whitelist item refwh.
|
||||
* return 1 if yes, 0 if no.
|
||||
* call with devcgroup_mutex held
|
||||
/**
|
||||
* may_access - verifies if a new exception is part of what is allowed
|
||||
* by a dev cgroup based on the default policy +
|
||||
* exceptions. This is used to make sure a child cgroup
|
||||
* won't have more privileges than its parent or to
|
||||
* verify if a certain access is allowed.
|
||||
* @dev_cgroup: dev cgroup to be tested against
|
||||
* @refex: new exception
|
||||
*/
|
||||
static int may_access_whitelist(struct dev_cgroup *c,
|
||||
struct dev_whitelist_item *refwh)
|
||||
static int may_access(struct dev_cgroup *dev_cgroup,
|
||||
struct dev_exception_item *refex)
|
||||
{
|
||||
struct dev_whitelist_item *whitem;
|
||||
struct dev_exception_item *ex;
|
||||
bool match = false;
|
||||
|
||||
list_for_each_entry(whitem, &c->whitelist, list) {
|
||||
if (whitem->type & DEV_ALL)
|
||||
return 1;
|
||||
if ((refwh->type & DEV_BLOCK) && !(whitem->type & DEV_BLOCK))
|
||||
list_for_each_entry(ex, &dev_cgroup->exceptions, list) {
|
||||
if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
|
||||
continue;
|
||||
if ((refwh->type & DEV_CHAR) && !(whitem->type & DEV_CHAR))
|
||||
if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
|
||||
continue;
|
||||
if (whitem->major != ~0 && whitem->major != refwh->major)
|
||||
if (ex->major != ~0 && ex->major != refex->major)
|
||||
continue;
|
||||
if (whitem->minor != ~0 && whitem->minor != refwh->minor)
|
||||
if (ex->minor != ~0 && ex->minor != refex->minor)
|
||||
continue;
|
||||
if (refwh->access & (~whitem->access))
|
||||
if (refex->access & (~ex->access))
|
||||
continue;
|
||||
return 1;
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* In two cases we'll consider this new exception valid:
|
||||
* - the dev cgroup has its default policy to allow + exception list:
|
||||
* the new exception should *not* match any of the exceptions
|
||||
* (behavior == DEVCG_DEFAULT_ALLOW, !match)
|
||||
* - the dev cgroup has its default policy to deny + exception list:
|
||||
* the new exception *should* match the exceptions
|
||||
* (behavior == DEVCG_DEFAULT_DENY, match)
|
||||
*/
|
||||
if ((dev_cgroup->behavior == DEVCG_DEFAULT_DENY) == match)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* parent_has_perm:
|
||||
* when adding a new allow rule to a device whitelist, the rule
|
||||
* when adding a new allow rule to a device exception list, the rule
|
||||
* must be allowed in the parent device
|
||||
*/
|
||||
static int parent_has_perm(struct dev_cgroup *childcg,
|
||||
struct dev_whitelist_item *wh)
|
||||
struct dev_exception_item *ex)
|
||||
{
|
||||
struct cgroup *pcg = childcg->css.cgroup->parent;
|
||||
struct dev_cgroup *parent;
|
||||
|
|
@ -309,17 +341,28 @@ static int parent_has_perm(struct dev_cgroup *childcg,
|
|||
if (!pcg)
|
||||
return 1;
|
||||
parent = cgroup_to_devcgroup(pcg);
|
||||
return may_access_whitelist(parent, wh);
|
||||
return may_access(parent, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* may_allow_all - checks if it's possible to change the behavior to
|
||||
* allow based on parent's rules.
|
||||
* @parent: device cgroup's parent
|
||||
* returns: != 0 in case it's allowed, 0 otherwise
|
||||
*/
|
||||
static inline int may_allow_all(struct dev_cgroup *parent)
|
||||
{
|
||||
return parent->behavior == DEVCG_DEFAULT_ALLOW;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the whitelist using allow/deny rules.
|
||||
* Modify the exception list using allow/deny rules.
|
||||
* CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
|
||||
* so we can give a container CAP_MKNOD to let it create devices but not
|
||||
* modify the whitelist.
|
||||
* modify the exception list.
|
||||
* It seems likely we'll want to add a CAP_CONTAINER capability to allow
|
||||
* us to also grant CAP_SYS_ADMIN to containers without giving away the
|
||||
* device whitelist controls, but for now we'll stick with CAP_SYS_ADMIN
|
||||
* device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
|
||||
*
|
||||
* Taking rules away is always allowed (given CAP_SYS_ADMIN). Granting
|
||||
* new access is only allowed if you're in the top-level cgroup, or your
|
||||
|
|
@ -329,28 +372,44 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
|
|||
int filetype, const char *buffer)
|
||||
{
|
||||
const char *b;
|
||||
char *endp;
|
||||
int count;
|
||||
struct dev_whitelist_item wh;
|
||||
char temp[12]; /* 11 + 1 characters needed for a u32 */
|
||||
int count, rc;
|
||||
struct dev_exception_item ex;
|
||||
struct cgroup *p = devcgroup->css.cgroup;
|
||||
struct dev_cgroup *parent = cgroup_to_devcgroup(p->parent);
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
memset(&wh, 0, sizeof(wh));
|
||||
memset(&ex, 0, sizeof(ex));
|
||||
b = buffer;
|
||||
|
||||
switch (*b) {
|
||||
case 'a':
|
||||
wh.type = DEV_ALL;
|
||||
wh.access = ACC_MASK;
|
||||
wh.major = ~0;
|
||||
wh.minor = ~0;
|
||||
goto handle;
|
||||
switch (filetype) {
|
||||
case DEVCG_ALLOW:
|
||||
if (!may_allow_all(parent))
|
||||
return -EPERM;
|
||||
dev_exception_clean(devcgroup);
|
||||
rc = dev_exceptions_copy(&devcgroup->exceptions,
|
||||
&parent->exceptions);
|
||||
if (rc)
|
||||
return rc;
|
||||
devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
|
||||
break;
|
||||
case DEVCG_DENY:
|
||||
dev_exception_clean(devcgroup);
|
||||
devcgroup->behavior = DEVCG_DEFAULT_DENY;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
case 'b':
|
||||
wh.type = DEV_BLOCK;
|
||||
ex.type = DEV_BLOCK;
|
||||
break;
|
||||
case 'c':
|
||||
wh.type = DEV_CHAR;
|
||||
ex.type = DEV_CHAR;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -360,11 +419,19 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
|
|||
return -EINVAL;
|
||||
b++;
|
||||
if (*b == '*') {
|
||||
wh.major = ~0;
|
||||
ex.major = ~0;
|
||||
b++;
|
||||
} else if (isdigit(*b)) {
|
||||
wh.major = simple_strtoul(b, &endp, 10);
|
||||
b = endp;
|
||||
memset(temp, 0, sizeof(temp));
|
||||
for (count = 0; count < sizeof(temp) - 1; count++) {
|
||||
temp[count] = *b;
|
||||
b++;
|
||||
if (!isdigit(*b))
|
||||
break;
|
||||
}
|
||||
rc = kstrtou32(temp, 10, &ex.major);
|
||||
if (rc)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -374,11 +441,19 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
|
|||
|
||||
/* read minor */
|
||||
if (*b == '*') {
|
||||
wh.minor = ~0;
|
||||
ex.minor = ~0;
|
||||
b++;
|
||||
} else if (isdigit(*b)) {
|
||||
wh.minor = simple_strtoul(b, &endp, 10);
|
||||
b = endp;
|
||||
memset(temp, 0, sizeof(temp));
|
||||
for (count = 0; count < sizeof(temp) - 1; count++) {
|
||||
temp[count] = *b;
|
||||
b++;
|
||||
if (!isdigit(*b))
|
||||
break;
|
||||
}
|
||||
rc = kstrtou32(temp, 10, &ex.minor);
|
||||
if (rc)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -387,13 +462,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
|
|||
for (b++, count = 0; count < 3; count++, b++) {
|
||||
switch (*b) {
|
||||
case 'r':
|
||||
wh.access |= ACC_READ;
|
||||
ex.access |= ACC_READ;
|
||||
break;
|
||||
case 'w':
|
||||
wh.access |= ACC_WRITE;
|
||||
ex.access |= ACC_WRITE;
|
||||
break;
|
||||
case 'm':
|
||||
wh.access |= ACC_MKNOD;
|
||||
ex.access |= ACC_MKNOD;
|
||||
break;
|
||||
case '\n':
|
||||
case '\0':
|
||||
|
|
@ -404,15 +479,31 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
|
|||
}
|
||||
}
|
||||
|
||||
handle:
|
||||
switch (filetype) {
|
||||
case DEVCG_ALLOW:
|
||||
if (!parent_has_perm(devcgroup, &wh))
|
||||
if (!parent_has_perm(devcgroup, &ex))
|
||||
return -EPERM;
|
||||
return dev_whitelist_add(devcgroup, &wh);
|
||||
/*
|
||||
* If the default policy is to allow by default, try to remove
|
||||
* an matching exception instead. And be silent about it: we
|
||||
* don't want to break compatibility
|
||||
*/
|
||||
if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
|
||||
dev_exception_rm(devcgroup, &ex);
|
||||
return 0;
|
||||
}
|
||||
return dev_exception_add(devcgroup, &ex);
|
||||
case DEVCG_DENY:
|
||||
dev_whitelist_rm(devcgroup, &wh);
|
||||
break;
|
||||
/*
|
||||
* If the default policy is to deny by default, try to remove
|
||||
* an matching exception instead. And be silent about it: we
|
||||
* don't want to break compatibility
|
||||
*/
|
||||
if (devcgroup->behavior == DEVCG_DEFAULT_DENY) {
|
||||
dev_exception_rm(devcgroup, &ex);
|
||||
return 0;
|
||||
}
|
||||
return dev_exception_add(devcgroup, &ex);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -457,75 +548,81 @@ struct cgroup_subsys devices_subsys = {
|
|||
.destroy = devcgroup_destroy,
|
||||
.subsys_id = devices_subsys_id,
|
||||
.base_cftypes = dev_cgroup_files,
|
||||
|
||||
/*
|
||||
* While devices cgroup has the rudimentary hierarchy support which
|
||||
* checks the parent's restriction, it doesn't properly propagates
|
||||
* config changes in ancestors to their descendents. A child
|
||||
* should only be allowed to add more restrictions to the parent's
|
||||
* configuration. Fix it and remove the following.
|
||||
*/
|
||||
.broken_hierarchy = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* __devcgroup_check_permission - checks if an inode operation is permitted
|
||||
* @dev_cgroup: the dev cgroup to be tested against
|
||||
* @type: device type
|
||||
* @major: device major number
|
||||
* @minor: device minor number
|
||||
* @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD
|
||||
*
|
||||
* returns 0 on success, -EPERM case the operation is not permitted
|
||||
*/
|
||||
static int __devcgroup_check_permission(short type, u32 major, u32 minor,
|
||||
short access)
|
||||
{
|
||||
struct dev_cgroup *dev_cgroup;
|
||||
struct dev_exception_item ex;
|
||||
int rc;
|
||||
|
||||
memset(&ex, 0, sizeof(ex));
|
||||
ex.type = type;
|
||||
ex.major = major;
|
||||
ex.minor = minor;
|
||||
ex.access = access;
|
||||
|
||||
rcu_read_lock();
|
||||
dev_cgroup = task_devcgroup(current);
|
||||
rc = may_access(dev_cgroup, &ex);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!rc)
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __devcgroup_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
struct dev_cgroup *dev_cgroup;
|
||||
struct dev_whitelist_item *wh;
|
||||
short type, access = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
if (S_ISBLK(inode->i_mode))
|
||||
type = DEV_BLOCK;
|
||||
if (S_ISCHR(inode->i_mode))
|
||||
type = DEV_CHAR;
|
||||
if (mask & MAY_WRITE)
|
||||
access |= ACC_WRITE;
|
||||
if (mask & MAY_READ)
|
||||
access |= ACC_READ;
|
||||
|
||||
dev_cgroup = task_devcgroup(current);
|
||||
|
||||
list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
|
||||
if (wh->type & DEV_ALL)
|
||||
goto found;
|
||||
if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
|
||||
continue;
|
||||
if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))
|
||||
continue;
|
||||
if (wh->major != ~0 && wh->major != imajor(inode))
|
||||
continue;
|
||||
if (wh->minor != ~0 && wh->minor != iminor(inode))
|
||||
continue;
|
||||
|
||||
if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))
|
||||
continue;
|
||||
if ((mask & MAY_READ) && !(wh->access & ACC_READ))
|
||||
continue;
|
||||
found:
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return -EPERM;
|
||||
return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
|
||||
access);
|
||||
}
|
||||
|
||||
int devcgroup_inode_mknod(int mode, dev_t dev)
|
||||
{
|
||||
struct dev_cgroup *dev_cgroup;
|
||||
struct dev_whitelist_item *wh;
|
||||
short type;
|
||||
|
||||
if (!S_ISBLK(mode) && !S_ISCHR(mode))
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
if (S_ISBLK(mode))
|
||||
type = DEV_BLOCK;
|
||||
else
|
||||
type = DEV_CHAR;
|
||||
|
||||
dev_cgroup = task_devcgroup(current);
|
||||
return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
|
||||
ACC_MKNOD);
|
||||
|
||||
list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
|
||||
if (wh->type & DEV_ALL)
|
||||
goto found;
|
||||
if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode))
|
||||
continue;
|
||||
if ((wh->type & DEV_CHAR) && !S_ISCHR(mode))
|
||||
continue;
|
||||
if (wh->major != ~0 && wh->major != MAJOR(dev))
|
||||
continue;
|
||||
if (wh->minor != ~0 && wh->minor != MINOR(dev))
|
||||
continue;
|
||||
|
||||
if (!(wh->access & ACC_MKNOD))
|
||||
continue;
|
||||
found:
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
|
|||
memset(&hmac_misc, 0, sizeof hmac_misc);
|
||||
hmac_misc.ino = inode->i_ino;
|
||||
hmac_misc.generation = inode->i_generation;
|
||||
hmac_misc.uid = inode->i_uid;
|
||||
hmac_misc.gid = inode->i_gid;
|
||||
hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid);
|
||||
hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
|
||||
hmac_misc.mode = inode->i_mode;
|
||||
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
|
||||
crypto_shash_final(desc, digest);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ char *evm_config_xattrnames[] = {
|
|||
#endif
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
XATTR_NAME_SMACK,
|
||||
#endif
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
XATTR_NAME_IMA,
|
||||
#endif
|
||||
XATTR_NAME_CAPS,
|
||||
NULL
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "integrity.h"
|
||||
|
||||
static struct rb_root integrity_iint_tree = RB_ROOT;
|
||||
static DEFINE_SPINLOCK(integrity_iint_lock);
|
||||
static DEFINE_RWLOCK(integrity_iint_lock);
|
||||
static struct kmem_cache *iint_cache __read_mostly;
|
||||
|
||||
int iint_initialized;
|
||||
|
|
@ -35,8 +35,6 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
|
|||
struct integrity_iint_cache *iint;
|
||||
struct rb_node *n = integrity_iint_tree.rb_node;
|
||||
|
||||
assert_spin_locked(&integrity_iint_lock);
|
||||
|
||||
while (n) {
|
||||
iint = rb_entry(n, struct integrity_iint_cache, rb_node);
|
||||
|
||||
|
|
@ -63,9 +61,9 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
|
|||
if (!IS_IMA(inode))
|
||||
return NULL;
|
||||
|
||||
spin_lock(&integrity_iint_lock);
|
||||
read_lock(&integrity_iint_lock);
|
||||
iint = __integrity_iint_find(inode);
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
read_unlock(&integrity_iint_lock);
|
||||
|
||||
return iint;
|
||||
}
|
||||
|
|
@ -74,59 +72,53 @@ static void iint_free(struct integrity_iint_cache *iint)
|
|||
{
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
iint->ima_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
kmem_cache_free(iint_cache, iint);
|
||||
}
|
||||
|
||||
/**
|
||||
* integrity_inode_alloc - allocate an iint associated with an inode
|
||||
* integrity_inode_get - find or allocate an iint associated with an inode
|
||||
* @inode: pointer to the inode
|
||||
* @return: allocated iint
|
||||
*
|
||||
* Caller must lock i_mutex
|
||||
*/
|
||||
int integrity_inode_alloc(struct inode *inode)
|
||||
struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
|
||||
{
|
||||
struct rb_node **p;
|
||||
struct rb_node *new_node, *parent = NULL;
|
||||
struct integrity_iint_cache *new_iint, *test_iint;
|
||||
int rc;
|
||||
struct rb_node *node, *parent = NULL;
|
||||
struct integrity_iint_cache *iint, *test_iint;
|
||||
|
||||
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
|
||||
if (!new_iint)
|
||||
return -ENOMEM;
|
||||
iint = integrity_iint_find(inode);
|
||||
if (iint)
|
||||
return iint;
|
||||
|
||||
new_iint->inode = inode;
|
||||
new_node = &new_iint->rb_node;
|
||||
iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
|
||||
if (!iint)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&inode->i_mutex); /* i_flags */
|
||||
spin_lock(&integrity_iint_lock);
|
||||
write_lock(&integrity_iint_lock);
|
||||
|
||||
p = &integrity_iint_tree.rb_node;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
test_iint = rb_entry(parent, struct integrity_iint_cache,
|
||||
rb_node);
|
||||
rc = -EEXIST;
|
||||
if (inode < test_iint->inode)
|
||||
p = &(*p)->rb_left;
|
||||
else if (inode > test_iint->inode)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
goto out_err;
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
iint->inode = inode;
|
||||
node = &iint->rb_node;
|
||||
inode->i_flags |= S_IMA;
|
||||
rb_link_node(new_node, parent, p);
|
||||
rb_insert_color(new_node, &integrity_iint_tree);
|
||||
rb_link_node(node, parent, p);
|
||||
rb_insert_color(node, &integrity_iint_tree);
|
||||
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
mutex_unlock(&inode->i_mutex); /* i_flags */
|
||||
|
||||
return 0;
|
||||
out_err:
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
mutex_unlock(&inode->i_mutex); /* i_flags */
|
||||
iint_free(new_iint);
|
||||
|
||||
return rc;
|
||||
write_unlock(&integrity_iint_lock);
|
||||
return iint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,10 +134,10 @@ void integrity_inode_free(struct inode *inode)
|
|||
if (!IS_IMA(inode))
|
||||
return;
|
||||
|
||||
spin_lock(&integrity_iint_lock);
|
||||
write_lock(&integrity_iint_lock);
|
||||
iint = __integrity_iint_find(inode);
|
||||
rb_erase(&iint->rb_node, &integrity_iint_tree);
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
write_unlock(&integrity_iint_lock);
|
||||
|
||||
iint_free(iint);
|
||||
}
|
||||
|
|
@ -157,7 +149,7 @@ static void init_once(void *foo)
|
|||
memset(iint, 0, sizeof *iint);
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
mutex_init(&iint->mutex);
|
||||
iint->ima_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ config IMA
|
|||
select CRYPTO_SHA1
|
||||
select TCG_TPM if HAS_IOMEM && !UML
|
||||
select TCG_TIS if TCG_TPM && X86
|
||||
select TCG_IBMVTPM if TCG_TPM && PPC64
|
||||
help
|
||||
The Trusted Computing Group(TCG) runtime Integrity
|
||||
Measurement Architecture(IMA) maintains a list of hash
|
||||
|
|
@ -55,3 +56,18 @@ config IMA_LSM_RULES
|
|||
default y
|
||||
help
|
||||
Disabling this option will disregard LSM based policy rules.
|
||||
|
||||
config IMA_APPRAISE
|
||||
bool "Appraise integrity measurements"
|
||||
depends on IMA
|
||||
default n
|
||||
help
|
||||
This option enables local measurement integrity appraisal.
|
||||
It requires the system to be labeled with a security extended
|
||||
attribute containing the file hash measurement. To protect
|
||||
the security extended attributes from offline attack, enable
|
||||
and configure EVM.
|
||||
|
||||
For more information on integrity appraisal refer to:
|
||||
<http://linux-ima.sourceforge.net>
|
||||
If unsure, say N.
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
|
|||
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
|
||||
ima_policy.o
|
||||
ima-$(CONFIG_IMA_AUDIT) += ima_audit.o
|
||||
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
|
|||
extern int ima_initialized;
|
||||
extern int ima_used_chip;
|
||||
extern char *ima_hash;
|
||||
extern int ima_appraise;
|
||||
|
||||
/* IMA inode template definition */
|
||||
struct ima_template_data {
|
||||
|
|
@ -107,11 +108,14 @@ static inline unsigned long ima_hash_key(u8 *digest)
|
|||
}
|
||||
|
||||
/* LIM API function definitions */
|
||||
int ima_get_action(struct inode *inode, int mask, int function);
|
||||
int ima_must_measure(struct inode *inode, int mask, int function);
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file);
|
||||
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename);
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename);
|
||||
int ima_store_template(struct ima_template_entry *entry, int violation,
|
||||
struct inode *inode);
|
||||
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
|
||||
|
|
@ -123,14 +127,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
|
|||
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
||||
|
||||
/* IMA policy related functions */
|
||||
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
|
||||
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR };
|
||||
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags);
|
||||
void ima_init_policy(void);
|
||||
void ima_update_policy(void);
|
||||
ssize_t ima_parse_add_rule(char *);
|
||||
void ima_delete_rules(void);
|
||||
|
||||
/* Appraise integrity measurements */
|
||||
#define IMA_APPRAISE_ENFORCE 0x01
|
||||
#define IMA_APPRAISE_FIX 0x02
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename);
|
||||
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
||||
|
||||
#else
|
||||
static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
return INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline int ima_must_appraise(struct inode *inode, int mask,
|
||||
enum ima_hooks func)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ima_update_xattr(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* LSM based policy rules require audit */
|
||||
#ifdef CONFIG_IMA_LSM_RULES
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,17 @@
|
|||
* License.
|
||||
*
|
||||
* File: ima_api.c
|
||||
* Implements must_measure, collect_measurement, store_measurement,
|
||||
* and store_template.
|
||||
* Implements must_appraise_or_measure, collect_measurement,
|
||||
* appraise_measurement, store_measurement and store_template.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/evm.h>
|
||||
#include "ima.h"
|
||||
|
||||
static const char *IMA_TEMPLATE_NAME = "ima";
|
||||
|
||||
/*
|
||||
|
|
@ -93,7 +97,7 @@ err_out:
|
|||
}
|
||||
|
||||
/**
|
||||
* ima_must_measure - measure decision based on policy.
|
||||
* ima_get_action - appraise & measure decision based on policy.
|
||||
* @inode: pointer to inode to measure
|
||||
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
|
||||
* @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
|
||||
|
|
@ -105,15 +109,22 @@ err_out:
|
|||
* mask: contains the permission mask
|
||||
* fsmagic: hex value
|
||||
*
|
||||
* Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
|
||||
* or other error, return an error code.
|
||||
*/
|
||||
* Returns IMA_MEASURE, IMA_APPRAISE mask.
|
||||
*
|
||||
*/
|
||||
int ima_get_action(struct inode *inode, int mask, int function)
|
||||
{
|
||||
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
|
||||
|
||||
if (!ima_appraise)
|
||||
flags &= ~IMA_APPRAISE;
|
||||
|
||||
return ima_match_policy(inode, function, mask, flags);
|
||||
}
|
||||
|
||||
int ima_must_measure(struct inode *inode, int mask, int function)
|
||||
{
|
||||
int must_measure;
|
||||
|
||||
must_measure = ima_match_policy(inode, function, mask);
|
||||
return must_measure ? 0 : -EACCES;
|
||||
return ima_match_policy(inode, function, mask, IMA_MEASURE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function)
|
|||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
{
|
||||
int result = -EEXIST;
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
const char *filename = file->f_dentry->d_name.name;
|
||||
int result = 0;
|
||||
|
||||
if (!(iint->flags & IMA_MEASURED)) {
|
||||
if (!(iint->flags & IMA_COLLECTED)) {
|
||||
u64 i_version = file->f_dentry->d_inode->i_version;
|
||||
|
||||
memset(iint->digest, 0, IMA_DIGEST_SIZE);
|
||||
result = ima_calc_hash(file, iint->digest);
|
||||
if (!result)
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
result = ima_calc_hash(file, iint->ima_xattr.digest);
|
||||
if (!result) {
|
||||
iint->version = i_version;
|
||||
iint->flags |= IMA_COLLECTED;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
|
||||
filename, "collect_data", "failed",
|
||||
result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
|||
struct ima_template_entry *entry;
|
||||
int violation = 0;
|
||||
|
||||
if (iint->flags & IMA_MEASURED)
|
||||
return;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
|
||||
|
|
@ -174,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
|||
return;
|
||||
}
|
||||
memset(&entry->template, 0, sizeof(entry->template));
|
||||
memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
|
||||
memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
|
||||
strcpy(entry->template.file_name,
|
||||
(strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
|
||||
file->f_dentry->d_name.name : filename);
|
||||
|
|
@ -185,3 +207,33 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
|||
if (result < 0)
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char hash[(IMA_DIGEST_SIZE * 2) + 1];
|
||||
int i;
|
||||
|
||||
if (iint->flags & IMA_AUDITED)
|
||||
return;
|
||||
|
||||
for (i = 0; i < IMA_DIGEST_SIZE; i++)
|
||||
hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]);
|
||||
hash[i * 2] = '\0';
|
||||
|
||||
ab = audit_log_start(current->audit_context, GFP_KERNEL,
|
||||
AUDIT_INTEGRITY_RULE);
|
||||
if (!ab)
|
||||
return;
|
||||
|
||||
audit_log_format(ab, "file=");
|
||||
audit_log_untrustedstring(ab, filename);
|
||||
audit_log_format(ab, " hash=");
|
||||
audit_log_untrustedstring(ab, hash);
|
||||
|
||||
audit_log_task_info(ab, current);
|
||||
audit_log_end(ab);
|
||||
|
||||
iint->flags |= IMA_AUDITED;
|
||||
}
|
||||
|
|
|
|||
263
security/integrity/ima/ima_appraise.c
Normal file
263
security/integrity/ima/ima_appraise.c
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (C) 2011 IBM Corporation
|
||||
*
|
||||
* Author:
|
||||
* Mimi Zohar <zohar@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/ima.h>
|
||||
#include <linux/evm.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
static int __init default_appraise_setup(char *str)
|
||||
{
|
||||
if (strncmp(str, "off", 3) == 0)
|
||||
ima_appraise = 0;
|
||||
else if (strncmp(str, "fix", 3) == 0)
|
||||
ima_appraise = IMA_APPRAISE_FIX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("ima_appraise=", default_appraise_setup);
|
||||
|
||||
/*
|
||||
* ima_must_appraise - set appraise flag
|
||||
*
|
||||
* Return 1 to appraise
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
static void ima_fix_xattr(struct dentry *dentry,
|
||||
struct integrity_iint_cache *iint)
|
||||
{
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
__vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
|
||||
sizeof iint->ima_xattr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_appraise_measurement - appraise file measurement
|
||||
*
|
||||
* Call evm_verifyxattr() to verify the integrity of 'security.ima'.
|
||||
* Assuming success, compare the xattr hash with the collected measurement.
|
||||
*
|
||||
* Return 0 on success, error code otherwise
|
||||
*/
|
||||
int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct evm_ima_xattr_data *xattr_value = NULL;
|
||||
enum integrity_status status = INTEGRITY_UNKNOWN;
|
||||
const char *op = "appraise_data";
|
||||
char *cause = "unknown";
|
||||
int rc;
|
||||
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
if (!inode->i_op->getxattr)
|
||||
return INTEGRITY_UNKNOWN;
|
||||
|
||||
if (iint->flags & IMA_APPRAISED)
|
||||
return iint->ima_status;
|
||||
|
||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
|
||||
0, GFP_NOFS);
|
||||
if (rc <= 0) {
|
||||
if (rc && rc != -ENODATA)
|
||||
goto out;
|
||||
|
||||
cause = "missing-hash";
|
||||
status =
|
||||
(inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
|
||||
if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
|
||||
if ((status == INTEGRITY_NOLABEL)
|
||||
|| (status == INTEGRITY_NOXATTRS))
|
||||
cause = "missing-HMAC";
|
||||
else if (status == INTEGRITY_FAIL)
|
||||
cause = "invalid-HMAC";
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (xattr_value->type) {
|
||||
case IMA_XATTR_DIGEST:
|
||||
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (rc) {
|
||||
cause = "invalid-hash";
|
||||
status = INTEGRITY_FAIL;
|
||||
print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
|
||||
xattr_value, sizeof(*xattr_value));
|
||||
print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
|
||||
(u8 *)&iint->ima_xattr,
|
||||
sizeof iint->ima_xattr);
|
||||
break;
|
||||
}
|
||||
status = INTEGRITY_PASS;
|
||||
break;
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
iint->flags |= IMA_DIGSIG;
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||
xattr_value->digest, rc - 1,
|
||||
iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
} else if (rc) {
|
||||
cause = "invalid-signature";
|
||||
status = INTEGRITY_FAIL;
|
||||
} else {
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
cause = "unknown-ima-data";
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (status != INTEGRITY_PASS) {
|
||||
if ((ima_appraise & IMA_APPRAISE_FIX) &&
|
||||
(!xattr_value ||
|
||||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
||||
ima_fix_xattr(dentry, iint);
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
|
||||
op, cause, rc, 0);
|
||||
} else {
|
||||
iint->flags |= IMA_APPRAISED;
|
||||
}
|
||||
iint->ima_status = status;
|
||||
kfree(xattr_value);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_update_xattr - update 'security.ima' hash value
|
||||
*/
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
int rc = 0;
|
||||
|
||||
/* do not collect and update hash for digital signatures */
|
||||
if (iint->flags & IMA_DIGSIG)
|
||||
return;
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
ima_fix_xattr(dentry, iint);
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_inode_post_setattr - reflect file metadata changes
|
||||
* @dentry: pointer to the affected dentry
|
||||
*
|
||||
* Changes to a dentry's metadata might result in needing to appraise.
|
||||
*
|
||||
* This function is called from notify_change(), which expects the caller
|
||||
* to lock the inode's i_mutex.
|
||||
*/
|
||||
void ima_inode_post_setattr(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
int must_appraise, rc;
|
||||
|
||||
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
|
||||
|| !inode->i_op->removexattr)
|
||||
return;
|
||||
|
||||
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
||||
iint = integrity_iint_find(inode);
|
||||
if (iint) {
|
||||
if (must_appraise)
|
||||
iint->flags |= IMA_APPRAISE;
|
||||
else
|
||||
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
|
||||
}
|
||||
if (!must_appraise)
|
||||
rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_protect_xattr - protect 'security.ima'
|
||||
*
|
||||
* Ensure that not just anyone can modify or remove 'security.ima'.
|
||||
*/
|
||||
static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len)
|
||||
{
|
||||
if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ima_reset_appraise_flags(struct inode *inode)
|
||||
{
|
||||
struct integrity_iint_cache *iint;
|
||||
|
||||
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
|
||||
return;
|
||||
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint)
|
||||
return;
|
||||
|
||||
iint->flags &= ~IMA_DONE_MASK;
|
||||
return;
|
||||
}
|
||||
|
||||
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
|
||||
xattr_value_len);
|
||||
if (result == 1) {
|
||||
ima_reset_appraise_flags(dentry->d_inode);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
|
||||
if (result == 1) {
|
||||
ima_reset_appraise_flags(dentry->d_inode);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -39,8 +39,9 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
|
|||
|
||||
ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
|
||||
audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
|
||||
current->pid, current_cred()->uid,
|
||||
audit_get_loginuid(current),
|
||||
current->pid,
|
||||
from_kuid(&init_user_ns, current_cred()->uid),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
audit_log_task_context(ab);
|
||||
audit_log_format(ab, " op=");
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest)
|
|||
struct scatterlist sg[1];
|
||||
loff_t i_size, offset = 0;
|
||||
char *rbuf;
|
||||
int rc;
|
||||
int rc, read = 0;
|
||||
|
||||
rc = init_desc(&desc);
|
||||
if (rc != 0)
|
||||
|
|
@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest)
|
|||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
file->f_mode |= FMODE_READ;
|
||||
read = 1;
|
||||
}
|
||||
i_size = i_size_read(file->f_dentry->d_inode);
|
||||
while (offset < i_size) {
|
||||
int rbuf_len;
|
||||
|
|
@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest)
|
|||
kfree(rbuf);
|
||||
if (!rc)
|
||||
rc = crypto_hash_final(&desc, digest);
|
||||
if (read)
|
||||
file->f_mode &= ~FMODE_READ;
|
||||
out:
|
||||
crypto_free_hash(desc.tfm);
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -22,12 +22,19 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
int ima_initialized;
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise = IMA_APPRAISE_ENFORCE;
|
||||
#else
|
||||
int ima_appraise;
|
||||
#endif
|
||||
|
||||
char *ima_hash = "sha1";
|
||||
static int __init hash_setup(char *str)
|
||||
{
|
||||
|
|
@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file)
|
|||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
fmode_t mode = file->f_mode;
|
||||
int rc;
|
||||
int must_measure;
|
||||
bool send_tomtou = false, send_writers = false;
|
||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
||||
|
||||
|
|
@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
|
||||
if (rc < 0)
|
||||
must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
|
||||
if (!must_measure)
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&inode->i_writecount) > 0)
|
||||
|
|
@ -100,17 +107,21 @@ out:
|
|||
}
|
||||
|
||||
static void ima_check_last_writer(struct integrity_iint_cache *iint,
|
||||
struct inode *inode,
|
||||
struct file *file)
|
||||
struct inode *inode, struct file *file)
|
||||
{
|
||||
fmode_t mode = file->f_mode;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
if (mode & FMODE_WRITE &&
|
||||
atomic_read(&inode->i_writecount) == 1 &&
|
||||
iint->version != inode->i_version)
|
||||
iint->flags &= ~IMA_MEASURED;
|
||||
mutex_unlock(&iint->mutex);
|
||||
if (!(mode & FMODE_WRITE))
|
||||
return;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
if (atomic_read(&inode->i_writecount) == 1 &&
|
||||
iint->version != inode->i_version) {
|
||||
iint->flags &= ~IMA_DONE_MASK;
|
||||
if (iint->flags & IMA_APPRAISE)
|
||||
ima_update_xattr(iint, file);
|
||||
}
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -140,29 +151,38 @@ static int process_measurement(struct file *file, const unsigned char *filename,
|
|||
struct inode *inode = file->f_dentry->d_inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
||||
int rc = 0;
|
||||
int rc = -ENOMEM, action, must_appraise;
|
||||
|
||||
if (!ima_initialized || !S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
rc = ima_must_measure(inode, mask, function);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
retry:
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint) {
|
||||
rc = integrity_inode_alloc(inode);
|
||||
if (!rc || rc == -EEXIST)
|
||||
goto retry;
|
||||
return rc;
|
||||
}
|
||||
/* Determine if in appraise/audit/measurement policy,
|
||||
* returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */
|
||||
action = ima_get_action(inode, mask, function);
|
||||
if (!action)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
must_appraise = action & IMA_APPRAISE;
|
||||
|
||||
rc = iint->flags & IMA_MEASURED ? 1 : 0;
|
||||
if (rc != 0)
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
iint = integrity_inode_get(inode);
|
||||
if (!iint)
|
||||
goto out;
|
||||
|
||||
/* Determine if already appraised/measured based on bitmask
|
||||
* (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
|
||||
* IMA_AUDIT, IMA_AUDITED) */
|
||||
iint->flags |= action;
|
||||
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
|
||||
|
||||
/* Nothing to do, just return existing appraised status */
|
||||
if (!action) {
|
||||
if (iint->flags & IMA_APPRAISED)
|
||||
rc = iint->ima_status;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
|
@ -177,11 +197,18 @@ retry:
|
|||
pathname = NULL;
|
||||
}
|
||||
}
|
||||
ima_store_measurement(iint, file, !pathname ? filename : pathname);
|
||||
if (action & IMA_MEASURE)
|
||||
ima_store_measurement(iint, file,
|
||||
!pathname ? filename : pathname);
|
||||
if (action & IMA_APPRAISE)
|
||||
rc = ima_appraise_measurement(iint, file,
|
||||
!pathname ? filename : pathname);
|
||||
if (action & IMA_AUDIT)
|
||||
ima_audit_measurement(iint, !pathname ? filename : pathname);
|
||||
kfree(pathbuf);
|
||||
out:
|
||||
mutex_unlock(&iint->mutex);
|
||||
return rc;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return (rc && must_appraise) ? -EACCES : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,14 +224,14 @@ out:
|
|||
*/
|
||||
int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
{
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
if (prot & PROT_EXEC)
|
||||
rc = process_measurement(file, file->f_dentry->d_name.name,
|
||||
MAY_EXEC, FILE_MMAP);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -228,7 +255,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
|
|||
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
||||
bprm->filename : bprm->interp,
|
||||
MAY_EXEC, BPRM_CHECK);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -249,7 +276,7 @@ int ima_file_check(struct file *file, int mask)
|
|||
rc = process_measurement(file, file->f_dentry->d_name.name,
|
||||
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
FILE_CHECK);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,22 +24,29 @@
|
|||
#define IMA_MASK 0x0002
|
||||
#define IMA_FSMAGIC 0x0004
|
||||
#define IMA_UID 0x0008
|
||||
#define IMA_FOWNER 0x0010
|
||||
|
||||
enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
|
||||
#define UNKNOWN 0
|
||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||
#define DONT_MEASURE 0x0002
|
||||
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
|
||||
#define DONT_APPRAISE 0x0008
|
||||
#define AUDIT 0x0040
|
||||
|
||||
#define MAX_LSM_RULES 6
|
||||
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
|
||||
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
|
||||
};
|
||||
|
||||
struct ima_measure_rule_entry {
|
||||
struct ima_rule_entry {
|
||||
struct list_head list;
|
||||
enum ima_action action;
|
||||
int action;
|
||||
unsigned int flags;
|
||||
enum ima_hooks func;
|
||||
int mask;
|
||||
unsigned long fsmagic;
|
||||
uid_t uid;
|
||||
kuid_t uid;
|
||||
kuid_t fowner;
|
||||
struct {
|
||||
void *rule; /* LSM file metadata specific */
|
||||
int type; /* audit type */
|
||||
|
|
@ -48,7 +55,7 @@ struct ima_measure_rule_entry {
|
|||
|
||||
/*
|
||||
* Without LSM specific knowledge, the default policy can only be
|
||||
* written in terms of .action, .func, .mask, .fsmagic, and .uid
|
||||
* written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
@ -57,7 +64,7 @@ struct ima_measure_rule_entry {
|
|||
* normal users can easily run the machine out of memory simply building
|
||||
* and running executables.
|
||||
*/
|
||||
static struct ima_measure_rule_entry default_rules[] = {
|
||||
static struct ima_rule_entry default_rules[] = {
|
||||
{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
|
|
@ -71,23 +78,45 @@ static struct ima_measure_rule_entry default_rules[] = {
|
|||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
|
||||
.flags = IMA_FUNC | IMA_MASK},
|
||||
{.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = 0,
|
||||
{.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = GLOBAL_ROOT_UID,
|
||||
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
};
|
||||
|
||||
static LIST_HEAD(measure_default_rules);
|
||||
static LIST_HEAD(measure_policy_rules);
|
||||
static struct list_head *ima_measure;
|
||||
static struct ima_rule_entry default_appraise_rules[] = {
|
||||
{.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = APPRAISE,.fowner = GLOBAL_ROOT_UID,.flags = IMA_FOWNER},
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ima_measure_mutex);
|
||||
static LIST_HEAD(ima_default_rules);
|
||||
static LIST_HEAD(ima_policy_rules);
|
||||
static struct list_head *ima_rules;
|
||||
|
||||
static DEFINE_MUTEX(ima_rules_mutex);
|
||||
|
||||
static bool ima_use_tcb __initdata;
|
||||
static int __init default_policy_setup(char *str)
|
||||
static int __init default_measure_policy_setup(char *str)
|
||||
{
|
||||
ima_use_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_tcb", default_policy_setup);
|
||||
__setup("ima_tcb", default_measure_policy_setup);
|
||||
|
||||
static bool ima_use_appraise_tcb __initdata;
|
||||
static int __init default_appraise_policy_setup(char *str)
|
||||
{
|
||||
ima_use_appraise_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_appraise_tcb", default_appraise_policy_setup);
|
||||
|
||||
/**
|
||||
* ima_match_rules - determine whether an inode matches the measure rule.
|
||||
|
|
@ -98,7 +127,7 @@ __setup("ima_tcb", default_policy_setup);
|
|||
*
|
||||
* Returns true on rule match, false on failure.
|
||||
*/
|
||||
static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
static bool ima_match_rules(struct ima_rule_entry *rule,
|
||||
struct inode *inode, enum ima_hooks func, int mask)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
|
|
@ -112,7 +141,9 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
|||
if ((rule->flags & IMA_FSMAGIC)
|
||||
&& rule->fsmagic != inode->i_sb->s_magic)
|
||||
return false;
|
||||
if ((rule->flags & IMA_UID) && rule->uid != cred->uid)
|
||||
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
|
||||
return false;
|
||||
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
|
||||
return false;
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
int rc = 0;
|
||||
|
|
@ -163,39 +194,61 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
|||
* as elements in the list are never deleted, nor does the list
|
||||
* change.)
|
||||
*/
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags)
|
||||
{
|
||||
struct ima_measure_rule_entry *entry;
|
||||
struct ima_rule_entry *entry;
|
||||
int action = 0, actmask = flags | (flags << 1);
|
||||
|
||||
list_for_each_entry(entry, ima_measure, list) {
|
||||
bool rc;
|
||||
list_for_each_entry(entry, ima_rules, list) {
|
||||
|
||||
rc = ima_match_rules(entry, inode, func, mask);
|
||||
if (rc)
|
||||
return entry->action;
|
||||
if (!(entry->action & actmask))
|
||||
continue;
|
||||
|
||||
if (!ima_match_rules(entry, inode, func, mask))
|
||||
continue;
|
||||
|
||||
action |= entry->action & IMA_DO_MASK;
|
||||
if (entry->action & IMA_DO_MASK)
|
||||
actmask &= ~(entry->action | entry->action << 1);
|
||||
else
|
||||
actmask &= ~(entry->action | entry->action >> 1);
|
||||
|
||||
if (!actmask)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_init_policy - initialize the default measure rules.
|
||||
*
|
||||
* ima_measure points to either the measure_default_rules or the
|
||||
* the new measure_policy_rules.
|
||||
* ima_rules points to either the ima_default_rules or the
|
||||
* the new ima_policy_rules.
|
||||
*/
|
||||
void __init ima_init_policy(void)
|
||||
{
|
||||
int i, entries;
|
||||
int i, measure_entries, appraise_entries;
|
||||
|
||||
/* if !ima_use_tcb set entries = 0 so we load NO default rules */
|
||||
if (ima_use_tcb)
|
||||
entries = ARRAY_SIZE(default_rules);
|
||||
else
|
||||
entries = 0;
|
||||
measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;
|
||||
appraise_entries = ima_use_appraise_tcb ?
|
||||
ARRAY_SIZE(default_appraise_rules) : 0;
|
||||
|
||||
for (i = 0; i < measure_entries + appraise_entries; i++) {
|
||||
if (i < measure_entries)
|
||||
list_add_tail(&default_rules[i].list,
|
||||
&ima_default_rules);
|
||||
else {
|
||||
int j = i - measure_entries;
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
list_add_tail(&default_rules[i].list, &measure_default_rules);
|
||||
ima_measure = &measure_default_rules;
|
||||
list_add_tail(&default_appraise_rules[j].list,
|
||||
&ima_default_rules);
|
||||
}
|
||||
}
|
||||
|
||||
ima_rules = &ima_default_rules;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -212,8 +265,8 @@ void ima_update_policy(void)
|
|||
int result = 1;
|
||||
int audit_info = 0;
|
||||
|
||||
if (ima_measure == &measure_default_rules) {
|
||||
ima_measure = &measure_policy_rules;
|
||||
if (ima_rules == &ima_default_rules) {
|
||||
ima_rules = &ima_policy_rules;
|
||||
cause = "complete";
|
||||
result = 0;
|
||||
}
|
||||
|
|
@ -224,14 +277,19 @@ void ima_update_policy(void)
|
|||
enum {
|
||||
Opt_err = -1,
|
||||
Opt_measure = 1, Opt_dont_measure,
|
||||
Opt_appraise, Opt_dont_appraise,
|
||||
Opt_audit,
|
||||
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
||||
Opt_subj_user, Opt_subj_role, Opt_subj_type,
|
||||
Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
|
||||
Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner
|
||||
};
|
||||
|
||||
static match_table_t policy_tokens = {
|
||||
{Opt_measure, "measure"},
|
||||
{Opt_dont_measure, "dont_measure"},
|
||||
{Opt_appraise, "appraise"},
|
||||
{Opt_dont_appraise, "dont_appraise"},
|
||||
{Opt_audit, "audit"},
|
||||
{Opt_obj_user, "obj_user=%s"},
|
||||
{Opt_obj_role, "obj_role=%s"},
|
||||
{Opt_obj_type, "obj_type=%s"},
|
||||
|
|
@ -242,10 +300,11 @@ static match_table_t policy_tokens = {
|
|||
{Opt_mask, "mask=%s"},
|
||||
{Opt_fsmagic, "fsmagic=%s"},
|
||||
{Opt_uid, "uid=%s"},
|
||||
{Opt_fowner, "fowner=%s"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
|
||||
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
|
||||
char *args, int lsm_rule, int audit_type)
|
||||
{
|
||||
int result;
|
||||
|
|
@ -269,7 +328,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
|
|||
audit_log_format(ab, " ");
|
||||
}
|
||||
|
||||
static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char *p;
|
||||
|
|
@ -277,7 +336,8 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
|
||||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
|
||||
|
||||
entry->uid = -1;
|
||||
entry->uid = INVALID_UID;
|
||||
entry->fowner = INVALID_UID;
|
||||
entry->action = UNKNOWN;
|
||||
while ((p = strsep(&rule, " \t")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
|
@ -306,11 +366,35 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
|
||||
entry->action = DONT_MEASURE;
|
||||
break;
|
||||
case Opt_appraise:
|
||||
ima_log_string(ab, "action", "appraise");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = APPRAISE;
|
||||
break;
|
||||
case Opt_dont_appraise:
|
||||
ima_log_string(ab, "action", "dont_appraise");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = DONT_APPRAISE;
|
||||
break;
|
||||
case Opt_audit:
|
||||
ima_log_string(ab, "action", "audit");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = AUDIT;
|
||||
break;
|
||||
case Opt_func:
|
||||
ima_log_string(ab, "func", args[0].from);
|
||||
|
||||
if (entry->func)
|
||||
result = -EINVAL;
|
||||
result = -EINVAL;
|
||||
|
||||
if (strcmp(args[0].from, "FILE_CHECK") == 0)
|
||||
entry->func = FILE_CHECK;
|
||||
|
|
@ -361,20 +445,37 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
case Opt_uid:
|
||||
ima_log_string(ab, "uid", args[0].from);
|
||||
|
||||
if (entry->uid != -1) {
|
||||
if (uid_valid(entry->uid)) {
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
result = strict_strtoul(args[0].from, 10, &lnum);
|
||||
if (!result) {
|
||||
entry->uid = (uid_t) lnum;
|
||||
if (entry->uid != lnum)
|
||||
entry->uid = make_kuid(current_user_ns(), (uid_t)lnum);
|
||||
if (!uid_valid(entry->uid) || (((uid_t)lnum) != lnum))
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= IMA_UID;
|
||||
}
|
||||
break;
|
||||
case Opt_fowner:
|
||||
ima_log_string(ab, "fowner", args[0].from);
|
||||
|
||||
if (uid_valid(entry->fowner)) {
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
result = strict_strtoul(args[0].from, 10, &lnum);
|
||||
if (!result) {
|
||||
entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);
|
||||
if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum))
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= IMA_FOWNER;
|
||||
}
|
||||
break;
|
||||
case Opt_obj_user:
|
||||
ima_log_string(ab, "obj_user", args[0].from);
|
||||
result = ima_lsm_rule_init(entry, args[0].from,
|
||||
|
|
@ -426,7 +527,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
}
|
||||
|
||||
/**
|
||||
* ima_parse_add_rule - add a rule to measure_policy_rules
|
||||
* ima_parse_add_rule - add a rule to ima_policy_rules
|
||||
* @rule - ima measurement policy rule
|
||||
*
|
||||
* Uses a mutex to protect the policy list from multiple concurrent writers.
|
||||
|
|
@ -436,12 +537,12 @@ ssize_t ima_parse_add_rule(char *rule)
|
|||
{
|
||||
const char *op = "update_policy";
|
||||
char *p;
|
||||
struct ima_measure_rule_entry *entry;
|
||||
struct ima_rule_entry *entry;
|
||||
ssize_t result, len;
|
||||
int audit_info = 0;
|
||||
|
||||
/* Prevent installed policy from changing */
|
||||
if (ima_measure != &measure_default_rules) {
|
||||
if (ima_rules != &ima_default_rules) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
|
||||
NULL, op, "already exists",
|
||||
-EACCES, audit_info);
|
||||
|
|
@ -474,9 +575,9 @@ ssize_t ima_parse_add_rule(char *rule)
|
|||
return result;
|
||||
}
|
||||
|
||||
mutex_lock(&ima_measure_mutex);
|
||||
list_add_tail(&entry->list, &measure_policy_rules);
|
||||
mutex_unlock(&ima_measure_mutex);
|
||||
mutex_lock(&ima_rules_mutex);
|
||||
list_add_tail(&entry->list, &ima_policy_rules);
|
||||
mutex_unlock(&ima_rules_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
|
@ -484,12 +585,12 @@ ssize_t ima_parse_add_rule(char *rule)
|
|||
/* ima_delete_rules called to cleanup invalid policy */
|
||||
void ima_delete_rules(void)
|
||||
{
|
||||
struct ima_measure_rule_entry *entry, *tmp;
|
||||
struct ima_rule_entry *entry, *tmp;
|
||||
|
||||
mutex_lock(&ima_measure_mutex);
|
||||
list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
|
||||
mutex_lock(&ima_rules_mutex);
|
||||
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
mutex_unlock(&ima_measure_mutex);
|
||||
mutex_unlock(&ima_rules_mutex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,22 @@
|
|||
#include <linux/integrity.h>
|
||||
#include <crypto/sha.h>
|
||||
|
||||
/* iint action cache flags */
|
||||
#define IMA_MEASURE 0x0001
|
||||
#define IMA_MEASURED 0x0002
|
||||
#define IMA_APPRAISE 0x0004
|
||||
#define IMA_APPRAISED 0x0008
|
||||
/*#define IMA_COLLECT 0x0010 do not use this flag */
|
||||
#define IMA_COLLECTED 0x0020
|
||||
#define IMA_AUDIT 0x0040
|
||||
#define IMA_AUDITED 0x0080
|
||||
|
||||
/* iint cache flags */
|
||||
#define IMA_MEASURED 0x01
|
||||
#define IMA_DIGSIG 0x0100
|
||||
|
||||
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT)
|
||||
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
|
||||
| IMA_COLLECTED)
|
||||
|
||||
enum evm_ima_xattr_type {
|
||||
IMA_XATTR_DIGEST = 0x01,
|
||||
|
|
@ -34,9 +48,9 @@ struct integrity_iint_cache {
|
|||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||
struct inode *inode; /* back pointer to inode in question */
|
||||
u64 version; /* track inode changes */
|
||||
unsigned char flags;
|
||||
u8 digest[SHA1_DIGEST_SIZE];
|
||||
struct mutex mutex; /* protects: version, flags, digest */
|
||||
unsigned short flags;
|
||||
struct evm_ima_xattr_data ima_xattr;
|
||||
enum integrity_status ima_status;
|
||||
enum integrity_status evm_status;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
|
|||
*
|
||||
* On success, return 0. Otherwise return errno.
|
||||
*/
|
||||
static int encrypted_instantiate(struct key *key, const void *data,
|
||||
size_t datalen)
|
||||
static int encrypted_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct encrypted_key_payload *epayload = NULL;
|
||||
char *datablob = NULL;
|
||||
|
|
@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data,
|
|||
char *master_desc = NULL;
|
||||
char *decrypted_datalen = NULL;
|
||||
char *hex_encoded_iv = NULL;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
if (!datablob)
|
||||
return -ENOMEM;
|
||||
datablob[datalen] = 0;
|
||||
memcpy(datablob, data, datalen);
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
ret = datablob_parse(datablob, &format, &master_desc,
|
||||
&decrypted_datalen, &hex_encoded_iv);
|
||||
if (ret < 0)
|
||||
|
|
@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu)
|
|||
*
|
||||
* On success, return 0. Otherwise return errno.
|
||||
*/
|
||||
static int encrypted_update(struct key *key, const void *data, size_t datalen)
|
||||
static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct encrypted_key_payload *epayload = key->payload.data;
|
||||
struct encrypted_key_payload *new_epayload;
|
||||
char *buf;
|
||||
char *new_master_desc = NULL;
|
||||
const char *format = NULL;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret = 0;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
buf = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
|
|
@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
|
|||
return -ENOMEM;
|
||||
|
||||
buf[datalen] = 0;
|
||||
memcpy(buf, data, datalen);
|
||||
memcpy(buf, prep->data, datalen);
|
||||
ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ void key_schedule_gc(time_t gc_at)
|
|||
|
||||
if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) {
|
||||
kdebug("IMMEDIATE");
|
||||
queue_work(system_nrt_wq, &key_gc_work);
|
||||
schedule_work(&key_gc_work);
|
||||
} else if (gc_at < key_gc_next_run) {
|
||||
kdebug("DEFERRED");
|
||||
key_gc_next_run = gc_at;
|
||||
|
|
@ -77,7 +77,7 @@ void key_schedule_gc(time_t gc_at)
|
|||
void key_schedule_gc_links(void)
|
||||
{
|
||||
set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags);
|
||||
queue_work(system_nrt_wq, &key_gc_work);
|
||||
schedule_work(&key_gc_work);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -120,7 +120,7 @@ void key_gc_keytype(struct key_type *ktype)
|
|||
set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags);
|
||||
|
||||
kdebug("schedule");
|
||||
queue_work(system_nrt_wq, &key_gc_work);
|
||||
schedule_work(&key_gc_work);
|
||||
|
||||
kdebug("sleep");
|
||||
wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit,
|
||||
|
|
@ -369,7 +369,7 @@ maybe_resched:
|
|||
}
|
||||
|
||||
if (gc_state & KEY_GC_REAP_AGAIN)
|
||||
queue_work(system_nrt_wq, &key_gc_work);
|
||||
schedule_work(&key_gc_work);
|
||||
kleave(" [end %x]", gc_state);
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ struct key_user {
|
|||
atomic_t usage; /* for accessing qnkeys & qnbytes */
|
||||
atomic_t nkeys; /* number of keys */
|
||||
atomic_t nikeys; /* number of instantiated keys */
|
||||
uid_t uid;
|
||||
struct user_namespace *user_ns;
|
||||
kuid_t uid;
|
||||
int qnkeys; /* number of keys allocated to this user */
|
||||
int qnbytes; /* number of bytes allocated to this user */
|
||||
};
|
||||
|
|
@ -62,8 +61,7 @@ extern struct rb_root key_user_tree;
|
|||
extern spinlock_t key_user_lock;
|
||||
extern struct key_user root_key_user;
|
||||
|
||||
extern struct key_user *key_user_lookup(uid_t uid,
|
||||
struct user_namespace *user_ns);
|
||||
extern struct key_user *key_user_lookup(kuid_t uid);
|
||||
extern void key_user_put(struct key_user *user);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include "internal.h"
|
||||
|
||||
struct kmem_cache *key_jar;
|
||||
|
|
@ -52,7 +51,7 @@ void __key_check(const struct key *key)
|
|||
* Get the key quota record for a user, allocating a new record if one doesn't
|
||||
* already exist.
|
||||
*/
|
||||
struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)
|
||||
struct key_user *key_user_lookup(kuid_t uid)
|
||||
{
|
||||
struct key_user *candidate = NULL, *user;
|
||||
struct rb_node *parent = NULL;
|
||||
|
|
@ -67,13 +66,9 @@ try_again:
|
|||
parent = *p;
|
||||
user = rb_entry(parent, struct key_user, node);
|
||||
|
||||
if (uid < user->uid)
|
||||
if (uid_lt(uid, user->uid))
|
||||
p = &(*p)->rb_left;
|
||||
else if (uid > user->uid)
|
||||
p = &(*p)->rb_right;
|
||||
else if (user_ns < user->user_ns)
|
||||
p = &(*p)->rb_left;
|
||||
else if (user_ns > user->user_ns)
|
||||
else if (uid_gt(uid, user->uid))
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
goto found;
|
||||
|
|
@ -102,7 +97,6 @@ try_again:
|
|||
atomic_set(&candidate->nkeys, 0);
|
||||
atomic_set(&candidate->nikeys, 0);
|
||||
candidate->uid = uid;
|
||||
candidate->user_ns = get_user_ns(user_ns);
|
||||
candidate->qnkeys = 0;
|
||||
candidate->qnbytes = 0;
|
||||
spin_lock_init(&candidate->lock);
|
||||
|
|
@ -131,7 +125,6 @@ void key_user_put(struct key_user *user)
|
|||
if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
|
||||
rb_erase(&user->node, &key_user_tree);
|
||||
spin_unlock(&key_user_lock);
|
||||
put_user_ns(user->user_ns);
|
||||
|
||||
kfree(user);
|
||||
}
|
||||
|
|
@ -229,7 +222,7 @@ serial_exists:
|
|||
* key_alloc() calls don't race with module unloading.
|
||||
*/
|
||||
struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
uid_t uid, gid_t gid, const struct cred *cred,
|
||||
kuid_t uid, kgid_t gid, const struct cred *cred,
|
||||
key_perm_t perm, unsigned long flags)
|
||||
{
|
||||
struct key_user *user = NULL;
|
||||
|
|
@ -253,16 +246,16 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
|||
quotalen = desclen + type->def_datalen;
|
||||
|
||||
/* get hold of the key tracking for this user */
|
||||
user = key_user_lookup(uid, cred->user_ns);
|
||||
user = key_user_lookup(uid);
|
||||
if (!user)
|
||||
goto no_memory_1;
|
||||
|
||||
/* check that the user's quota permits allocation of another key and
|
||||
* its description */
|
||||
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
|
||||
unsigned maxkeys = (uid == 0) ?
|
||||
unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxkeys : key_quota_maxkeys;
|
||||
unsigned maxbytes = (uid == 0) ?
|
||||
unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxbytes : key_quota_maxbytes;
|
||||
|
||||
spin_lock(&user->lock);
|
||||
|
|
@ -380,7 +373,7 @@ int key_payload_reserve(struct key *key, size_t datalen)
|
|||
|
||||
/* contemplate the quota adjustment */
|
||||
if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
|
||||
unsigned maxbytes = (key->user->uid == 0) ?
|
||||
unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxbytes : key_quota_maxbytes;
|
||||
|
||||
spin_lock(&key->user->lock);
|
||||
|
|
@ -412,8 +405,7 @@ EXPORT_SYMBOL(key_payload_reserve);
|
|||
* key_construction_mutex.
|
||||
*/
|
||||
static int __key_instantiate_and_link(struct key *key,
|
||||
const void *data,
|
||||
size_t datalen,
|
||||
struct key_preparsed_payload *prep,
|
||||
struct key *keyring,
|
||||
struct key *authkey,
|
||||
unsigned long *_prealloc)
|
||||
|
|
@ -431,7 +423,7 @@ static int __key_instantiate_and_link(struct key *key,
|
|||
/* can't instantiate twice */
|
||||
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
|
||||
/* instantiate the key */
|
||||
ret = key->type->instantiate(key, data, datalen);
|
||||
ret = key->type->instantiate(key, prep);
|
||||
|
||||
if (ret == 0) {
|
||||
/* mark the key as being instantiated */
|
||||
|
|
@ -482,22 +474,37 @@ int key_instantiate_and_link(struct key *key,
|
|||
struct key *keyring,
|
||||
struct key *authkey)
|
||||
{
|
||||
struct key_preparsed_payload prep;
|
||||
unsigned long prealloc;
|
||||
int ret;
|
||||
|
||||
memset(&prep, 0, sizeof(prep));
|
||||
prep.data = data;
|
||||
prep.datalen = datalen;
|
||||
prep.quotalen = key->type->def_datalen;
|
||||
if (key->type->preparse) {
|
||||
ret = key->type->preparse(&prep);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (keyring) {
|
||||
ret = __key_link_begin(keyring, key->type, key->description,
|
||||
&prealloc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto error_free_preparse;
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
|
||||
&prealloc);
|
||||
|
||||
if (keyring)
|
||||
__key_link_end(keyring, key->type, prealloc);
|
||||
|
||||
error_free_preparse:
|
||||
if (key->type->preparse)
|
||||
key->type->free_preparse(&prep);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -598,7 +605,7 @@ void key_put(struct key *key)
|
|||
key_check(key);
|
||||
|
||||
if (atomic_dec_and_test(&key->usage))
|
||||
queue_work(system_nrt_wq, &key_gc_work);
|
||||
schedule_work(&key_gc_work);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(key_put);
|
||||
|
|
@ -706,7 +713,7 @@ void key_type_put(struct key_type *ktype)
|
|||
* if we get an error.
|
||||
*/
|
||||
static inline key_ref_t __key_update(key_ref_t key_ref,
|
||||
const void *payload, size_t plen)
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct key *key = key_ref_to_ptr(key_ref);
|
||||
int ret;
|
||||
|
|
@ -722,7 +729,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
|
|||
|
||||
down_write(&key->sem);
|
||||
|
||||
ret = key->type->update(key, payload, plen);
|
||||
ret = key->type->update(key, prep);
|
||||
if (ret == 0)
|
||||
/* updating a negative key instantiates it */
|
||||
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
||||
|
|
@ -774,6 +781,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
unsigned long flags)
|
||||
{
|
||||
unsigned long prealloc;
|
||||
struct key_preparsed_payload prep;
|
||||
const struct cred *cred = current_cred();
|
||||
struct key_type *ktype;
|
||||
struct key *keyring, *key = NULL;
|
||||
|
|
@ -789,8 +797,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
}
|
||||
|
||||
key_ref = ERR_PTR(-EINVAL);
|
||||
if (!ktype->match || !ktype->instantiate)
|
||||
goto error_2;
|
||||
if (!ktype->match || !ktype->instantiate ||
|
||||
(!description && !ktype->preparse))
|
||||
goto error_put_type;
|
||||
|
||||
keyring = key_ref_to_ptr(keyring_ref);
|
||||
|
||||
|
|
@ -798,18 +807,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
|
||||
key_ref = ERR_PTR(-ENOTDIR);
|
||||
if (keyring->type != &key_type_keyring)
|
||||
goto error_2;
|
||||
goto error_put_type;
|
||||
|
||||
memset(&prep, 0, sizeof(prep));
|
||||
prep.data = payload;
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = ktype->def_datalen;
|
||||
if (ktype->preparse) {
|
||||
ret = ktype->preparse(&prep);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_put_type;
|
||||
}
|
||||
if (!description)
|
||||
description = prep.description;
|
||||
key_ref = ERR_PTR(-EINVAL);
|
||||
if (!description)
|
||||
goto error_free_prep;
|
||||
}
|
||||
|
||||
ret = __key_link_begin(keyring, ktype, description, &prealloc);
|
||||
if (ret < 0)
|
||||
goto error_2;
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
}
|
||||
|
||||
/* if we're going to allocate a new key, we're going to have
|
||||
* to modify the keyring */
|
||||
ret = key_permission(keyring_ref, KEY_WRITE);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_3;
|
||||
goto error_link_end;
|
||||
}
|
||||
|
||||
/* if it's possible to update this type of key, search for an existing
|
||||
|
|
@ -840,25 +868,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
perm, flags);
|
||||
if (IS_ERR(key)) {
|
||||
key_ref = ERR_CAST(key);
|
||||
goto error_3;
|
||||
goto error_link_end;
|
||||
}
|
||||
|
||||
/* instantiate it and link it into the target keyring */
|
||||
ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
|
||||
&prealloc);
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
|
||||
if (ret < 0) {
|
||||
key_put(key);
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_3;
|
||||
goto error_link_end;
|
||||
}
|
||||
|
||||
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
|
||||
|
||||
error_3:
|
||||
error_link_end:
|
||||
__key_link_end(keyring, ktype, prealloc);
|
||||
error_2:
|
||||
error_free_prep:
|
||||
if (ktype->preparse)
|
||||
ktype->free_preparse(&prep);
|
||||
error_put_type:
|
||||
key_type_put(ktype);
|
||||
error:
|
||||
error:
|
||||
return key_ref;
|
||||
|
||||
found_matching_key:
|
||||
|
|
@ -866,10 +896,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|||
* - we can drop the locks first as we have the key pinned
|
||||
*/
|
||||
__key_link_end(keyring, ktype, prealloc);
|
||||
key_type_put(ktype);
|
||||
|
||||
key_ref = __key_update(key_ref, payload, plen);
|
||||
goto error;
|
||||
key_ref = __key_update(key_ref, &prep);
|
||||
goto error_free_prep;
|
||||
}
|
||||
EXPORT_SYMBOL(key_create_or_update);
|
||||
|
||||
|
|
@ -888,6 +917,7 @@ EXPORT_SYMBOL(key_create_or_update);
|
|||
*/
|
||||
int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
||||
{
|
||||
struct key_preparsed_payload prep;
|
||||
struct key *key = key_ref_to_ptr(key_ref);
|
||||
int ret;
|
||||
|
||||
|
|
@ -900,18 +930,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
|||
|
||||
/* attempt to update it if supported */
|
||||
ret = -EOPNOTSUPP;
|
||||
if (key->type->update) {
|
||||
down_write(&key->sem);
|
||||
if (!key->type->update)
|
||||
goto error;
|
||||
|
||||
ret = key->type->update(key, payload, plen);
|
||||
if (ret == 0)
|
||||
/* updating a negative key instantiates it */
|
||||
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
||||
|
||||
up_write(&key->sem);
|
||||
memset(&prep, 0, sizeof(prep));
|
||||
prep.data = payload;
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = key->type->def_datalen;
|
||||
if (key->type->preparse) {
|
||||
ret = key->type->preparse(&prep);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
down_write(&key->sem);
|
||||
|
||||
ret = key->type->update(key, &prep);
|
||||
if (ret == 0)
|
||||
/* updating a negative key instantiates it */
|
||||
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
||||
|
||||
up_write(&key->sem);
|
||||
|
||||
if (key->type->preparse)
|
||||
key->type->free_preparse(&prep);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(key_update);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type,
|
|||
* Extract the description of a new key from userspace and either add it as a
|
||||
* new key to the specified keyring or update a matching key in that keyring.
|
||||
*
|
||||
* If the description is NULL or an empty string, the key type is asked to
|
||||
* generate one from the payload.
|
||||
*
|
||||
* The keyring must be writable so that we can attach the key to it.
|
||||
*
|
||||
* If successful, the new key's serial number is returned, otherwise an error
|
||||
|
|
@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
|
|||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
description = strndup_user(_description, PAGE_SIZE);
|
||||
if (IS_ERR(description)) {
|
||||
ret = PTR_ERR(description);
|
||||
goto error;
|
||||
description = NULL;
|
||||
if (_description) {
|
||||
description = strndup_user(_description, PAGE_SIZE);
|
||||
if (IS_ERR(description)) {
|
||||
ret = PTR_ERR(description);
|
||||
goto error;
|
||||
}
|
||||
if (!*description) {
|
||||
kfree(description);
|
||||
description = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* pull the payload in if one was supplied */
|
||||
|
|
@ -569,8 +579,8 @@ okay:
|
|||
ret = snprintf(tmpbuf, PAGE_SIZE - 1,
|
||||
"%s;%d;%d;%08x;%s",
|
||||
key->type->name,
|
||||
key->uid,
|
||||
key->gid,
|
||||
from_kuid_munged(current_user_ns(), key->uid),
|
||||
from_kgid_munged(current_user_ns(), key->gid),
|
||||
key->perm,
|
||||
key->description ?: "");
|
||||
|
||||
|
|
@ -766,15 +776,25 @@ error:
|
|||
*
|
||||
* If successful, 0 will be returned.
|
||||
*/
|
||||
long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
|
||||
long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
|
||||
{
|
||||
struct key_user *newowner, *zapowner = NULL;
|
||||
struct key *key;
|
||||
key_ref_t key_ref;
|
||||
long ret;
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
|
||||
uid = make_kuid(current_user_ns(), user);
|
||||
gid = make_kgid(current_user_ns(), group);
|
||||
ret = -EINVAL;
|
||||
if ((user != (uid_t) -1) && !uid_valid(uid))
|
||||
goto error;
|
||||
if ((group != (gid_t) -1) && !gid_valid(gid))
|
||||
goto error;
|
||||
|
||||
ret = 0;
|
||||
if (uid == (uid_t) -1 && gid == (gid_t) -1)
|
||||
if (user == (uid_t) -1 && group == (gid_t) -1)
|
||||
goto error;
|
||||
|
||||
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
|
||||
|
|
@ -792,27 +812,27 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
|
|||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
/* only the sysadmin can chown a key to some other UID */
|
||||
if (uid != (uid_t) -1 && key->uid != uid)
|
||||
if (user != (uid_t) -1 && !uid_eq(key->uid, uid))
|
||||
goto error_put;
|
||||
|
||||
/* only the sysadmin can set the key's GID to a group other
|
||||
* than one of those that the current process subscribes to */
|
||||
if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
|
||||
if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid))
|
||||
goto error_put;
|
||||
}
|
||||
|
||||
/* change the UID */
|
||||
if (uid != (uid_t) -1 && uid != key->uid) {
|
||||
if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) {
|
||||
ret = -ENOMEM;
|
||||
newowner = key_user_lookup(uid, current_user_ns());
|
||||
newowner = key_user_lookup(uid);
|
||||
if (!newowner)
|
||||
goto error_put;
|
||||
|
||||
/* transfer the quota burden to the new user */
|
||||
if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
|
||||
unsigned maxkeys = (uid == 0) ?
|
||||
unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxkeys : key_quota_maxkeys;
|
||||
unsigned maxbytes = (uid == 0) ?
|
||||
unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxbytes : key_quota_maxbytes;
|
||||
|
||||
spin_lock(&newowner->lock);
|
||||
|
|
@ -846,7 +866,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
|
|||
}
|
||||
|
||||
/* change the GID */
|
||||
if (gid != (gid_t) -1)
|
||||
if (group != (gid_t) -1)
|
||||
key->gid = gid;
|
||||
|
||||
ret = 0;
|
||||
|
|
@ -897,7 +917,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
|
|||
down_write(&key->sem);
|
||||
|
||||
/* if we're not the sysadmin, we can only change a key that we own */
|
||||
if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) {
|
||||
if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
|
||||
key->perm = perm;
|
||||
ret = 0;
|
||||
}
|
||||
|
|
@ -1486,7 +1506,6 @@ long keyctl_session_to_parent(void)
|
|||
oldwork = NULL;
|
||||
parent = me->real_parent;
|
||||
|
||||
task_lock(parent);
|
||||
/* the parent mustn't be init and mustn't be a kernel thread */
|
||||
if (parent->pid <= 1 || !parent->mm)
|
||||
goto unlock;
|
||||
|
|
@ -1507,18 +1526,18 @@ long keyctl_session_to_parent(void)
|
|||
|
||||
/* the parent must have the same effective ownership and mustn't be
|
||||
* SUID/SGID */
|
||||
if (pcred->uid != mycred->euid ||
|
||||
pcred->euid != mycred->euid ||
|
||||
pcred->suid != mycred->euid ||
|
||||
pcred->gid != mycred->egid ||
|
||||
pcred->egid != mycred->egid ||
|
||||
pcred->sgid != mycred->egid)
|
||||
if (!uid_eq(pcred->uid, mycred->euid) ||
|
||||
!uid_eq(pcred->euid, mycred->euid) ||
|
||||
!uid_eq(pcred->suid, mycred->euid) ||
|
||||
!gid_eq(pcred->gid, mycred->egid) ||
|
||||
!gid_eq(pcred->egid, mycred->egid) ||
|
||||
!gid_eq(pcred->sgid, mycred->egid))
|
||||
goto unlock;
|
||||
|
||||
/* the keyrings must have the same UID */
|
||||
if ((pcred->tgcred->session_keyring &&
|
||||
pcred->tgcred->session_keyring->uid != mycred->euid) ||
|
||||
mycred->tgcred->session_keyring->uid != mycred->euid)
|
||||
!uid_eq(pcred->tgcred->session_keyring->uid, mycred->euid)) ||
|
||||
!uid_eq(mycred->tgcred->session_keyring->uid, mycred->euid))
|
||||
goto unlock;
|
||||
|
||||
/* cancel an already pending keyring replacement */
|
||||
|
|
@ -1530,7 +1549,6 @@ long keyctl_session_to_parent(void)
|
|||
if (!ret)
|
||||
newwork = NULL;
|
||||
unlock:
|
||||
task_unlock(parent);
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
rcu_read_unlock();
|
||||
if (oldwork)
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc)
|
|||
* operations.
|
||||
*/
|
||||
static int keyring_instantiate(struct key *keyring,
|
||||
const void *data, size_t datalen);
|
||||
struct key_preparsed_payload *prep);
|
||||
static int keyring_match(const struct key *keyring, const void *criterion);
|
||||
static void keyring_revoke(struct key *keyring);
|
||||
static void keyring_destroy(struct key *keyring);
|
||||
|
|
@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring)
|
|||
* Returns 0 on success, -EINVAL if given any data.
|
||||
*/
|
||||
static int keyring_instantiate(struct key *keyring,
|
||||
const void *data, size_t datalen)
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen == 0) {
|
||||
if (prep->datalen == 0) {
|
||||
/* make the keyring available by name if it has one */
|
||||
keyring_publish_name(keyring);
|
||||
ret = 0;
|
||||
|
|
@ -256,7 +256,7 @@ error:
|
|||
/*
|
||||
* Allocate a keyring and link into the destination keyring.
|
||||
*/
|
||||
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
|
||||
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
|
||||
const struct cred *cred, unsigned long flags,
|
||||
struct key *dest)
|
||||
{
|
||||
|
|
@ -612,7 +612,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
|
|||
&keyring_name_hash[bucket],
|
||||
type_data.link
|
||||
) {
|
||||
if (keyring->user->user_ns != current_user_ns())
|
||||
if (!kuid_has_mapping(current_user_ns(), keyring->user->uid))
|
||||
continue;
|
||||
|
||||
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
|
||||
|
|
|
|||
|
|
@ -36,33 +36,27 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
|
|||
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
|
||||
if (key->user->user_ns != cred->user_ns)
|
||||
goto use_other_perms;
|
||||
|
||||
/* use the second 8-bits of permissions for keys the caller owns */
|
||||
if (key->uid == cred->fsuid) {
|
||||
if (uid_eq(key->uid, cred->fsuid)) {
|
||||
kperm = key->perm >> 16;
|
||||
goto use_these_perms;
|
||||
}
|
||||
|
||||
/* use the third 8-bits of permissions for keys the caller has a group
|
||||
* membership in common with */
|
||||
if (key->gid != -1 && key->perm & KEY_GRP_ALL) {
|
||||
if (key->gid == cred->fsgid) {
|
||||
if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) {
|
||||
if (gid_eq(key->gid, cred->fsgid)) {
|
||||
kperm = key->perm >> 8;
|
||||
goto use_these_perms;
|
||||
}
|
||||
|
||||
ret = groups_search(cred->group_info,
|
||||
make_kgid(current_user_ns(), key->gid));
|
||||
ret = groups_search(cred->group_info, key->gid);
|
||||
if (ret) {
|
||||
kperm = key->perm >> 8;
|
||||
goto use_these_perms;
|
||||
}
|
||||
}
|
||||
|
||||
use_other_perms:
|
||||
|
||||
/* otherwise use the least-significant 8-bits */
|
||||
kperm = key->perm;
|
||||
|
||||
|
|
|
|||
|
|
@ -88,14 +88,14 @@ __initcall(key_proc_init);
|
|||
*/
|
||||
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
|
||||
|
||||
static struct rb_node *key_serial_next(struct rb_node *n)
|
||||
static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
struct user_namespace *user_ns = seq_user_ns(p);
|
||||
|
||||
n = rb_next(n);
|
||||
while (n) {
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
if (key->user->user_ns == user_ns)
|
||||
if (kuid_has_mapping(user_ns, key->user->uid))
|
||||
break;
|
||||
n = rb_next(n);
|
||||
}
|
||||
|
|
@ -107,9 +107,9 @@ static int proc_keys_open(struct inode *inode, struct file *file)
|
|||
return seq_open(file, &proc_keys_ops);
|
||||
}
|
||||
|
||||
static struct key *find_ge_key(key_serial_t id)
|
||||
static struct key *find_ge_key(struct seq_file *p, key_serial_t id)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
struct user_namespace *user_ns = seq_user_ns(p);
|
||||
struct rb_node *n = key_serial_tree.rb_node;
|
||||
struct key *minkey = NULL;
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ static struct key *find_ge_key(key_serial_t id)
|
|||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
if (minkey->user->user_ns == user_ns)
|
||||
if (kuid_has_mapping(user_ns, minkey->user->uid))
|
||||
return minkey;
|
||||
n = rb_next(&minkey->serial_node);
|
||||
if (!n)
|
||||
|
|
@ -151,7 +151,7 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
|
|||
|
||||
if (*_pos > INT_MAX)
|
||||
return NULL;
|
||||
key = find_ge_key(pos);
|
||||
key = find_ge_key(p, pos);
|
||||
if (!key)
|
||||
return NULL;
|
||||
*_pos = key->serial;
|
||||
|
|
@ -168,7 +168,7 @@ static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
|
|||
{
|
||||
struct rb_node *n;
|
||||
|
||||
n = key_serial_next(v);
|
||||
n = key_serial_next(p, v);
|
||||
if (n)
|
||||
*_pos = key_node_serial(n);
|
||||
return n;
|
||||
|
|
@ -254,8 +254,8 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
|||
atomic_read(&key->usage),
|
||||
xbuf,
|
||||
key->perm,
|
||||
key->uid,
|
||||
key->gid,
|
||||
from_kuid_munged(seq_user_ns(m), key->uid),
|
||||
from_kgid_munged(seq_user_ns(m), key->gid),
|
||||
key->type->name);
|
||||
|
||||
#undef showflag
|
||||
|
|
@ -270,26 +270,26 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
|||
|
||||
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
|
||||
|
||||
static struct rb_node *__key_user_next(struct rb_node *n)
|
||||
static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n)
|
||||
{
|
||||
while (n) {
|
||||
struct key_user *user = rb_entry(n, struct key_user, node);
|
||||
if (user->user_ns == current_user_ns())
|
||||
if (kuid_has_mapping(user_ns, user->uid))
|
||||
break;
|
||||
n = rb_next(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rb_node *key_user_next(struct rb_node *n)
|
||||
static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n)
|
||||
{
|
||||
return __key_user_next(rb_next(n));
|
||||
return __key_user_next(user_ns, rb_next(n));
|
||||
}
|
||||
|
||||
static struct rb_node *key_user_first(struct rb_root *r)
|
||||
static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r)
|
||||
{
|
||||
struct rb_node *n = rb_first(r);
|
||||
return __key_user_next(n);
|
||||
return __key_user_next(user_ns, n);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -309,10 +309,10 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
|
|||
|
||||
spin_lock(&key_user_lock);
|
||||
|
||||
_p = key_user_first(&key_user_tree);
|
||||
_p = key_user_first(seq_user_ns(p), &key_user_tree);
|
||||
while (pos > 0 && _p) {
|
||||
pos--;
|
||||
_p = key_user_next(_p);
|
||||
_p = key_user_next(seq_user_ns(p), _p);
|
||||
}
|
||||
|
||||
return _p;
|
||||
|
|
@ -321,7 +321,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
|
|||
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
|
||||
{
|
||||
(*_pos)++;
|
||||
return key_user_next((struct rb_node *)v);
|
||||
return key_user_next(seq_user_ns(p), (struct rb_node *)v);
|
||||
}
|
||||
|
||||
static void proc_key_users_stop(struct seq_file *p, void *v)
|
||||
|
|
@ -334,13 +334,13 @@ static int proc_key_users_show(struct seq_file *m, void *v)
|
|||
{
|
||||
struct rb_node *_p = v;
|
||||
struct key_user *user = rb_entry(_p, struct key_user, node);
|
||||
unsigned maxkeys = (user->uid == 0) ?
|
||||
unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxkeys : key_quota_maxkeys;
|
||||
unsigned maxbytes = (user->uid == 0) ?
|
||||
unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ?
|
||||
key_quota_root_maxbytes : key_quota_maxbytes;
|
||||
|
||||
seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
|
||||
user->uid,
|
||||
from_kuid_munged(seq_user_ns(m), user->uid),
|
||||
atomic_read(&user->usage),
|
||||
atomic_read(&user->nkeys),
|
||||
atomic_read(&user->nikeys),
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ struct key_user root_key_user = {
|
|||
.lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
|
||||
.nkeys = ATOMIC_INIT(2),
|
||||
.nikeys = ATOMIC_INIT(2),
|
||||
.uid = 0,
|
||||
.user_ns = &init_user_ns,
|
||||
.uid = GLOBAL_ROOT_UID,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -48,11 +47,13 @@ int install_user_keyrings(void)
|
|||
struct key *uid_keyring, *session_keyring;
|
||||
char buf[20];
|
||||
int ret;
|
||||
uid_t uid;
|
||||
|
||||
cred = current_cred();
|
||||
user = cred->user;
|
||||
uid = from_kuid(cred->user_ns, user->uid);
|
||||
|
||||
kenter("%p{%u}", user, user->uid);
|
||||
kenter("%p{%u}", user, uid);
|
||||
|
||||
if (user->uid_keyring) {
|
||||
kleave(" = 0 [exist]");
|
||||
|
|
@ -67,11 +68,11 @@ int install_user_keyrings(void)
|
|||
* - there may be one in existence already as it may have been
|
||||
* pinned by a session, but the user_struct pointing to it
|
||||
* may have been destroyed by setuid */
|
||||
sprintf(buf, "_uid.%u", user->uid);
|
||||
sprintf(buf, "_uid.%u", uid);
|
||||
|
||||
uid_keyring = find_keyring_by_name(buf, true);
|
||||
if (IS_ERR(uid_keyring)) {
|
||||
uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
|
||||
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
|
||||
cred, KEY_ALLOC_IN_QUOTA,
|
||||
NULL);
|
||||
if (IS_ERR(uid_keyring)) {
|
||||
|
|
@ -82,12 +83,12 @@ int install_user_keyrings(void)
|
|||
|
||||
/* get a default session keyring (which might also exist
|
||||
* already) */
|
||||
sprintf(buf, "_uid_ses.%u", user->uid);
|
||||
sprintf(buf, "_uid_ses.%u", uid);
|
||||
|
||||
session_keyring = find_keyring_by_name(buf, true);
|
||||
if (IS_ERR(session_keyring)) {
|
||||
session_keyring =
|
||||
keyring_alloc(buf, user->uid, (gid_t) -1,
|
||||
keyring_alloc(buf, user->uid, INVALID_GID,
|
||||
cred, KEY_ALLOC_IN_QUOTA, NULL);
|
||||
if (IS_ERR(session_keyring)) {
|
||||
ret = PTR_ERR(session_keyring);
|
||||
|
|
|
|||
|
|
@ -139,8 +139,8 @@ static int call_sbin_request_key(struct key_construction *cons,
|
|||
goto error_link;
|
||||
|
||||
/* record the UID and GID */
|
||||
sprintf(uid_str, "%d", cred->fsuid);
|
||||
sprintf(gid_str, "%d", cred->fsgid);
|
||||
sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid));
|
||||
sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid));
|
||||
|
||||
/* we say which key is under construction */
|
||||
sprintf(key_str, "%d", key->serial);
|
||||
|
|
@ -442,7 +442,7 @@ static struct key *construct_key_and_link(struct key_type *type,
|
|||
|
||||
kenter("");
|
||||
|
||||
user = key_user_lookup(current_fsuid(), current_user_ns());
|
||||
user = key_user_lookup(current_fsuid());
|
||||
if (!user)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
static int request_key_auth_instantiate(struct key *, const void *, size_t);
|
||||
static int request_key_auth_instantiate(struct key *,
|
||||
struct key_preparsed_payload *);
|
||||
static void request_key_auth_describe(const struct key *, struct seq_file *);
|
||||
static void request_key_auth_revoke(struct key *);
|
||||
static void request_key_auth_destroy(struct key *);
|
||||
|
|
@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = {
|
|||
* Instantiate a request-key authorisation key.
|
||||
*/
|
||||
static int request_key_auth_instantiate(struct key *key,
|
||||
const void *data,
|
||||
size_t datalen)
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
key->payload.data = (struct request_key_auth *) data;
|
||||
key->payload.data = (struct request_key_auth *)prep->data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -368,38 +368,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a random value from TPM
|
||||
*/
|
||||
static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_COMMAND);
|
||||
store32(tb, TPM_GETRANDOM_SIZE);
|
||||
store32(tb, TPM_ORD_GETRANDOM);
|
||||
store32(tb, len);
|
||||
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
|
||||
if (!ret)
|
||||
memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int my_get_random(unsigned char *buf, int len)
|
||||
{
|
||||
struct tpm_buf *tb;
|
||||
int ret;
|
||||
|
||||
tb = kmalloc(sizeof *tb, GFP_KERNEL);
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
ret = tpm_get_random(tb, buf, len);
|
||||
|
||||
kfree(tb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock a trusted key, by extending a selected PCR.
|
||||
*
|
||||
|
|
@ -413,8 +381,8 @@ static int pcrlock(const int pcrnum)
|
|||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
ret = my_get_random(hash, SHA1_DIGEST_SIZE);
|
||||
if (ret < 0)
|
||||
ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE);
|
||||
if (ret != SHA1_DIGEST_SIZE)
|
||||
return ret;
|
||||
return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
|
||||
}
|
||||
|
|
@ -429,8 +397,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
|
|||
unsigned char ononce[TPM_NONCE_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
|
||||
if (ret < 0)
|
||||
ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE)
|
||||
return ret;
|
||||
|
||||
INIT_BUF(tb);
|
||||
|
|
@ -524,8 +492,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret < 0)
|
||||
ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE)
|
||||
goto out;
|
||||
ordinal = htonl(TPM_ORD_SEAL);
|
||||
datsize = htonl(datalen);
|
||||
|
|
@ -634,8 +602,8 @@ static int tpm_unseal(struct tpm_buf *tb,
|
|||
|
||||
ordinal = htonl(TPM_ORD_UNSEAL);
|
||||
keyhndl = htonl(SRKHANDLE);
|
||||
ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret < 0) {
|
||||
ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE) {
|
||||
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -927,22 +895,24 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
|
|||
*
|
||||
* On success, return 0. Otherwise return errno.
|
||||
*/
|
||||
static int trusted_instantiate(struct key *key, const void *data,
|
||||
size_t datalen)
|
||||
static int trusted_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct trusted_key_payload *payload = NULL;
|
||||
struct trusted_key_options *options = NULL;
|
||||
size_t datalen = prep->datalen;
|
||||
char *datablob;
|
||||
int ret = 0;
|
||||
int key_cmd;
|
||||
size_t key_len;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
if (!datablob)
|
||||
return -ENOMEM;
|
||||
memcpy(datablob, data, datalen);
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
datablob[datalen] = '\0';
|
||||
|
||||
options = trusted_options_alloc();
|
||||
|
|
@ -974,8 +944,9 @@ static int trusted_instantiate(struct key *key, const void *data,
|
|||
pr_info("trusted_key: key_unseal failed (%d)\n", ret);
|
||||
break;
|
||||
case Opt_new:
|
||||
ret = my_get_random(payload->key, payload->key_len);
|
||||
if (ret < 0) {
|
||||
key_len = payload->key_len;
|
||||
ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len);
|
||||
if (ret != key_len) {
|
||||
pr_info("trusted_key: key_create failed (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1011,17 +982,18 @@ static void trusted_rcu_free(struct rcu_head *rcu)
|
|||
/*
|
||||
* trusted_update - reseal an existing key with new PCR values
|
||||
*/
|
||||
static int trusted_update(struct key *key, const void *data, size_t datalen)
|
||||
static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct trusted_key_payload *p = key->payload.data;
|
||||
struct trusted_key_payload *new_p;
|
||||
struct trusted_key_options *new_o;
|
||||
size_t datalen = prep->datalen;
|
||||
char *datablob;
|
||||
int ret = 0;
|
||||
|
||||
if (!p->migratable)
|
||||
return -EPERM;
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
|
|
@ -1038,7 +1010,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen)
|
|||
goto out;
|
||||
}
|
||||
|
||||
memcpy(datablob, data, datalen);
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
datablob[datalen] = '\0';
|
||||
ret = datablob_parse(datablob, new_p, new_o);
|
||||
if (ret != Opt_update) {
|
||||
|
|
|
|||
|
|
@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon);
|
|||
/*
|
||||
* instantiate a user defined key
|
||||
*/
|
||||
int user_instantiate(struct key *key, const void *data, size_t datalen)
|
||||
int user_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto error;
|
||||
|
||||
ret = key_payload_reserve(key, datalen);
|
||||
|
|
@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen)
|
|||
|
||||
/* attach the data */
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, data, datalen);
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
rcu_assign_keypointer(key, upayload);
|
||||
ret = 0;
|
||||
|
||||
|
|
@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate);
|
|||
* update a user defined key
|
||||
* - the key's semaphore is write-locked
|
||||
*/
|
||||
int user_update(struct key *key, const void *data, size_t datalen)
|
||||
int user_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct user_key_payload *upayload, *zap;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
goto error;
|
||||
|
||||
/* construct a replacement payload */
|
||||
|
|
@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen)
|
|||
goto error;
|
||||
|
||||
upayload->datalen = datalen;
|
||||
memcpy(upayload->data, data, datalen);
|
||||
memcpy(upayload->data, prep->data, datalen);
|
||||
|
||||
/* check the quota and attach the new data */
|
||||
zap = upayload;
|
||||
|
|
|
|||
|
|
@ -136,11 +136,23 @@ int __init register_security(struct security_operations *ops)
|
|||
|
||||
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
int rc;
|
||||
rc = yama_ptrace_access_check(child, mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
return security_ops->ptrace_access_check(child, mode);
|
||||
}
|
||||
|
||||
int security_ptrace_traceme(struct task_struct *parent)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
int rc;
|
||||
rc = yama_ptrace_traceme(parent);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
return security_ops->ptrace_traceme(parent);
|
||||
}
|
||||
|
||||
|
|
@ -264,8 +276,8 @@ int security_sb_statfs(struct dentry *dentry)
|
|||
return security_ops->sb_statfs(dentry);
|
||||
}
|
||||
|
||||
int security_sb_mount(char *dev_name, struct path *path,
|
||||
char *type, unsigned long flags, void *data)
|
||||
int security_sb_mount(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags, void *data)
|
||||
{
|
||||
return security_ops->sb_mount(dev_name, path, type, flags, data);
|
||||
}
|
||||
|
|
@ -434,7 +446,7 @@ int security_path_chmod(struct path *path, umode_t mode)
|
|||
return security_ops->path_chmod(path, mode);
|
||||
}
|
||||
|
||||
int security_path_chown(struct path *path, uid_t uid, gid_t gid)
|
||||
int security_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
|
||||
return 0;
|
||||
|
|
@ -559,6 +571,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name,
|
|||
if (unlikely(IS_PRIVATE(dentry->d_inode)))
|
||||
return 0;
|
||||
ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ima_inode_setxattr(dentry, name, value, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
return evm_inode_setxattr(dentry, name, value, size);
|
||||
|
|
@ -594,6 +609,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
|
|||
if (unlikely(IS_PRIVATE(dentry->d_inode)))
|
||||
return 0;
|
||||
ret = security_ops->inode_removexattr(dentry, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ima_inode_removexattr(dentry, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
return evm_inode_removexattr(dentry, name);
|
||||
|
|
@ -761,6 +779,9 @@ int security_task_create(unsigned long clone_flags)
|
|||
|
||||
void security_task_free(struct task_struct *task)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
yama_task_free(task);
|
||||
#endif
|
||||
security_ops->task_free(task);
|
||||
}
|
||||
|
||||
|
|
@ -876,6 +897,12 @@ int security_task_wait(struct task_struct *p)
|
|||
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
int rc;
|
||||
rc = yama_task_prctl(option, arg2, arg3, arg4, arg5);
|
||||
if (rc != -ENOSYS)
|
||||
return rc;
|
||||
#endif
|
||||
return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2088,15 +2088,19 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
|
|||
return (atsecure || cap_bprm_secureexec(bprm));
|
||||
}
|
||||
|
||||
static int match_file(const void *p, struct file *file, unsigned fd)
|
||||
{
|
||||
return file_has_perm(p, file, file_to_av(file)) ? fd + 1 : 0;
|
||||
}
|
||||
|
||||
/* Derived from fs/exec.c:flush_old_files. */
|
||||
static inline void flush_unauthorized_files(const struct cred *cred,
|
||||
struct files_struct *files)
|
||||
{
|
||||
struct file *file, *devnull = NULL;
|
||||
struct tty_struct *tty;
|
||||
struct fdtable *fdt;
|
||||
long j = -1;
|
||||
int drop_tty = 0;
|
||||
unsigned n;
|
||||
|
||||
tty = get_current_tty();
|
||||
if (tty) {
|
||||
|
|
@ -2123,58 +2127,19 @@ static inline void flush_unauthorized_files(const struct cred *cred,
|
|||
no_tty();
|
||||
|
||||
/* Revalidate access to inherited open files. */
|
||||
spin_lock(&files->file_lock);
|
||||
for (;;) {
|
||||
unsigned long set, i;
|
||||
int fd;
|
||||
n = iterate_fd(files, 0, match_file, cred);
|
||||
if (!n) /* none found? */
|
||||
return;
|
||||
|
||||
j++;
|
||||
i = j * BITS_PER_LONG;
|
||||
fdt = files_fdtable(files);
|
||||
if (i >= fdt->max_fds)
|
||||
break;
|
||||
set = fdt->open_fds[j];
|
||||
if (!set)
|
||||
continue;
|
||||
spin_unlock(&files->file_lock);
|
||||
for ( ; set ; i++, set >>= 1) {
|
||||
if (set & 1) {
|
||||
file = fget(i);
|
||||
if (!file)
|
||||
continue;
|
||||
if (file_has_perm(cred,
|
||||
file,
|
||||
file_to_av(file))) {
|
||||
sys_close(i);
|
||||
fd = get_unused_fd();
|
||||
if (fd != i) {
|
||||
if (fd >= 0)
|
||||
put_unused_fd(fd);
|
||||
fput(file);
|
||||
continue;
|
||||
}
|
||||
if (devnull) {
|
||||
get_file(devnull);
|
||||
} else {
|
||||
devnull = dentry_open(
|
||||
&selinux_null,
|
||||
O_RDWR, cred);
|
||||
if (IS_ERR(devnull)) {
|
||||
devnull = NULL;
|
||||
put_unused_fd(fd);
|
||||
fput(file);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fd_install(fd, devnull);
|
||||
}
|
||||
fput(file);
|
||||
}
|
||||
}
|
||||
spin_lock(&files->file_lock);
|
||||
|
||||
}
|
||||
spin_unlock(&files->file_lock);
|
||||
devnull = dentry_open(&selinux_null, O_RDWR, cred);
|
||||
if (IS_ERR(devnull))
|
||||
devnull = NULL;
|
||||
/* replace all the matching ones with this */
|
||||
do {
|
||||
replace_fd(n - 1, devnull, 0);
|
||||
} while ((n = iterate_fd(files, n, match_file, cred)) != 0);
|
||||
if (devnull)
|
||||
fput(devnull);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2483,9 +2448,9 @@ static int selinux_sb_statfs(struct dentry *dentry)
|
|||
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
|
||||
}
|
||||
|
||||
static int selinux_mount(char *dev_name,
|
||||
static int selinux_mount(const char *dev_name,
|
||||
struct path *path,
|
||||
char *type,
|
||||
const char *type,
|
||||
unsigned long flags,
|
||||
void *data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -113,13 +113,12 @@ static int __init selnl_init(void)
|
|||
{
|
||||
struct netlink_kernel_cfg cfg = {
|
||||
.groups = SELNLGRP_MAX,
|
||||
.flags = NL_CFG_F_NONROOT_RECV,
|
||||
};
|
||||
|
||||
selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX,
|
||||
THIS_MODULE, &cfg);
|
||||
selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, &cfg);
|
||||
if (selnl == NULL)
|
||||
panic("SELinux: Cannot create netlink socket.");
|
||||
netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
|
|||
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
|
||||
"enforcing=%d old_enforcing=%d auid=%u ses=%u",
|
||||
new_value, selinux_enforcing,
|
||||
audit_get_loginuid(current),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
selinux_enforcing = new_value;
|
||||
if (selinux_enforcing)
|
||||
|
|
@ -305,7 +305,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
|
|||
goto out;
|
||||
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
|
||||
"selinux=0 auid=%u ses=%u",
|
||||
audit_get_loginuid(current),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
}
|
||||
|
||||
|
|
@ -485,7 +485,7 @@ static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
|
|||
return -EACCES;
|
||||
}
|
||||
|
||||
vma->vm_flags |= VM_RESERVED;
|
||||
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
|
||||
vma->vm_ops = &sel_mmap_policy_ops;
|
||||
|
||||
return 0;
|
||||
|
|
@ -551,7 +551,7 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
|
|||
out1:
|
||||
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
|
||||
"policy loaded auid=%u ses=%u",
|
||||
audit_get_loginuid(current),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
out:
|
||||
mutex_unlock(&sel_mutex);
|
||||
|
|
|
|||
|
|
@ -2440,7 +2440,7 @@ int security_set_bools(int len, int *values)
|
|||
sym_name(&policydb, SYM_BOOLS, i),
|
||||
!!values[i],
|
||||
policydb.bool_val_to_struct[i]->state,
|
||||
audit_get_loginuid(current),
|
||||
from_kuid(&init_user_ns, audit_get_loginuid(current)),
|
||||
audit_get_sessionid(current));
|
||||
}
|
||||
if (values[i])
|
||||
|
|
|
|||
|
|
@ -408,8 +408,8 @@ static int smack_sb_statfs(struct dentry *dentry)
|
|||
* Returns 0 if current can write the floor of the filesystem
|
||||
* being mounted on, an error code otherwise.
|
||||
*/
|
||||
static int smack_sb_mount(char *dev_name, struct path *path,
|
||||
char *type, unsigned long flags, void *data)
|
||||
static int smack_sb_mount(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags, void *data)
|
||||
{
|
||||
struct superblock_smack *sbp = path->dentry->d_sb->s_security;
|
||||
struct smk_audit_info ad;
|
||||
|
|
@ -1691,40 +1691,19 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
|
|||
* smack_task_wait - Smack access check for waiting
|
||||
* @p: task to wait for
|
||||
*
|
||||
* Returns 0 if current can wait for p, error code otherwise
|
||||
* Returns 0
|
||||
*/
|
||||
static int smack_task_wait(struct task_struct *p)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
char *sp = smk_of_current();
|
||||
char *tsp = smk_of_forked(task_security(p));
|
||||
int rc;
|
||||
|
||||
/* we don't log here, we can be overriden */
|
||||
rc = smk_access(tsp, sp, MAY_WRITE, NULL);
|
||||
if (rc == 0)
|
||||
goto out_log;
|
||||
|
||||
/*
|
||||
* Allow the operation to succeed if either task
|
||||
* has privilege to perform operations that might
|
||||
* account for the smack labels having gotten to
|
||||
* be different in the first place.
|
||||
*
|
||||
* This breaks the strict subject/object access
|
||||
* control ideal, taking the object's privilege
|
||||
* state into account in the decision as well as
|
||||
* the smack value.
|
||||
* Allow the operation to succeed.
|
||||
* Zombies are bad.
|
||||
* In userless environments (e.g. phones) programs
|
||||
* get marked with SMACK64EXEC and even if the parent
|
||||
* and child shouldn't be talking the parent still
|
||||
* may expect to know when the child exits.
|
||||
*/
|
||||
if (smack_privileged(CAP_MAC_OVERRIDE) ||
|
||||
has_capability(p, CAP_MAC_OVERRIDE))
|
||||
rc = 0;
|
||||
/* we log only if we didn't get overriden */
|
||||
out_log:
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, p);
|
||||
smack_log(tsp, sp, MAY_WRITE, rc, &ad);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2705,9 +2684,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
|
|||
static int smack_setprocattr(struct task_struct *p, char *name,
|
||||
void *value, size_t size)
|
||||
{
|
||||
int rc;
|
||||
struct task_smack *tsp;
|
||||
struct task_smack *oldtsp;
|
||||
struct cred *new;
|
||||
char *newsmack;
|
||||
|
||||
|
|
@ -2737,21 +2714,13 @@ static int smack_setprocattr(struct task_struct *p, char *name,
|
|||
if (newsmack == smack_known_web.smk_known)
|
||||
return -EPERM;
|
||||
|
||||
oldtsp = p->cred->security;
|
||||
new = prepare_creds();
|
||||
if (new == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL);
|
||||
if (tsp == NULL) {
|
||||
kfree(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
tsp = new->security;
|
||||
tsp->smk_task = newsmack;
|
||||
|
||||
new->security = tsp;
|
||||
commit_creds(new);
|
||||
return size;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ enum smk_inos {
|
|||
SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */
|
||||
SMK_ACCESS2 = 16, /* make an access check with long labels */
|
||||
SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */
|
||||
SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -1991,6 +1992,77 @@ static const struct file_operations smk_access2_ops = {
|
|||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_write_revoke_subj - write() for /smack/revoke-subject
|
||||
* @file: file pointer
|
||||
* @buf: data from user space
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start - must be 0
|
||||
*/
|
||||
static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *data = NULL;
|
||||
const char *cp = NULL;
|
||||
struct smack_known *skp;
|
||||
struct smack_rule *sp;
|
||||
struct list_head *rule_list;
|
||||
struct mutex *rule_lock;
|
||||
int rc = count;
|
||||
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (count == 0 || count > SMK_LONGLABEL)
|
||||
return -EINVAL;
|
||||
|
||||
data = kzalloc(count, GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(data, buf, count) != 0) {
|
||||
rc = -EFAULT;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
cp = smk_parse_smack(data, count);
|
||||
if (cp == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
skp = smk_find_entry(cp);
|
||||
if (skp == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
rule_list = &skp->smk_rules;
|
||||
rule_lock = &skp->smk_rules_lock;
|
||||
|
||||
mutex_lock(rule_lock);
|
||||
|
||||
list_for_each_entry_rcu(sp, rule_list, list)
|
||||
sp->smk_access = 0;
|
||||
|
||||
mutex_unlock(rule_lock);
|
||||
|
||||
free_out:
|
||||
kfree(data);
|
||||
kfree(cp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations smk_revoke_subj_ops = {
|
||||
.write = smk_write_revoke_subj,
|
||||
.read = simple_transaction_read,
|
||||
.release = simple_transaction_release,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_fill_super - fill the /smackfs superblock
|
||||
* @sb: the empty superblock
|
||||
|
|
@ -2037,6 +2109,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
|||
"access2", &smk_access2_ops, S_IRUGO|S_IWUGO},
|
||||
[SMK_CIPSO2] = {
|
||||
"cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_REVOKE_SUBJ] = {
|
||||
"revoke-subject", &smk_revoke_subj_ops,
|
||||
S_IRUGO|S_IWUSR},
|
||||
/* last one */
|
||||
{""}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -168,9 +168,14 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
|||
stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
|
||||
tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
|
||||
tomoyo_sys_getpid(), tomoyo_sys_getppid(),
|
||||
current_uid(), current_gid(), current_euid(),
|
||||
current_egid(), current_suid(), current_sgid(),
|
||||
current_fsuid(), current_fsgid());
|
||||
from_kuid(&init_user_ns, current_uid()),
|
||||
from_kgid(&init_user_ns, current_gid()),
|
||||
from_kuid(&init_user_ns, current_euid()),
|
||||
from_kgid(&init_user_ns, current_egid()),
|
||||
from_kuid(&init_user_ns, current_suid()),
|
||||
from_kgid(&init_user_ns, current_sgid()),
|
||||
from_kuid(&init_user_ns, current_fsuid()),
|
||||
from_kgid(&init_user_ns, current_fsgid()));
|
||||
if (!obj)
|
||||
goto no_obj_info;
|
||||
if (!obj->validate_done) {
|
||||
|
|
@ -191,15 +196,19 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
|||
tomoyo_buffer_len - 1 - pos,
|
||||
" path%u.parent={ uid=%u gid=%u "
|
||||
"ino=%lu perm=0%o }", (i >> 1) + 1,
|
||||
stat->uid, stat->gid, (unsigned long)
|
||||
stat->ino, stat->mode & S_IALLUGO);
|
||||
from_kuid(&init_user_ns, stat->uid),
|
||||
from_kgid(&init_user_ns, stat->gid),
|
||||
(unsigned long)stat->ino,
|
||||
stat->mode & S_IALLUGO);
|
||||
continue;
|
||||
}
|
||||
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
|
||||
" path%u={ uid=%u gid=%u ino=%lu major=%u"
|
||||
" minor=%u perm=0%o type=%s", (i >> 1) + 1,
|
||||
stat->uid, stat->gid, (unsigned long)
|
||||
stat->ino, MAJOR(dev), MINOR(dev),
|
||||
from_kuid(&init_user_ns, stat->uid),
|
||||
from_kgid(&init_user_ns, stat->gid),
|
||||
(unsigned long)stat->ino,
|
||||
MAJOR(dev), MINOR(dev),
|
||||
mode & S_IALLUGO, tomoyo_filetype(mode));
|
||||
if (S_ISCHR(mode) || S_ISBLK(mode)) {
|
||||
dev = stat->rdev;
|
||||
|
|
|
|||
|
|
@ -925,7 +925,9 @@ static bool tomoyo_manager(void)
|
|||
|
||||
if (!tomoyo_policy_loaded)
|
||||
return true;
|
||||
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
|
||||
if (!tomoyo_manage_by_non_root &&
|
||||
(!uid_eq(task->cred->uid, GLOBAL_ROOT_UID) ||
|
||||
!uid_eq(task->cred->euid, GLOBAL_ROOT_UID)))
|
||||
return false;
|
||||
exe = tomoyo_get_exe();
|
||||
if (!exe)
|
||||
|
|
|
|||
|
|
@ -561,8 +561,8 @@ struct tomoyo_address_group {
|
|||
|
||||
/* Subset of "struct stat". Used by conditional ACL and audit logs. */
|
||||
struct tomoyo_mini_stat {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
ino_t ino;
|
||||
umode_t mode;
|
||||
dev_t dev;
|
||||
|
|
@ -970,7 +970,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r,
|
|||
const u8 index);
|
||||
int tomoyo_mkdev_perm(const u8 operation, struct path *path,
|
||||
const unsigned int mode, unsigned int dev);
|
||||
int tomoyo_mount_permission(char *dev_name, struct path *path,
|
||||
int tomoyo_mount_permission(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags,
|
||||
void *data_page);
|
||||
int tomoyo_open_control(const u8 type, struct file *file);
|
||||
|
|
|
|||
|
|
@ -813,28 +813,28 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
|||
unsigned long value = 0;
|
||||
switch (index) {
|
||||
case TOMOYO_TASK_UID:
|
||||
value = current_uid();
|
||||
value = from_kuid(&init_user_ns, current_uid());
|
||||
break;
|
||||
case TOMOYO_TASK_EUID:
|
||||
value = current_euid();
|
||||
value = from_kuid(&init_user_ns, current_euid());
|
||||
break;
|
||||
case TOMOYO_TASK_SUID:
|
||||
value = current_suid();
|
||||
value = from_kuid(&init_user_ns, current_suid());
|
||||
break;
|
||||
case TOMOYO_TASK_FSUID:
|
||||
value = current_fsuid();
|
||||
value = from_kuid(&init_user_ns, current_fsuid());
|
||||
break;
|
||||
case TOMOYO_TASK_GID:
|
||||
value = current_gid();
|
||||
value = from_kgid(&init_user_ns, current_gid());
|
||||
break;
|
||||
case TOMOYO_TASK_EGID:
|
||||
value = current_egid();
|
||||
value = from_kgid(&init_user_ns, current_egid());
|
||||
break;
|
||||
case TOMOYO_TASK_SGID:
|
||||
value = current_sgid();
|
||||
value = from_kgid(&init_user_ns, current_sgid());
|
||||
break;
|
||||
case TOMOYO_TASK_FSGID:
|
||||
value = current_fsgid();
|
||||
value = from_kgid(&init_user_ns, current_fsgid());
|
||||
break;
|
||||
case TOMOYO_TASK_PID:
|
||||
value = tomoyo_sys_getpid();
|
||||
|
|
@ -970,13 +970,13 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
|
|||
case TOMOYO_PATH2_UID:
|
||||
case TOMOYO_PATH1_PARENT_UID:
|
||||
case TOMOYO_PATH2_PARENT_UID:
|
||||
value = stat->uid;
|
||||
value = from_kuid(&init_user_ns, stat->uid);
|
||||
break;
|
||||
case TOMOYO_PATH1_GID:
|
||||
case TOMOYO_PATH2_GID:
|
||||
case TOMOYO_PATH1_PARENT_GID:
|
||||
case TOMOYO_PATH2_PARENT_GID:
|
||||
value = stat->gid;
|
||||
value = from_kgid(&init_user_ns, stat->gid);
|
||||
break;
|
||||
case TOMOYO_PATH1_INO:
|
||||
case TOMOYO_PATH2_INO:
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
|
|||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
|
||||
static int tomoyo_mount_acl(struct tomoyo_request_info *r,
|
||||
const char *dev_name,
|
||||
struct path *dir, const char *type,
|
||||
unsigned long flags)
|
||||
{
|
||||
|
|
@ -183,7 +184,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
|
|||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
int tomoyo_mount_permission(char *dev_name, struct path *path,
|
||||
int tomoyo_mount_permission(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags,
|
||||
void *data_page)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -373,13 +373,15 @@ static int tomoyo_path_chmod(struct path *path, umode_t mode)
|
|||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
|
||||
static int tomoyo_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
int error = 0;
|
||||
if (uid != (uid_t) -1)
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid);
|
||||
if (!error && gid != (gid_t) -1)
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid);
|
||||
if (uid_valid(uid))
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
|
||||
from_kuid(&init_user_ns, uid));
|
||||
if (!error && gid_valid(gid))
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path,
|
||||
from_kgid(&init_user_ns, gid));
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
@ -406,8 +408,8 @@ static int tomoyo_path_chroot(struct path *path)
|
|||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_sb_mount(char *dev_name, struct path *path,
|
||||
char *type, unsigned long flags, void *data)
|
||||
static int tomoyo_sb_mount(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags, void *data)
|
||||
{
|
||||
return tomoyo_mount_permission(dev_name, path, type, flags, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -949,18 +949,13 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
|
|||
const char *tomoyo_get_exe(void)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
const char *cp = NULL;
|
||||
|
||||
if (!mm)
|
||||
return NULL;
|
||||
down_read(&mm->mmap_sem);
|
||||
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
||||
if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
|
||||
cp = tomoyo_realpath_from_path(&vma->vm_file->f_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mm->exe_file)
|
||||
cp = tomoyo_realpath_from_path(&mm->exe_file->f_path);
|
||||
up_read(&mm->mmap_sem);
|
||||
return cp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,3 +11,11 @@ config SECURITY_YAMA
|
|||
Further information can be found in Documentation/security/Yama.txt.
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_YAMA_STACKED
|
||||
bool "Yama stacked with other LSMs"
|
||||
depends on SECURITY_YAMA
|
||||
default n
|
||||
help
|
||||
When Yama is built into the kernel, force it to stack with the
|
||||
selected primary LSM.
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ static void yama_ptracer_del(struct task_struct *tracer,
|
|||
* yama_task_free - check for task_pid to remove from exception list
|
||||
* @task: task being removed
|
||||
*/
|
||||
static void yama_task_free(struct task_struct *task)
|
||||
void yama_task_free(struct task_struct *task)
|
||||
{
|
||||
yama_ptracer_del(task, task);
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ static void yama_task_free(struct task_struct *task)
|
|||
* Return 0 on success, -ve on error. -ENOSYS is returned when Yama
|
||||
* does not handle the given option.
|
||||
*/
|
||||
static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
int rc;
|
||||
|
|
@ -143,7 +143,7 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||
if (arg2 == 0) {
|
||||
yama_ptracer_del(NULL, myself);
|
||||
rc = 0;
|
||||
} else if (arg2 == PR_SET_PTRACER_ANY) {
|
||||
} else if (arg2 == PR_SET_PTRACER_ANY || (int)arg2 == -1) {
|
||||
rc = yama_ptracer_add(NULL, myself);
|
||||
} else {
|
||||
struct task_struct *tracer;
|
||||
|
|
@ -243,7 +243,7 @@ static int ptracer_exception_found(struct task_struct *tracer,
|
|||
*
|
||||
* Returns 0 if following the ptrace is allowed, -ve on error.
|
||||
*/
|
||||
static int yama_ptrace_access_check(struct task_struct *child,
|
||||
int yama_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
int rc;
|
||||
|
|
@ -293,7 +293,7 @@ static int yama_ptrace_access_check(struct task_struct *child,
|
|||
*
|
||||
* Returns 0 if following the ptrace is allowed, -ve on error.
|
||||
*/
|
||||
static int yama_ptrace_traceme(struct task_struct *parent)
|
||||
int yama_ptrace_traceme(struct task_struct *parent)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -324,6 +324,7 @@ static int yama_ptrace_traceme(struct task_struct *parent)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
static struct security_operations yama_ops = {
|
||||
.name = "yama",
|
||||
|
||||
|
|
@ -332,6 +333,7 @@ static struct security_operations yama_ops = {
|
|||
.task_prctl = yama_task_prctl,
|
||||
.task_free = yama_task_free,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static int yama_dointvec_minmax(struct ctl_table *table, int write,
|
||||
|
|
@ -378,13 +380,17 @@ static struct ctl_table yama_sysctl_table[] = {
|
|||
|
||||
static __init int yama_init(void)
|
||||
{
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
if (!security_module_enable(&yama_ops))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "Yama: becoming mindful.\n");
|
||||
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
if (register_security(&yama_ops))
|
||||
panic("Yama: kernel registration failed.\n");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))
|
||||
|
|
|
|||
Loading…
Reference in a new issue