summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/include/apparmor.h1
-rw-r--r--security/apparmor/lib.c14
-rw-r--r--security/device_cgroup.c243
-rw-r--r--security/integrity/evm/evm_crypto.c2
-rw-r--r--security/integrity/evm/evm_main.c2
-rw-r--r--security/integrity/integrity_audit.c2
-rw-r--r--security/keys/compat.c4
-rw-r--r--security/lsm_audit.c11
-rw-r--r--security/security.c22
-rw-r--r--security/selinux/hooks.c3
-rw-r--r--security/selinux/include/xfrm.h5
-rw-r--r--security/tomoyo/realpath.c4
12 files changed, 214 insertions, 99 deletions
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 8fb1488a3cd4..97130f88838b 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -66,7 +66,6 @@ extern int apparmor_initialized __initdata;
char *aa_split_fqname(char *args, char **ns_name);
void aa_info_message(const char *str);
void *__aa_kvmalloc(size_t size, gfp_t flags);
-void kvfree(void *buffer);
static inline void *kvmalloc(size_t size)
{
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 69689922c491..c1827e068454 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -104,17 +104,3 @@ void *__aa_kvmalloc(size_t size, gfp_t flags)
}
return buffer;
}
-
-/**
- * kvfree - free an allocation do by kvmalloc
- * @buffer: buffer to free (MAYBE_NULL)
- *
- * Free a buffer allocated by kvmalloc
- */
-void kvfree(void *buffer)
-{
- if (is_vmalloc_addr(buffer))
- vfree(buffer);
- else
- kfree(buffer);
-}
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index d3b6d2cd3a06..d9d69e6930ed 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -58,11 +58,9 @@ static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
{
- return css_to_devcgroup(task_css(task, devices_subsys_id));
+ return css_to_devcgroup(task_css(task, devices_cgrp_id));
}
-struct cgroup_subsys devices_subsys;
-
/*
* called under devcgroup_mutex
*/
@@ -184,7 +182,7 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg)
static int devcgroup_online(struct cgroup_subsys_state *css)
{
struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
- struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css));
+ struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css->parent);
int ret = 0;
mutex_lock(&devcgroup_mutex);
@@ -308,57 +306,138 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
}
/**
- * 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
- * @behavior: behavior of the exception
+ * match_exception - iterates the exception list trying to find a complete match
+ * @exceptions: list of exceptions
+ * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @major: device file major number, ~0 to match all
+ * @minor: device file minor number, ~0 to match all
+ * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ *
+ * It is considered a complete match if an exception is found that will
+ * contain the entire range of provided parameters.
+ *
+ * Return: true in case it matches an exception completely
*/
-static bool may_access(struct dev_cgroup *dev_cgroup,
- struct dev_exception_item *refex,
- enum devcg_behavior behavior)
+static bool match_exception(struct list_head *exceptions, short type,
+ u32 major, u32 minor, short access)
{
struct dev_exception_item *ex;
- bool match = false;
- rcu_lockdep_assert(rcu_read_lock_held() ||
- lockdep_is_held(&devcgroup_mutex),
- "device_cgroup::may_access() called without proper synchronization");
+ list_for_each_entry_rcu(ex, exceptions, list) {
+ if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+ continue;
+ if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+ continue;
+ if (ex->major != ~0 && ex->major != major)
+ continue;
+ if (ex->minor != ~0 && ex->minor != minor)
+ continue;
+ /* provided access cannot have more than the exception rule */
+ if (access & (~ex->access))
+ continue;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * match_exception_partial - iterates the exception list trying to find a partial match
+ * @exceptions: list of exceptions
+ * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @major: device file major number, ~0 to match all
+ * @minor: device file minor number, ~0 to match all
+ * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ *
+ * It is considered a partial match if an exception's range is found to
+ * contain *any* of the devices specified by provided parameters. This is
+ * used to make sure no extra access is being granted that is forbidden by
+ * any of the exception list.
+ *
+ * Return: true in case the provided range mat matches an exception completely
+ */
+static bool match_exception_partial(struct list_head *exceptions, short type,
+ u32 major, u32 minor, short access)
+{
+ struct dev_exception_item *ex;
- list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
- if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+ list_for_each_entry_rcu(ex, exceptions, list) {
+ if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
continue;
- if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+ if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
continue;
- if (ex->major != ~0 && ex->major != refex->major)
+ /*
+ * We must be sure that both the exception and the provided
+ * range aren't masking all devices
+ */
+ if (ex->major != ~0 && major != ~0 && ex->major != major)
continue;
- if (ex->minor != ~0 && ex->minor != refex->minor)
+ if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)
continue;
- if (refex->access & (~ex->access))
+ /*
+ * In order to make sure the provided range isn't matching
+ * an exception, all its access bits shouldn't match the
+ * exception's access bits
+ */
+ if (!(access & ex->access))
continue;
- match = true;
- break;
+ return true;
}
+ return false;
+}
+
+/**
+ * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions
+ * @dev_cgroup: dev cgroup to be tested against
+ * @refex: new exception
+ * @behavior: behavior of the exception's dev_cgroup
+ *
+ * This is used to make sure a child cgroup won't have more privileges
+ * than its parent
+ */
+static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
+ struct dev_exception_item *refex,
+ enum devcg_behavior behavior)
+{
+ bool match = false;
+
+ rcu_lockdep_assert(rcu_read_lock_held() ||
+ lockdep_is_held(&devcgroup_mutex),
+ "device_cgroup:verify_new_ex called without proper synchronization");
if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
if (behavior == DEVCG_DEFAULT_ALLOW) {
- /* the exception will deny access to certain devices */
+ /*
+ * new exception in the child doesn't matter, only
+ * adding extra restrictions
+ */
return true;
} else {
- /* the exception will allow access to certain devices */
+ /*
+ * new exception in the child will add more devices
+ * that can be acessed, so it can't match any of
+ * parent's exceptions, even slightly
+ */
+ match = match_exception_partial(&dev_cgroup->exceptions,
+ refex->type,
+ refex->major,
+ refex->minor,
+ refex->access);
+
if (match)
- /*
- * a new exception allowing access shouldn't
- * match an parent's exception
- */
return false;
return true;
}
} else {
- /* only behavior == DEVCG_DEFAULT_DENY allowed here */
+ /*
+ * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore
+ * the new exception will add access to more devices and must
+ * be contained completely in an parent's exception to be
+ * allowed
+ */
+ match = match_exception(&dev_cgroup->exceptions, refex->type,
+ refex->major, refex->minor,
+ refex->access);
+
if (match)
/* parent has an exception that matches the proposed */
return true;
@@ -376,11 +455,42 @@ static bool may_access(struct dev_cgroup *dev_cgroup,
static int parent_has_perm(struct dev_cgroup *childcg,
struct dev_exception_item *ex)
{
- struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css));
+ struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);
if (!parent)
return 1;
- return may_access(parent, ex, childcg->behavior);
+ return verify_new_ex(parent, ex, childcg->behavior);
+}
+
+/**
+ * parent_allows_removal - verify if it's ok to remove an exception
+ * @childcg: child cgroup from where the exception will be removed
+ * @ex: exception being removed
+ *
+ * When removing an exception in cgroups with default ALLOW policy, it must
+ * be checked if removing it will give the child cgroup more access than the
+ * parent.
+ *
+ * Return: true if it's ok to remove exception, false otherwise
+ */
+static bool parent_allows_removal(struct dev_cgroup *childcg,
+ struct dev_exception_item *ex)
+{
+ struct dev_cgroup *parent = css_to_devcgroup(childcg->css.parent);
+
+ if (!parent)
+ return true;
+
+ /* It's always allowed to remove access to devices */
+ if (childcg->behavior == DEVCG_DEFAULT_DENY)
+ return true;
+
+ /*
+ * Make sure you're not removing part or a whole exception existing in
+ * the parent cgroup
+ */
+ return !match_exception_partial(&parent->exceptions, ex->type,
+ ex->major, ex->minor, ex->access);
}
/**
@@ -477,13 +587,6 @@ static int propagate_exception(struct dev_cgroup *devcg_root,
return rc;
}
-static inline bool has_children(struct dev_cgroup *devcgroup)
-{
- struct cgroup *cgrp = devcgroup->css.cgroup;
-
- return !list_empty(&cgrp->children);
-}
-
/*
* Modify the exception list using allow/deny rules.
* CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
@@ -498,13 +601,13 @@ static inline bool has_children(struct dev_cgroup *devcgroup)
* parent cgroup has the access you're asking for.
*/
static int devcgroup_update_access(struct dev_cgroup *devcgroup,
- int filetype, const char *buffer)
+ int filetype, char *buffer)
{
const char *b;
char temp[12]; /* 11 + 1 characters needed for a u32 */
int count, rc = 0;
struct dev_exception_item ex;
- struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css));
+ struct dev_cgroup *parent = css_to_devcgroup(devcgroup->css.parent);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -516,7 +619,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
case 'a':
switch (filetype) {
case DEVCG_ALLOW:
- if (has_children(devcgroup))
+ if (css_has_online_children(&devcgroup->css))
return -EINVAL;
if (!may_allow_all(parent))
@@ -532,7 +635,7 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
return rc;
break;
case DEVCG_DENY:
- if (has_children(devcgroup))
+ if (css_has_online_children(&devcgroup->css))
return -EINVAL;
dev_exception_clean(devcgroup);
@@ -618,17 +721,21 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
switch (filetype) {
case DEVCG_ALLOW:
- if (!parent_has_perm(devcgroup, &ex))
- return -EPERM;
/*
* 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) {
+ /* Check if the parent allows removing it first */
+ if (!parent_allows_removal(devcgroup, &ex))
+ return -EPERM;
dev_exception_rm(devcgroup, &ex);
- return 0;
+ break;
}
+
+ if (!parent_has_perm(devcgroup, &ex))
+ return -EPERM;
rc = dev_exception_add(devcgroup, &ex);
break;
case DEVCG_DENY:
@@ -653,27 +760,27 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
return rc;
}
-static int devcgroup_access_write(struct cgroup_subsys_state *css,
- struct cftype *cft, const char *buffer)
+static ssize_t devcgroup_access_write(struct kernfs_open_file *of,
+ char *buf, size_t nbytes, loff_t off)
{
int retval;
mutex_lock(&devcgroup_mutex);
- retval = devcgroup_update_access(css_to_devcgroup(css),
- cft->private, buffer);
+ retval = devcgroup_update_access(css_to_devcgroup(of_css(of)),
+ of_cft(of)->private, strstrip(buf));
mutex_unlock(&devcgroup_mutex);
- return retval;
+ return retval ?: nbytes;
}
static struct cftype dev_cgroup_files[] = {
{
.name = "allow",
- .write_string = devcgroup_access_write,
+ .write = devcgroup_access_write,
.private = DEVCG_ALLOW,
},
{
.name = "deny",
- .write_string = devcgroup_access_write,
+ .write = devcgroup_access_write,
.private = DEVCG_DENY,
},
{
@@ -684,13 +791,11 @@ static struct cftype dev_cgroup_files[] = {
{ } /* terminate */
};
-struct cgroup_subsys devices_subsys = {
- .name = "devices",
+struct cgroup_subsys devices_cgrp_subsys = {
.css_alloc = devcgroup_css_alloc,
.css_free = devcgroup_css_free,
.css_online = devcgroup_online,
.css_offline = devcgroup_offline,
- .subsys_id = devices_subsys_id,
.base_cftypes = dev_cgroup_files,
};
@@ -708,18 +813,18 @@ 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;
+ bool rc;
rcu_read_lock();
dev_cgroup = task_devcgroup(current);
- rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
+ if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW)
+ /* Can't match any of the exceptions, even partially */
+ rc = !match_exception_partial(&dev_cgroup->exceptions,
+ type, major, minor, access);
+ else
+ /* Need to match completely one exception to be allowed */
+ rc = match_exception(&dev_cgroup->exceptions, type, major,
+ minor, access);
rcu_read_unlock();
if (!rc)
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index babd8626bf96..6b540f1822e0 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -139,7 +139,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
int error;
int size;
- if (!inode->i_op || !inode->i_op->getxattr)
+ if (!inode->i_op->getxattr)
return -EOPNOTSUPP;
desc = init_desc(type);
if (IS_ERR(desc))
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 996092f21b64..6e0bd933b6a9 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -64,7 +64,7 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
int error;
int count = 0;
- if (!inode->i_op || !inode->i_op->getxattr)
+ if (!inode->i_op->getxattr)
return -EOPNOTSUPP;
for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) {
diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c
index aab9fa5a8231..90987d15b6fe 100644
--- a/security/integrity/integrity_audit.c
+++ b/security/integrity/integrity_audit.c
@@ -40,7 +40,7 @@ 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,
+ task_pid_nr(current),
from_kuid(&init_user_ns, current_cred()->uid),
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
diff --git a/security/keys/compat.c b/security/keys/compat.c
index bbd32c729dbb..347896548ad3 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -65,8 +65,8 @@ no_payload:
* taking a 32-bit syscall are zero. If you can, you should call sys_keyctl()
* directly.
*/
-asmlinkage long compat_sys_keyctl(u32 option,
- u32 arg2, u32 arg3, u32 arg4, u32 arg5)
+COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
+ u32, arg2, u32, arg3, u32, arg4, u32, arg5)
{
switch (option) {
case KEYCTL_GET_KEYRING_ID:
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 9a62045e6282..69fdf3bc765b 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -220,7 +220,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
*/
BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
- audit_log_format(ab, " pid=%d comm=", tsk->pid);
+ audit_log_format(ab, " pid=%d comm=", task_pid_nr(tsk));
audit_log_untrustedstring(ab, tsk->comm);
switch (a->type) {
@@ -278,9 +278,12 @@ static void dump_common_audit_data(struct audit_buffer *ab,
}
case LSM_AUDIT_DATA_TASK:
tsk = a->u.tsk;
- if (tsk && tsk->pid) {
- audit_log_format(ab, " pid=%d comm=", tsk->pid);
- audit_log_untrustedstring(ab, tsk->comm);
+ if (tsk) {
+ pid_t pid = task_pid_nr(tsk);
+ if (pid) {
+ audit_log_format(ab, " pid=%d comm=", pid);
+ audit_log_untrustedstring(ab, tsk->comm);
+ }
}
break;
case LSM_AUDIT_DATA_NET:
diff --git a/security/security.c b/security/security.c
index d91fec458e90..31614e9e96e5 100644
--- a/security/security.c
+++ b/security/security.c
@@ -433,11 +433,20 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir,
}
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
- struct path *new_dir, struct dentry *new_dentry)
+ struct path *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
{
if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
(new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
return 0;
+
+ if (flags & RENAME_EXCHANGE) {
+ int err = security_ops->path_rename(new_dir, new_dentry,
+ old_dir, old_dentry);
+ if (err)
+ return err;
+ }
+
return security_ops->path_rename(old_dir, old_dentry, new_dir,
new_dentry);
}
@@ -524,11 +533,20 @@ int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
}
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags)
{
if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
(new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
return 0;
+
+ if (flags & RENAME_EXCHANGE) {
+ int err = security_ops->inode_rename(new_dir, new_dentry,
+ old_dir, old_dentry);
+ if (err)
+ return err;
+ }
+
return security_ops->inode_rename(old_dir, old_dentry,
new_dir, new_dentry);
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d4cbf7d16f07..83d06db34d03 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3320,6 +3320,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
case F_GETLK:
case F_SETLK:
case F_SETLKW:
+ case F_OFD_GETLK:
+ case F_OFD_SETLK:
+ case F_OFD_SETLKW:
#if BITS_PER_LONG == 32
case F_GETLK64:
case F_SETLK64:
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 9f0584710c85..1450f85b946d 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -46,10 +46,11 @@ static inline void selinux_xfrm_notify_policyload(void)
{
struct net *net;
- atomic_inc(&flow_cache_genid);
rtnl_lock();
- for_each_net(net)
+ for_each_net(net) {
+ atomic_inc(&net->xfrm.flow_cache_genid);
rt_genid_bump_all(net);
+ }
rtnl_unlock();
}
#else
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 80a09c37cac8..a3386d119425 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -173,7 +173,7 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
* Use filesystem name if filesystem does not support rename()
* operation.
*/
- if (inode->i_op && !inode->i_op->rename)
+ if (!inode->i_op->rename)
goto prepend_filesystem_name;
}
/* Prepend device name. */
@@ -282,7 +282,7 @@ char *tomoyo_realpath_from_path(struct path *path)
* Get local name for filesystems without rename() operation
* or dentry without vfsmount.
*/
- if (!path->mnt || (inode->i_op && !inode->i_op->rename))
+ if (!path->mnt || !inode->i_op->rename)
pos = tomoyo_get_local_path(path->dentry, buf,
buf_len - 1);
/* Get absolute name for the rest. */