aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig15
-rw-r--r--security/apparmor/apparmorfs.c6
-rw-r--r--security/apparmor/include/path.h7
-rw-r--r--security/apparmor/lsm.c18
-rw-r--r--security/integrity/digsig.c1
-rw-r--r--security/integrity/evm/evm.h11
-rw-r--r--security/integrity/evm/evm_crypto.c78
-rw-r--r--security/integrity/evm/evm_main.c75
-rw-r--r--security/integrity/evm/evm_secfs.c20
-rw-r--r--security/integrity/iint.c7
-rw-r--r--security/integrity/ima/Kconfig1
-rw-r--r--security/integrity/ima/ima.h9
-rw-r--r--security/integrity/ima/ima_api.c30
-rw-r--r--security/integrity/ima/ima_appraise.c101
-rw-r--r--security/integrity/ima/ima_crypto.c4
-rw-r--r--security/integrity/ima/ima_init.c2
-rw-r--r--security/integrity/ima/ima_main.c159
-rw-r--r--security/integrity/ima/ima_policy.c62
-rw-r--r--security/integrity/ima/ima_queue.c2
-rw-r--r--security/integrity/ima/ima_template.c11
-rw-r--r--security/integrity/ima/ima_template_lib.c11
-rw-r--r--security/integrity/integrity.h52
-rw-r--r--security/keys/big_key.c111
-rw-r--r--security/keys/keyring.c7
-rw-r--r--security/keys/trusted.c35
-rw-r--r--security/loadpin/loadpin.c1
-rw-r--r--security/security.c95
-rw-r--r--security/selinux/avc.c282
-rw-r--r--security/selinux/hooks.c1030
-rw-r--r--security/selinux/ibpkey.c3
-rw-r--r--security/selinux/include/avc.h38
-rw-r--r--security/selinux/include/avc_ss.h9
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/conditional.h11
-rw-r--r--security/selinux/include/netlabel.h25
-rw-r--r--security/selinux/include/objsec.h6
-rw-r--r--security/selinux/include/security.h231
-rw-r--r--security/selinux/include/xfrm.h4
-rw-r--r--security/selinux/netif.c2
-rw-r--r--security/selinux/netlabel.c151
-rw-r--r--security/selinux/netnode.c4
-rw-r--r--security/selinux/netport.c2
-rw-r--r--security/selinux/selinuxfs.c494
-rw-r--r--security/selinux/ss/avtab.c9
-rw-r--r--security/selinux/ss/avtab.h3
-rw-r--r--security/selinux/ss/ebitmap.c7
-rw-r--r--security/selinux/ss/ebitmap.h3
-rw-r--r--security/selinux/ss/hashtab.c8
-rw-r--r--security/selinux/ss/hashtab.h4
-rw-r--r--security/selinux/ss/mls.c72
-rw-r--r--security/selinux/ss/mls.h38
-rw-r--r--security/selinux/ss/services.c1119
-rw-r--r--security/selinux/ss/services.h24
-rw-r--r--security/selinux/ss/status.c47
-rw-r--r--security/selinux/xfrm.c25
-rw-r--r--security/smack/smack.h1
-rw-r--r--security/smack/smack_access.c40
-rw-r--r--security/smack/smack_lsm.c242
-rw-r--r--security/tomoyo/audit.c8
-rw-r--r--security/tomoyo/common.c18
-rw-r--r--security/tomoyo/common.h6
-rw-r--r--security/tomoyo/network.c5
-rw-r--r--security/tomoyo/securityfs_if.c6
-rw-r--r--security/yama/yama_lsm.c11
64 files changed, 3185 insertions, 1736 deletions
diff --git a/security/Kconfig b/security/Kconfig
index b0cb9a5f9448..c4302067a3ad 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -154,6 +154,7 @@ config HARDENED_USERCOPY
bool "Harden memory copies between kernel and userspace"
depends on HAVE_HARDENED_USERCOPY_ALLOCATOR
select BUG
+ imply STRICT_DEVMEM
help
This option checks for obviously wrong memory regions when
copying memory to/from the kernel (via copy_to_user() and
@@ -163,6 +164,20 @@ config HARDENED_USERCOPY
or are part of the kernel text. This kills entire classes
of heap overflow exploits and similar kernel memory exposures.
+config HARDENED_USERCOPY_FALLBACK
+ bool "Allow usercopy whitelist violations to fallback to object size"
+ depends on HARDENED_USERCOPY
+ default y
+ help
+ This is a temporary option that allows missing usercopy whitelists
+ to be discovered via a WARN() to the kernel log, instead of
+ rejecting the copy, falling back to non-whitelisted hardened
+ usercopy that checks the slab allocation size instead of the
+ whitelist size. This option will be removed once it seems like
+ all missing usercopy whitelists have been identified and fixed.
+ Booting with "slab_common.usercopy_fallback=Y/N" can change
+ this setting.
+
config HARDENED_USERCOPY_PAGESPAN
bool "Refuse to copy allocations that span multiple pages"
depends on HARDENED_USERCOPY
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index f4308683c0af..949dd8a48164 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -569,16 +569,16 @@ static int ns_revision_open(struct inode *inode, struct file *file)
return 0;
}
-static unsigned int ns_revision_poll(struct file *file, poll_table *pt)
+static __poll_t ns_revision_poll(struct file *file, poll_table *pt)
{
struct aa_revision *rev = file->private_data;
- unsigned int mask = 0;
+ __poll_t mask = 0;
if (rev) {
mutex_lock_nested(&rev->ns->lock, rev->ns->level);
poll_wait(file, &rev->ns->wait, pt);
if (rev->last_read < rev->ns->revision)
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
mutex_unlock(&rev->ns->lock);
}
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index 05fb3305671e..e042b994f2b8 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -43,15 +43,10 @@ struct aa_buffers {
DECLARE_PER_CPU(struct aa_buffers, aa_buffers);
-#define COUNT_ARGS(X...) COUNT_ARGS_HELPER(, ##X, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
-#define COUNT_ARGS_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, n, X...) n
-#define CONCAT(X, Y) X ## Y
-#define CONCAT_AFTER(X, Y) CONCAT(X, Y)
-
#define ASSIGN(FN, X, N) ((X) = FN(N))
#define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/
#define EVAL2(FN, X, Y...) do { ASSIGN(FN, X, 1); EVAL1(FN, Y); } while (0)
-#define EVAL(FN, X...) CONCAT_AFTER(EVAL, COUNT_ARGS(X))(FN, X)
+#define EVAL(FN, X...) CONCATENATE(EVAL, COUNT_ARGS(X))(FN, X)
#define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++)
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 956edebf83eb..ce2b89e9ad94 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -23,7 +23,6 @@
#include <linux/sysctl.h>
#include <linux/audit.h>
#include <linux/user_namespace.h>
-#include <linux/kmemleak.h>
#include <net/sock.h>
#include "include/apparmor.h"
@@ -725,16 +724,23 @@ static int apparmor_task_setrlimit(struct task_struct *task,
}
static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
- int sig, u32 secid)
+ int sig, const struct cred *cred)
{
struct aa_label *cl, *tl;
int error;
- if (secid)
- /* TODO: after secid to label mapping is done.
- * Dealing with USB IO specific behavior
+ if (cred) {
+ /*
+ * Dealing with USB IO specific behavior
*/
- return 0;
+ cl = aa_get_newest_cred_label(cred);
+ tl = aa_get_task_label(target);
+ error = aa_may_signal(cl, tl, sig);
+ aa_put_label(cl);
+ aa_put_label(tl);
+ return error;
+ }
+
cl = __begin_current_label_crit_section();
tl = aa_get_task_label(target);
error = aa_may_signal(cl, tl, sig);
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 6f9e4ce568cd..9bb0a7f2863e 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -18,6 +18,7 @@
#include <linux/cred.h>
#include <linux/key-type.h>
#include <linux/digsig.h>
+#include <linux/vmalloc.h>
#include <crypto/public_key.h>
#include <keys/system_keyring.h>
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 241aca315b0c..45c4a89c02ff 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -23,13 +23,14 @@
#define EVM_INIT_HMAC 0x0001
#define EVM_INIT_X509 0x0002
-#define EVM_SETUP 0x80000000 /* userland has signaled key load */
+#define EVM_ALLOW_METADATA_WRITES 0x0004
+#define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */
-#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP)
+#define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509)
+#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \
+ EVM_ALLOW_METADATA_WRITES)
extern int evm_initialized;
-extern char *evm_hmac;
-extern char *evm_hash;
#define EVM_ATTR_FSUUID 0x0001
@@ -51,7 +52,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
size_t req_xattr_value_len, char *digest);
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
- size_t req_xattr_value_len, char *digest);
+ size_t req_xattr_value_len, char type, char *digest);
int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
char *hmac_val);
int evm_init_secfs(void);
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index bcd64baf8788..a46fba322340 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -37,6 +37,9 @@ static DEFINE_MUTEX(mutex);
static unsigned long evm_set_key_flags;
+static char * const evm_hmac = "hmac(sha1)";
+static char * const evm_hash = "sha1";
+
/**
* evm_set_key() - set EVM HMAC key from the kernel
* @key: pointer to a buffer with the key data
@@ -138,7 +141,7 @@ out:
* protection.)
*/
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
- char *digest)
+ char type, char *digest)
{
struct h_misc {
unsigned long ino;
@@ -149,8 +152,13 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
} hmac_misc;
memset(&hmac_misc, 0, sizeof(hmac_misc));
- hmac_misc.ino = inode->i_ino;
- hmac_misc.generation = inode->i_generation;
+ /* Don't include the inode or generation number in portable
+ * signatures
+ */
+ if (type != EVM_XATTR_PORTABLE_DIGSIG) {
+ hmac_misc.ino = inode->i_ino;
+ hmac_misc.generation = inode->i_generation;
+ }
/* The hmac uid and gid must be encoded in the initial user
* namespace (not the filesystems user namespace) as encoding
* them in the filesystems user namespace allows an attack
@@ -163,7 +171,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
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));
- if (evm_hmac_attrs & EVM_ATTR_FSUUID)
+ if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
+ type != EVM_XATTR_PORTABLE_DIGSIG)
crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0],
sizeof(inode->i_sb->s_uuid));
crypto_shash_final(desc, digest);
@@ -189,6 +198,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
char *xattr_value = NULL;
int error;
int size;
+ bool ima_present = false;
if (!(inode->i_opflags & IOP_XATTR))
return -EOPNOTSUPP;
@@ -199,11 +209,18 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
error = -ENODATA;
for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+ bool is_ima = false;
+
+ if (strcmp(*xattrname, XATTR_NAME_IMA) == 0)
+ is_ima = true;
+
if ((req_xattr_name && req_xattr_value)
&& !strcmp(*xattrname, req_xattr_name)) {
error = 0;
crypto_shash_update(desc, (const u8 *)req_xattr_value,
req_xattr_value_len);
+ if (is_ima)
+ ima_present = true;
continue;
}
size = vfs_getxattr_alloc(dentry, *xattrname,
@@ -218,9 +235,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
error = 0;
xattr_size = size;
crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
+ if (is_ima)
+ ima_present = true;
}
- hmac_add_misc(desc, inode, digest);
+ hmac_add_misc(desc, inode, type, digest);
+ /* Portable EVM signatures must include an IMA hash */
+ if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
+ return -EPERM;
out:
kfree(xattr_value);
kfree(desc);
@@ -232,17 +254,45 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
char *digest)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
- req_xattr_value_len, EVM_XATTR_HMAC, digest);
+ req_xattr_value_len, EVM_XATTR_HMAC, digest);
}
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
- char *digest)
+ char type, char *digest)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
- req_xattr_value_len, IMA_XATTR_DIGEST, digest);
+ req_xattr_value_len, type, digest);
}
+static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
+{
+ const struct evm_ima_xattr_data *xattr_data = NULL;
+ struct integrity_iint_cache *iint;
+ int rc = 0;
+
+ iint = integrity_iint_find(inode);
+ if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
+ return 1;
+
+ /* Do this the hard way */
+ rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
+ GFP_NOFS);
+ if (rc <= 0) {
+ if (rc == -ENODATA)
+ return 0;
+ return rc;
+ }
+ if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
+ rc = 1;
+ else
+ rc = 0;
+
+ kfree(xattr_data);
+ return rc;
+}
+
+
/*
* Calculate the hmac and update security.evm xattr
*
@@ -255,6 +305,16 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
struct evm_ima_xattr_data xattr_data;
int rc = 0;
+ /*
+ * Don't permit any transformation of the EVM xattr if the signature
+ * is of an immutable type
+ */
+ rc = evm_is_immutable(dentry, inode);
+ if (rc < 0)
+ return rc;
+ if (rc)
+ return -EPERM;
+
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, xattr_data.digest);
if (rc == 0) {
@@ -280,7 +340,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
}
crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
- hmac_add_misc(desc, inode, hmac_val);
+ hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
kfree(desc);
return 0;
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 9826c02e2db8..9ea9c19a545c 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -30,11 +30,9 @@
int evm_initialized;
-static char *integrity_status_msg[] = {
- "pass", "fail", "no_label", "no_xattrs", "unknown"
+static const char * const integrity_status_msg[] = {
+ "pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown"
};
-char *evm_hmac = "hmac(sha1)";
-char *evm_hash = "sha1";
int evm_hmac_attrs;
char *evm_config_xattrnames[] = {
@@ -76,6 +74,11 @@ static void __init evm_init_config(void)
pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs);
}
+static bool evm_key_loaded(void)
+{
+ return (bool)(evm_initialized & EVM_KEY_MASK);
+}
+
static int evm_find_protected_xattrs(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
@@ -121,9 +124,11 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
struct evm_ima_xattr_data *xattr_data = NULL;
struct evm_ima_xattr_data calc;
enum integrity_status evm_status = INTEGRITY_PASS;
+ struct inode *inode;
int rc, xattr_len;
- if (iint && iint->evm_status == INTEGRITY_PASS)
+ if (iint && (iint->evm_status == INTEGRITY_PASS ||
+ iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
return iint->evm_status;
/* if status is not PASS, try to check again - against -ENOMEM */
@@ -164,22 +169,29 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
rc = -EINVAL;
break;
case EVM_IMA_XATTR_DIGSIG:
+ case EVM_XATTR_PORTABLE_DIGSIG:
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
- xattr_value_len, calc.digest);
+ xattr_value_len, xattr_data->type,
+ calc.digest);
if (rc)
break;
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
(const char *)xattr_data, xattr_len,
calc.digest, sizeof(calc.digest));
if (!rc) {
- /* Replace RSA with HMAC if not mounted readonly and
- * not immutable
- */
- if (!IS_RDONLY(d_backing_inode(dentry)) &&
- !IS_IMMUTABLE(d_backing_inode(dentry)))
+ inode = d_backing_inode(dentry);
+
+ if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
+ if (iint)
+ iint->flags |= EVM_IMMUTABLE_DIGSIG;
+ evm_status = INTEGRITY_PASS_IMMUTABLE;
+ } else if (!IS_RDONLY(inode) &&
+ !(inode->i_sb->s_readonly_remount) &&
+ !IS_IMMUTABLE(inode)) {
evm_update_evmxattr(dentry, xattr_name,
xattr_value,
xattr_value_len);
+ }
}
break;
default:
@@ -241,7 +253,7 @@ enum integrity_status evm_verifyxattr(struct dentry *dentry,
void *xattr_value, size_t xattr_value_len,
struct integrity_iint_cache *iint)
{
- if (!evm_initialized || !evm_protected_xattr(xattr_name))
+ if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
return INTEGRITY_UNKNOWN;
if (!iint) {
@@ -265,7 +277,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
- if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
+ if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
return 0;
return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
}
@@ -280,7 +292,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
* affect security.evm. An interesting side affect of writing posix xattr
* acls is their modifying of the i_mode, which is included in security.evm.
* For posix xattr acls only, permit security.evm, even if it currently
- * doesn't exist, to be updated.
+ * doesn't exist, to be updated unless the EVM signature is immutable.
*/
static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
@@ -299,6 +311,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
return 0;
goto out;
}
+
evm_status = evm_verify_current_integrity(dentry);
if (evm_status == INTEGRITY_NOXATTRS) {
struct integrity_iint_cache *iint;
@@ -345,10 +358,17 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
{
const struct evm_ima_xattr_data *xattr_data = xattr_value;
+ /* Policy permits modification of the protected xattrs even though
+ * there's no HMAC key loaded
+ */
+ if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
+ return 0;
+
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
if (!xattr_value_len)
return -EINVAL;
- if (xattr_data->type != EVM_IMA_XATTR_DIGSIG)
+ if (xattr_data->type != EVM_IMA_XATTR_DIGSIG &&
+ xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
return -EPERM;
}
return evm_protect_xattr(dentry, xattr_name, xattr_value,
@@ -365,6 +385,12 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
*/
int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
+ /* Policy permits modification of the protected xattrs even though
+ * there's no HMAC key loaded
+ */
+ if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
+ return 0;
+
return evm_protect_xattr(dentry, xattr_name, NULL, 0);
}
@@ -393,8 +419,8 @@ static void evm_reset_status(struct inode *inode)
void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
- if (!evm_initialized || (!evm_protected_xattr(xattr_name)
- && !posix_xattr_acl(xattr_name)))
+ if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name)
+ && !posix_xattr_acl(xattr_name)))
return;
evm_reset_status(dentry->d_inode);
@@ -414,7 +440,7 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
*/
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
{
- if (!evm_initialized || !evm_protected_xattr(xattr_name))
+ if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
return;
evm_reset_status(dentry->d_inode);
@@ -425,12 +451,21 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
/**
* evm_inode_setattr - prevent updating an invalid EVM extended attribute
* @dentry: pointer to the affected dentry
+ *
+ * Permit update of file attributes when files have a valid EVM signature,
+ * except in the case of them having an immutable portable signature.
*/
int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
+ /* Policy permits modification of the protected attrs even though
+ * there's no HMAC key loaded
+ */
+ if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
+ return 0;
+
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
return 0;
evm_status = evm_verify_current_integrity(dentry);
@@ -456,7 +491,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
*/
void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
{
- if (!evm_initialized)
+ if (!evm_key_loaded())
return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
@@ -473,7 +508,7 @@ int evm_inode_init_security(struct inode *inode,
struct evm_ima_xattr_data *xattr_data;
int rc;
- if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
+ if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
return 0;
xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index 319cf16d6603..feba03bbedae 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -40,7 +40,7 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
if (*ppos != 0)
return 0;
- sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP));
+ sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
return rc;
@@ -63,7 +63,7 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
{
int i, ret;
- if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP))
+ if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
return -EPERM;
ret = kstrtoint_from_user(buf, count, 0, &i);
@@ -75,16 +75,30 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
if (!i || (i & ~EVM_INIT_MASK) != 0)
return -EINVAL;
+ /* Don't allow a request to freshly enable metadata writes if
+ * keys are loaded.
+ */
+ if ((i & EVM_ALLOW_METADATA_WRITES) &&
+ ((evm_initialized & EVM_KEY_MASK) != 0) &&
+ !(evm_initialized & EVM_ALLOW_METADATA_WRITES))
+ return -EPERM;
+
if (i & EVM_INIT_HMAC) {
ret = evm_init_key();
if (ret != 0)
return ret;
/* Forbid further writes after the symmetric key is loaded */
- i |= EVM_SETUP;
+ i |= EVM_SETUP_COMPLETE;
}
evm_initialized |= i;
+ /* Don't allow protected metadata modification if a symmetric key
+ * is loaded
+ */
+ if (evm_initialized & EVM_INIT_HMAC)
+ evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
+
return count;
}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index c84e05866052..f266e4b3b7d4 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -74,10 +74,12 @@ static void iint_free(struct integrity_iint_cache *iint)
iint->ima_hash = NULL;
iint->version = 0;
iint->flags = 0UL;
+ iint->atomic_flags = 0UL;
iint->ima_file_status = INTEGRITY_UNKNOWN;
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_read_status = INTEGRITY_UNKNOWN;
+ iint->ima_creds_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
iint->measured_pcrs = 0;
kmem_cache_free(iint_cache, iint);
@@ -153,14 +155,13 @@ static void init_once(void *foo)
struct integrity_iint_cache *iint = foo;
memset(iint, 0, sizeof(*iint));
- iint->version = 0;
- iint->flags = 0UL;
iint->ima_file_status = INTEGRITY_UNKNOWN;
iint->ima_mmap_status = INTEGRITY_UNKNOWN;
iint->ima_bprm_status = INTEGRITY_UNKNOWN;
iint->ima_read_status = INTEGRITY_UNKNOWN;
+ iint->ima_creds_status = INTEGRITY_UNKNOWN;
iint->evm_status = INTEGRITY_UNKNOWN;
- iint->measured_pcrs = 0;
+ mutex_init(&iint->mutex);
}
static int __init integrity_iintcache_init(void)
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 35ef69312811..6a8f67714c83 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -10,6 +10,7 @@ config IMA
select CRYPTO_HASH_INFO
select TCG_TPM if HAS_IOMEM && !UML
select TCG_TIS if TCG_TPM && X86
+ select TCG_CRB if TCG_TPM && ACPI
select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES
help
The Trusted Computing Group(TCG) runtime Integrity
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index d52b487ad259..35fe91aa1fc9 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -177,6 +177,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
hook(FILE_CHECK) \
hook(MMAP_CHECK) \
hook(BPRM_CHECK) \
+ hook(CREDS_CHECK) \
hook(POST_SETATTR) \
hook(MODULE_CHECK) \
hook(FIRMWARE_CHECK) \
@@ -191,8 +192,8 @@ enum ima_hooks {
};
/* LIM API function definitions */
-int ima_get_action(struct inode *inode, int mask,
- enum ima_hooks func, int *pcr);
+int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
+ int mask, enum ima_hooks func, int *pcr);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
@@ -212,8 +213,8 @@ void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
- int flags, int *pcr);
+int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
+ enum ima_hooks func, int mask, int flags, int *pcr);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index c7e8db0ea4c0..bf88236b7a0b 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -18,6 +18,7 @@
#include <linux/fs.h>
#include <linux/xattr.h>
#include <linux/evm.h>
+#include <linux/iversion.h>
#include "ima.h"
@@ -157,6 +158,8 @@ err_out:
/**
* ima_get_action - appraise & measure decision based on policy.
* @inode: pointer to inode to measure
+ * @cred: pointer to credentials structure to validate
+ * @secid: secid of the task being validated
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
* MAY_APPEND)
* @func: caller identifier
@@ -165,20 +168,21 @@ err_out:
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
- * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
+ * func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
* mask: contains the permission mask
* fsmagic: hex value
*
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
+int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
+ int mask, enum ima_hooks func, int *pcr)
{
- int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
+ int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
- return ima_match_policy(inode, func, mask, flags, pcr);
+ return ima_match_policy(inode, cred, secid, func, mask, flags, pcr);
}
/*
@@ -215,7 +219,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
* which do not support i_version, support is limited to an initial
* measurement/appraisal/audit.
*/
- i_version = file_inode(file)->i_version;
+ i_version = inode_query_iversion(inode);
hash.hdr.algo = algo;
/* Initialize hash digest to 0's in case of failure */
@@ -307,14 +311,17 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename)
{
struct audit_buffer *ab;
- char hash[(iint->ima_hash->length * 2) + 1];
+ char *hash;
const char *algo_name = hash_algo_name[iint->ima_hash->algo];
- char algo_hash[sizeof(hash) + strlen(algo_name) + 2];
int i;
if (iint->flags & IMA_AUDITED)
return;
+ hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL);
+ if (!hash)
+ return;
+
for (i = 0; i < iint->ima_hash->length; i++)
hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
hash[i * 2] = '\0';
@@ -322,18 +329,19 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
ab = audit_log_start(current->audit_context, GFP_KERNEL,
AUDIT_INTEGRITY_RULE);
if (!ab)
- return;
+ goto out;
audit_log_format(ab, "file=");
audit_log_untrustedstring(ab, filename);
- audit_log_format(ab, " hash=");
- snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash);
- audit_log_untrustedstring(ab, algo_hash);
+ audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash);
audit_log_task_info(ab, current);
audit_log_end(ab);
iint->flags |= IMA_AUDITED;
+out:
+ kfree(hash);
+ return;
}
/*
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 65fbcf3c32c7..8bd7a0733e51 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -46,14 +46,18 @@ bool is_ima_appraise_enabled(void)
/*
* ima_must_appraise - set appraise flag
*
- * Return 1 to appraise
+ * Return 1 to appraise or hash
*/
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
{
+ u32 secid;
+
if (!ima_appraise)
return 0;
- return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL);
+ security_task_getsecid(current, &secid);
+ return ima_match_policy(inode, current_cred(), secid, func, mask,
+ IMA_APPRAISE | IMA_HASH, NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -86,6 +90,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
return iint->ima_mmap_status;
case BPRM_CHECK:
return iint->ima_bprm_status;
+ case CREDS_CHECK:
+ return iint->ima_creds_status;
case FILE_CHECK:
case POST_SETATTR:
return iint->ima_file_status;
@@ -106,6 +112,8 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
case BPRM_CHECK:
iint->ima_bprm_status = status;
break;
+ case CREDS_CHECK:
+ iint->ima_creds_status = status;
case FILE_CHECK:
case POST_SETATTR:
iint->ima_file_status = status;
@@ -127,6 +135,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
case BPRM_CHECK:
iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
break;
+ case CREDS_CHECK:
+ iint->flags |= (IMA_CREDS_APPRAISED | IMA_APPRAISED);
+ break;
case FILE_CHECK:
case POST_SETATTR:
iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
@@ -204,7 +215,7 @@ int ima_appraise_measurement(enum ima_hooks func,
int xattr_len, int opened)
{
static const char op[] = "appraise_data";
- char *cause = "unknown";
+ const char *cause = "unknown";
struct dentry *dentry = file_dentry(file);
struct inode *inode = d_backing_inode(dentry);
enum integrity_status status = INTEGRITY_UNKNOWN;
@@ -223,20 +234,29 @@ int ima_appraise_measurement(enum ima_hooks func,
if (opened & FILE_CREATED)
iint->flags |= IMA_NEW_FILE;
if ((iint->flags & IMA_NEW_FILE) &&
- !(iint->flags & IMA_DIGSIG_REQUIRED))
+ (!(iint->flags & IMA_DIGSIG_REQUIRED) ||
+ (inode->i_size == 0)))
status = INTEGRITY_PASS;
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";
+ switch (status) {
+ case INTEGRITY_PASS:
+ case INTEGRITY_PASS_IMMUTABLE:
+ case INTEGRITY_UNKNOWN:
+ break;
+ case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
+ case INTEGRITY_NOLABEL: /* No security.evm xattr. */
+ cause = "missing-HMAC";
+ goto out;
+ case INTEGRITY_FAIL: /* Invalid HMAC/signature. */
+ cause = "invalid-HMAC";
goto out;
+ default:
+ WARN_ONCE(true, "Unexpected integrity status %d\n", status);
}
+
switch (xattr_value->type) {
case IMA_XATTR_DIGEST_NG:
/* first byte contains algorithm id */
@@ -248,6 +268,7 @@ int ima_appraise_measurement(enum ima_hooks func,
status = INTEGRITY_FAIL;
break;
}
+ clear_bit(IMA_DIGSIG, &iint->atomic_flags);
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
iint->ima_hash->length)
/* xattr length may be longer. md5 hash in previous
@@ -266,7 +287,7 @@ int ima_appraise_measurement(enum ima_hooks func,
status = INTEGRITY_PASS;
break;
case EVM_IMA_XATTR_DIGSIG:
- iint->flags |= IMA_DIGSIG;
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
(const char *)xattr_value, rc,
iint->ima_hash->digest,
@@ -287,23 +308,40 @@ int ima_appraise_measurement(enum ima_hooks func,
}
out:
- if (status != INTEGRITY_PASS) {
+ /*
+ * File signatures on some filesystems can not be properly verified.
+ * When such filesystems are mounted by an untrusted mounter or on a
+ * system not willing to accept such a risk, fail the file signature
+ * verification.
+ */
+ if ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
+ ((inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) ||
+ (iint->flags & IMA_FAIL_UNVERIFIABLE_SIGS))) {
+ status = INTEGRITY_FAIL;
+ cause = "unverifiable-signature";
+ integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
+ op, cause, rc, 0);
+ } else if (status != INTEGRITY_PASS) {
+ /* Fix mode, but don't replace file signatures. */
if ((ima_appraise & IMA_APPRAISE_FIX) &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint))
status = INTEGRITY_PASS;
- } else if ((inode->i_size == 0) &&
- (iint->flags & IMA_NEW_FILE) &&
- (xattr_value &&
- xattr_value->type == EVM_IMA_XATTR_DIGSIG)) {
+ }
+
+ /* Permit new files with file signatures, but without data. */
+ if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
+ xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG) {
status = INTEGRITY_PASS;
}
+
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
op, cause, rc, 0);
} else {
ima_cache_flags(iint, func);
}
+
ima_set_cache_status(iint, func, status);
return status;
}
@@ -317,17 +355,20 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
int rc = 0;
/* do not collect and update hash for digital signatures */
- if (iint->flags & IMA_DIGSIG)
+ if (test_bit(IMA_DIGSIG, &iint->atomic_flags))
return;
- if (iint->ima_file_status != INTEGRITY_PASS)
+ if ((iint->ima_file_status != INTEGRITY_PASS) &&
+ !(iint->flags & IMA_HASH))
return;
rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo);
if (rc < 0)
return;
+ inode_lock(file_inode(file));
ima_fix_xattr(dentry, iint);
+ inode_unlock(file_inode(file));
}
/**
@@ -343,23 +384,21 @@ void ima_inode_post_setattr(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
- int must_appraise;
+ int action;
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !(inode->i_opflags & IOP_XATTR))
return;
- must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
+ action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
+ if (!action)
+ __vfs_removexattr(dentry, XATTR_NAME_IMA);
iint = integrity_iint_find(inode);
if (iint) {
- iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
- IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
- IMA_ACTION_RULE_FLAGS);
- if (must_appraise)
- iint->flags |= IMA_APPRAISE;
+ set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
+ if (!action)
+ clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
}
- if (!must_appraise)
- __vfs_removexattr(dentry, XATTR_NAME_IMA);
}
/*
@@ -388,12 +427,12 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
iint = integrity_iint_find(inode);
if (!iint)
return;
-
- iint->flags &= ~IMA_DONE_MASK;
iint->measured_pcrs = 0;
+ set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
if (digsig)
- iint->flags |= IMA_DIGSIG;
- return;
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+ else
+ clear_bit(IMA_DIGSIG, &iint->atomic_flags);
}
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 9057b163c378..4e085a17124f 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -73,6 +73,8 @@ int __init ima_init_crypto(void)
hash_algo_name[ima_hash_algo], rc);
return rc;
}
+ pr_info("Allocated hash algorithm: %s\n",
+ hash_algo_name[ima_hash_algo]);
return 0;
}
@@ -632,7 +634,7 @@ static void __init ima_pcrread(int idx, u8 *pcr)
if (!ima_used_chip)
return;
- if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
+ if (tpm_pcr_read(NULL, idx, pcr) != 0)
pr_err("Error Communicating to TPM chip\n");
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 2967d497a665..29b72cd2502e 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -110,7 +110,7 @@ int __init ima_init(void)
int rc;
ima_used_chip = 0;
- rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i);
+ rc = tpm_pcr_read(NULL, 0, pcr_i);
if (rc == 0)
ima_used_chip = 1;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 770654694efc..74d0bd7e76d7 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -16,6 +16,9 @@
* implements the IMA hooks: ima_bprm_check, ima_file_mmap,
* and ima_file_check.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/file.h>
#include <linux/binfmts.h>
@@ -24,6 +27,8 @@
#include <linux/slab.h>
#include <linux/xattr.h>
#include <linux/ima.h>
+#include <linux/iversion.h>
+#include <linux/fs.h>
#include "ima.h"
@@ -84,10 +89,10 @@ static void ima_rdwr_violation_check(struct file *file,
struct integrity_iint_cache *iint,
int must_measure,
char **pathbuf,
- const char **pathname)
+ const char **pathname,
+ char *filename)
{
struct inode *inode = file_inode(file);
- char filename[NAME_MAX];
fmode_t mode = file->f_mode;
bool send_tomtou = false, send_writers = false;
@@ -96,10 +101,13 @@ static void ima_rdwr_violation_check(struct file *file,
if (!iint)
iint = integrity_iint_find(inode);
/* IMA_MEASURE is set from reader side */
- if (iint && (iint->flags & IMA_MEASURE))
+ if (iint && test_bit(IMA_MUST_MEASURE,
+ &iint->atomic_flags))
send_tomtou = true;
}
} else {
+ if (must_measure)
+ set_bit(IMA_MUST_MEASURE, &iint->atomic_flags);
if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
send_writers = true;
}
@@ -121,21 +129,25 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
struct inode *inode, struct file *file)
{
fmode_t mode = file->f_mode;
+ bool update;
if (!(mode & FMODE_WRITE))
return;
- inode_lock(inode);
+ mutex_lock(&iint->mutex);
if (atomic_read(&inode->i_writecount) == 1) {
- if ((iint->version != inode->i_version) ||
+ update = test_and_clear_bit(IMA_UPDATE_XATTR,
+ &iint->atomic_flags);
+ if (!IS_I_VERSION(inode) ||
+ !inode_eq_iversion(inode, iint->version) ||
(iint->flags & IMA_NEW_FILE)) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0;
- if (iint->flags & IMA_APPRAISE)
+ if (update)
ima_update_xattr(iint, file);
}
}
- inode_unlock(inode);
+ mutex_unlock(&iint->mutex);
}
/**
@@ -159,8 +171,9 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
-static int process_measurement(struct file *file, char *buf, loff_t size,
- int mask, enum ima_hooks func, int opened)
+static int process_measurement(struct file *file, const struct cred *cred,
+ u32 secid, char *buf, loff_t size, int mask,
+ enum ima_hooks func, int opened)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
@@ -168,7 +181,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
char *pathbuf = NULL;
char filename[NAME_MAX];
const char *pathname = NULL;
- int rc = -ENOMEM, action, must_appraise;
+ int rc = 0, action, must_appraise = 0;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
struct evm_ima_xattr_data *xattr_value = NULL;
int xattr_len = 0;
@@ -182,7 +195,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
- action = ima_get_action(inode, mask, func, &pcr);
+ action = ima_get_action(inode, cred, secid, mask, func, &pcr);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
@@ -199,16 +212,39 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
if (action) {
iint = integrity_inode_get(inode);
if (!iint)
- goto out;
+ rc = -ENOMEM;
}
- if (violation_check) {
+ if (!rc && violation_check)
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
- &pathbuf, &pathname);
- if (!action) {
- rc = 0;
- goto out_free;
- }
+ &pathbuf, &pathname, filename);
+
+ inode_unlock(inode);
+
+ if (rc)
+ goto out;
+ if (!action)
+ goto out;
+
+ mutex_lock(&iint->mutex);
+
+ if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
+ /* reset appraisal flags if ima_inode_post_setattr was called */
+ iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+ IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+ IMA_ACTION_FLAGS);
+
+ /*
+ * Re-evaulate the file if either the xattr has changed or the
+ * kernel has no way of detecting file change on the filesystem.
+ * (Limited to privileged mounted filesystems.)
+ */
+ if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags) ||
+ ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
+ !(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) &&
+ !(action & IMA_FAIL_UNVERIFIABLE_SIGS))) {
+ iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
}
/* Determine if already appraised/measured based on bitmask
@@ -223,11 +259,23 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
action ^= IMA_MEASURE;
+ /* HASH sets the digital signature and update flags, nothing else */
+ if ((action & IMA_HASH) &&
+ !(test_bit(IMA_DIGSIG, &iint->atomic_flags))) {
+ xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
+ if ((xattr_value && xattr_len > 2) &&
+ (xattr_value->type == EVM_IMA_XATTR_DIGSIG))
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+ iint->flags |= IMA_HASHED;
+ action ^= IMA_HASH;
+ set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+ }
+
/* Nothing to do, just return existing appraised status */
if (!action) {
if (must_appraise)
rc = ima_get_cache_status(iint, func);
- goto out_digsig;
+ goto out_locked;
}
template_desc = ima_template_desc_current();
@@ -240,7 +288,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
- goto out_digsig;
+ goto out_locked;
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
@@ -248,26 +296,32 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
xattr_value, xattr_len, pcr);
- if (rc == 0 && (action & IMA_APPRAISE_SUBMASK))
+ if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
+ inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file, pathname,
xattr_value, xattr_len, opened);
+ inode_unlock(inode);
+ }
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
rc = 0;
-out_digsig:
- if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
+out_locked:
+ if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
!(iint->flags & IMA_NEW_FILE))
rc = -EACCES;
+ mutex_unlock(&iint->mutex);
kfree(xattr_value);
-out_free:
+out:
if (pathbuf)
__putname(pathbuf);
-out:
- inode_unlock(inode);
- if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
- return -EACCES;
+ if (must_appraise) {
+ if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE))
+ return -EACCES;
+ if (file->f_mode & FMODE_WRITE)
+ set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+ }
return 0;
}
@@ -284,9 +338,14 @@ out:
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
- if (file && (prot & PROT_EXEC))
- return process_measurement(file, NULL, 0, MAY_EXEC,
- MMAP_CHECK, 0);
+ u32 secid;
+
+ if (file && (prot & PROT_EXEC)) {
+ security_task_getsecid(current, &secid);
+ return process_measurement(file, current_cred(), secid, NULL,
+ 0, MAY_EXEC, MMAP_CHECK, 0);
+ }
+
return 0;
}
@@ -305,8 +364,18 @@ int ima_file_mmap(struct file *file, unsigned long prot)
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
- return process_measurement(bprm->file, NULL, 0, MAY_EXEC,
- BPRM_CHECK, 0);
+ int ret;
+ u32 secid;
+
+ security_task_getsecid(current, &secid);
+ ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
+ MAY_EXEC, BPRM_CHECK, 0);
+ if (ret)
+ return ret;
+
+ security_cred_getsecid(bprm->cred, &secid);
+ return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
+ MAY_EXEC, CREDS_CHECK, 0);
}
/**
@@ -321,7 +390,10 @@ int ima_bprm_check(struct linux_binprm *bprm)
*/
int ima_file_check(struct file *file, int mask, int opened)
{
- return process_measurement(file, NULL, 0,
+ u32 secid;
+
+ security_task_getsecid(current, &secid);
+ return process_measurement(file, current_cred(), secid, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
MAY_APPEND), FILE_CHECK, opened);
}
@@ -366,8 +438,10 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id)
if (!file && read_id == READING_MODULE) {
if (!sig_enforce && (ima_appraise & IMA_APPRAISE_MODULES) &&
- (ima_appraise & IMA_APPRAISE_ENFORCE))
+ (ima_appraise & IMA_APPRAISE_ENFORCE)) {
+ pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
+ }
return 0; /* We rely on module signature checking */
}
return 0;
@@ -398,6 +472,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id read_id)
{
enum ima_hooks func;
+ u32 secid;
if (!file && read_id == READING_FIRMWARE) {
if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
@@ -420,7 +495,9 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
}
func = read_idmap[read_id] ?: FILE_CHECK;
- return process_measurement(file, buf, size, MAY_READ, func, 0);
+ security_task_getsecid(current, &secid);
+ return process_measurement(file, current_cred(), secid, buf, size,
+ MAY_READ, func, 0);
}
static int __init init_ima(void)
@@ -430,6 +507,16 @@ static int __init init_ima(void)
ima_init_template_list();
hash_setup(CONFIG_IMA_DEFAULT_HASH);
error = ima_init();
+
+ if (error && strcmp(hash_algo_name[ima_hash_algo],
+ CONFIG_IMA_DEFAULT_HASH) != 0) {
+ pr_info("Allocating %s failed, going to use default hash algorithm %s\n",
+ hash_algo_name[ima_hash_algo], CONFIG_IMA_DEFAULT_HASH);
+ hash_setup_done = 0;
+ hash_setup(CONFIG_IMA_DEFAULT_HASH);
+ error = ima_init();
+ }
+
if (!error) {
ima_initialized = 1;
ima_update_policy_flag();
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index ee4613fa5840..d89bebf85421 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -40,6 +40,8 @@
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
#define DONT_APPRAISE 0x0008
#define AUDIT 0x0040
+#define HASH 0x0100
+#define DONT_HASH 0x0200
#define INVALID_PCR(a) (((a) < 0) || \
(a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8))
@@ -94,6 +96,7 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = {
{.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = CGROUP_SUPER_MAGIC,
.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC,
@@ -139,6 +142,7 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = {
{.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 = SMACK_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC},
@@ -186,6 +190,7 @@ __setup("ima_tcb", default_measure_policy_setup);
static bool ima_use_appraise_tcb __initdata;
static bool ima_use_secure_boot __initdata;
+static bool ima_fail_unverifiable_sigs __ro_after_init;
static int __init policy_setup(char *str)
{
char *p;
@@ -199,6 +204,8 @@ static int __init policy_setup(char *str)
ima_use_appraise_tcb = true;
else if (strcmp(p, "secure_boot") == 0)
ima_use_secure_boot = true;
+ else if (strcmp(p, "fail_securely") == 0)
+ ima_fail_unverifiable_sigs = true;
}
return 1;
@@ -241,16 +248,17 @@ static void ima_lsm_update_rules(void)
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
* @inode: a pointer to an inode
+ * @cred: a pointer to a credentials structure for user validation
+ * @secid: the secid of the task to be validated
* @func: LIM hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
*
* Returns true on rule match, false on failure.
*/
static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
+ const struct cred *cred, u32 secid,
enum ima_hooks func, int mask)
{
- struct task_struct *tsk = current;
- const struct cred *cred = current_cred();
int i;
if ((rule->flags & IMA_FUNC) &&
@@ -285,7 +293,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
return false;
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
- u32 osid, sid;
+ u32 osid;
int retried = 0;
if (!rule->lsm[i].rule)
@@ -305,8 +313,7 @@ retry:
case LSM_SUBJ_USER:
case LSM_SUBJ_ROLE:
case LSM_SUBJ_TYPE:
- security_task_getsecid(tsk, &sid);
- rc = security_filter_rule_match(sid,
+ rc = security_filter_rule_match(secid,
rule->lsm[i].type,
Audit_equal,
rule->lsm[i].rule,
@@ -339,6 +346,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
return IMA_MMAP_APPRAISE;
case BPRM_CHECK:
return IMA_BPRM_APPRAISE;
+ case CREDS_CHECK:
+ return IMA_CREDS_APPRAISE;
case FILE_CHECK:
case POST_SETATTR:
return IMA_FILE_APPRAISE;
@@ -351,6 +360,9 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
/**
* ima_match_policy - decision based on LSM and other conditions
* @inode: pointer to an inode for which the policy decision is being made
+ * @cred: pointer to a credentials structure for which the policy decision is
+ * being made
+ * @secid: LSM secid of the task to be validated
* @func: IMA hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
* @pcr: set the pcr to extend
@@ -362,8 +374,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* list when walking it. Reads are many orders of magnitude more numerous
* than writes so ima_match_policy() is classical RCU candidate.
*/
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
- int flags, int *pcr)
+int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
+ enum ima_hooks func, int mask, int flags, int *pcr)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
@@ -374,14 +386,18 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
if (!(entry->action & actmask))
continue;
- if (!ima_match_rules(entry, inode, func, mask))
+ if (!ima_match_rules(entry, inode, cred, secid, func, mask))
continue;
action |= entry->flags & IMA_ACTION_FLAGS;
action |= entry->action & IMA_DO_MASK;
- if (entry->action & IMA_APPRAISE)
+ if (entry->action & IMA_APPRAISE) {
action |= get_subaction(entry, func);
+ action &= ~IMA_HASH;
+ if (ima_fail_unverifiable_sigs)
+ action |= IMA_FAIL_UNVERIFIABLE_SIGS;
+ }
if (entry->action & IMA_DO_MASK)
actmask &= ~(entry->action | entry->action << 1);
@@ -521,7 +537,7 @@ enum {
Opt_err = -1,
Opt_measure = 1, Opt_dont_measure,
Opt_appraise, Opt_dont_appraise,
- Opt_audit,
+ Opt_audit, Opt_hash, Opt_dont_hash,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
Opt_func, Opt_mask, Opt_fsmagic,
@@ -538,6 +554,8 @@ static match_table_t policy_tokens = {
{Opt_appraise, "appraise"},
{Opt_dont_appraise, "dont_appraise"},
{Opt_audit, "audit"},
+ {Opt_hash, "hash"},
+ {Opt_dont_hash, "dont_hash"},
{Opt_obj_user, "obj_user=%s"},
{Opt_obj_role, "obj_role=%s"},
{Opt_obj_type, "obj_type=%s"},
@@ -671,6 +689,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->action = AUDIT;
break;
+ case Opt_hash:
+ ima_log_string(ab, "action", "hash");
+
+ if (entry->action != UNKNOWN)
+ result = -EINVAL;
+
+ entry->action = HASH;
+ break;
+ case Opt_dont_hash:
+ ima_log_string(ab, "action", "dont_hash");
+
+ if (entry->action != UNKNOWN)
+ result = -EINVAL;
+
+ entry->action = DONT_HASH;
+ break;
case Opt_func:
ima_log_string(ab, "func", args[0].from);
@@ -691,6 +725,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->func = MMAP_CHECK;
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
entry->func = BPRM_CHECK;
+ else if (strcmp(args[0].from, "CREDS_CHECK") == 0)
+ entry->func = CREDS_CHECK;
else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") ==
0)
entry->func = KEXEC_KERNEL_CHECK;
@@ -743,7 +779,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
case Opt_fsuuid:
ima_log_string(ab, "fsuuid", args[0].from);
- if (uuid_is_null(&entry->fsuuid)) {
+ if (!uuid_is_null(&entry->fsuuid)) {
result = -EINVAL;
break;
}
@@ -1040,6 +1076,10 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, pt(Opt_dont_appraise));
if (entry->action & AUDIT)
seq_puts(m, pt(Opt_audit));
+ if (entry->action & HASH)
+ seq_puts(m, pt(Opt_hash));
+ if (entry->action & DONT_HASH)
+ seq_puts(m, pt(Opt_dont_hash));
seq_puts(m, " ");
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index a02a86d51102..418f35e38015 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -145,7 +145,7 @@ static int ima_pcr_extend(const u8 *hash, int pcr)
if (!ima_used_chip)
return result;
- result = tpm_pcr_extend(TPM_ANY_NUM, pcr, hash);
+ result = tpm_pcr_extend(NULL, pcr, hash);
if (result != 0)
pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 7412d0291ab9..30db39b23804 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -377,8 +377,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
break;
if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) {
- pr_err("attempting to restore a template name \
- that is too long\n");
+ pr_err("attempting to restore a template name that is too long\n");
ret = -EINVAL;
break;
}
@@ -389,8 +388,8 @@ int ima_restore_measurement_list(loff_t size, void *buf)
template_name[hdr[HDR_TEMPLATE_NAME].len] = 0;
if (strcmp(template_name, "ima") == 0) {
- pr_err("attempting to restore an unsupported \
- template \"%s\" failed\n", template_name);
+ pr_err("attempting to restore an unsupported template \"%s\" failed\n",
+ template_name);
ret = -EINVAL;
break;
}
@@ -410,8 +409,8 @@ int ima_restore_measurement_list(loff_t size, void *buf)
&(template_desc->fields),
&(template_desc->num_fields));
if (ret < 0) {
- pr_err("attempting to restore the template fmt \"%s\" \
- failed\n", template_desc->fmt);
+ pr_err("attempting to restore the template fmt \"%s\" failed\n",
+ template_desc->fmt);
ret = -EINVAL;
break;
}
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 28af43f63572..5afaa53decc5 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -378,16 +378,11 @@ int ima_eventname_ng_init(struct ima_event_data *event_data,
int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
- enum data_formats fmt = DATA_FMT_HEX;
struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
- int xattr_len = event_data->xattr_len;
- int rc = 0;
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
- goto out;
+ return 0;
- rc = ima_write_template_field_data(xattr_value, xattr_len, fmt,
- field_data);
-out:
- return rc;
+ return ima_write_template_field_data(xattr_value, event_data->xattr_len,
+ DATA_FMT_HEX, field_data);
}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index e1bf040fb110..5e58e02ba8dc 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -25,39 +25,54 @@
#define IMA_COLLECTED 0x00000020
#define IMA_AUDIT 0x00000040
#define IMA_AUDITED 0x00000080
+#define IMA_HASH 0x00000100
+#define IMA_HASHED 0x00000200
/* iint cache flags */
#define IMA_ACTION_FLAGS 0xff000000
-#define IMA_ACTION_RULE_FLAGS 0x06000000
-#define IMA_DIGSIG 0x01000000
-#define IMA_DIGSIG_REQUIRED 0x02000000
-#define IMA_PERMIT_DIRECTIO 0x04000000
-#define IMA_NEW_FILE 0x08000000
+#define IMA_DIGSIG_REQUIRED 0x01000000
+#define IMA_PERMIT_DIRECTIO 0x02000000
+#define IMA_NEW_FILE 0x04000000
+#define EVM_IMMUTABLE_DIGSIG 0x08000000
+#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
- IMA_APPRAISE_SUBMASK)
+ IMA_HASH | IMA_APPRAISE_SUBMASK)
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
- IMA_COLLECTED | IMA_APPRAISED_SUBMASK)
+ IMA_HASHED | IMA_COLLECTED | \
+ IMA_APPRAISED_SUBMASK)
/* iint subaction appraise cache flags */
-#define IMA_FILE_APPRAISE 0x00000100
-#define IMA_FILE_APPRAISED 0x00000200
-#define IMA_MMAP_APPRAISE 0x00000400
-#define IMA_MMAP_APPRAISED 0x00000800
-#define IMA_BPRM_APPRAISE 0x00001000
-#define IMA_BPRM_APPRAISED 0x00002000
-#define IMA_READ_APPRAISE 0x00004000
-#define IMA_READ_APPRAISED 0x00008000
+#define IMA_FILE_APPRAISE 0x00001000
+#define IMA_FILE_APPRAISED 0x00002000
+#define IMA_MMAP_APPRAISE 0x00004000
+#define IMA_MMAP_APPRAISED 0x00008000
+#define IMA_BPRM_APPRAISE 0x00010000
+#define IMA_BPRM_APPRAISED 0x00020000
+#define IMA_READ_APPRAISE 0x00040000
+#define IMA_READ_APPRAISED 0x00080000
+#define IMA_CREDS_APPRAISE 0x00100000
+#define IMA_CREDS_APPRAISED 0x00200000
#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
- IMA_BPRM_APPRAISE | IMA_READ_APPRAISE)
+ IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
+ IMA_CREDS_APPRAISE)
#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
- IMA_BPRM_APPRAISED | IMA_READ_APPRAISED)
+ IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
+ IMA_CREDS_APPRAISED)
+
+/* iint cache atomic_flags */
+#define IMA_CHANGE_XATTR 0
+#define IMA_UPDATE_XATTR 1
+#define IMA_CHANGE_ATTR 2
+#define IMA_DIGSIG 3
+#define IMA_MUST_MEASURE 4
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
EVM_XATTR_HMAC,
EVM_IMA_XATTR_DIGSIG,
IMA_XATTR_DIGEST_NG,
+ EVM_XATTR_PORTABLE_DIGSIG,
IMA_XATTR_LAST
};
@@ -100,14 +115,17 @@ struct signature_v2_hdr {
/* integrity data associated with an inode */
struct integrity_iint_cache {
struct rb_node rb_node; /* rooted in integrity_iint_tree */
+ struct mutex mutex; /* protects: version, flags, digest */
struct inode *inode; /* back pointer to inode in question */
u64 version; /* track inode changes */
unsigned long flags;
unsigned long measured_pcrs;
+ unsigned long atomic_flags;
enum integrity_status ima_file_status:4;
enum integrity_status ima_mmap_status:4;
enum integrity_status ima_bprm_status:4;
enum integrity_status ima_read_status:4;
+ enum integrity_status ima_creds_status:4;
enum integrity_status evm_status:4;
struct ima_digest_data *ima_hash;
};
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 929e14978c42..933623784ccd 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -18,10 +18,18 @@
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/random.h>
+#include <linux/vmalloc.h>
#include <keys/user-type.h>
#include <keys/big_key-type.h>
#include <crypto/aead.h>
+struct big_key_buf {
+ unsigned int nr_pages;
+ void *virt;
+ struct scatterlist *sg;
+ struct page *pages[];
+};
+
/*
* Layout of key payload words.
*/
@@ -91,10 +99,9 @@ static DEFINE_MUTEX(big_key_aead_lock);
/*
* Encrypt/decrypt big_key data
*/
-static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
+static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t datalen, u8 *key)
{
int ret;
- struct scatterlist sgio;
struct aead_request *aead_req;
/* We always use a zero nonce. The reason we can get away with this is
* because we're using a different randomly generated key for every
@@ -109,8 +116,7 @@ static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
return -ENOMEM;
memset(zero_nonce, 0, sizeof(zero_nonce));
- sg_init_one(&sgio, data, datalen + (op == BIG_KEY_ENC ? ENC_AUTHTAG_SIZE : 0));
- aead_request_set_crypt(aead_req, &sgio, &sgio, datalen, zero_nonce);
+ aead_request_set_crypt(aead_req, buf->sg, buf->sg, datalen, zero_nonce);
aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
aead_request_set_ad(aead_req, 0);
@@ -130,21 +136,81 @@ error:
}
/*
+ * Free up the buffer.
+ */
+static void big_key_free_buffer(struct big_key_buf *buf)
+{
+ unsigned int i;
+
+ if (buf->virt) {
+ memset(buf->virt, 0, buf->nr_pages * PAGE_SIZE);
+ vunmap(buf->virt);
+ }
+
+ for (i = 0; i < buf->nr_pages; i++)
+ if (buf->pages[i])
+ __free_page(buf->pages[i]);
+
+ kfree(buf);
+}
+
+/*
+ * Allocate a buffer consisting of a set of pages with a virtual mapping
+ * applied over them.
+ */
+static void *big_key_alloc_buffer(size_t len)
+{
+ struct big_key_buf *buf;
+ unsigned int npg = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ unsigned int i, l;
+
+ buf = kzalloc(sizeof(struct big_key_buf) +
+ sizeof(struct page) * npg +
+ sizeof(struct scatterlist) * npg,
+ GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->nr_pages = npg;
+ buf->sg = (void *)(buf->pages + npg);
+ sg_init_table(buf->sg, npg);
+
+ for (i = 0; i < buf->nr_pages; i++) {
+ buf->pages[i] = alloc_page(GFP_KERNEL);
+ if (!buf->pages[i])
+ goto nomem;
+
+ l = min_t(size_t, len, PAGE_SIZE);
+ sg_set_page(&buf->sg[i], buf->pages[i], l, 0);
+ len -= l;
+ }
+
+ buf->virt = vmap(buf->pages, buf->nr_pages, VM_MAP, PAGE_KERNEL);
+ if (!buf->virt)
+ goto nomem;
+
+ return buf;
+
+nomem:
+ big_key_free_buffer(buf);
+ return NULL;
+}
+
+/*
* Preparse a big key
*/
int big_key_preparse(struct key_preparsed_payload *prep)
{
+ struct big_key_buf *buf;
struct path *path = (struct path *)&prep->payload.data[big_key_path];
struct file *file;
u8 *enckey;
- u8 *data = NULL;
ssize_t written;
- size_t datalen = prep->datalen;
+ size_t datalen = prep->datalen, enclen = datalen + ENC_AUTHTAG_SIZE;
int ret;
- ret = -EINVAL;
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
- goto error;
+ return -EINVAL;
/* Set an arbitrary quota */
prep->quotalen = 16;
@@ -157,13 +223,12 @@ int big_key_preparse(struct key_preparsed_payload *prep)
*
* File content is stored encrypted with randomly generated key.
*/
- size_t enclen = datalen + ENC_AUTHTAG_SIZE;
loff_t pos = 0;
- data = kmalloc(enclen, GFP_KERNEL);
- if (!data)
+ buf = big_key_alloc_buffer(enclen);
+ if (!buf)
return -ENOMEM;
- memcpy(data, prep->data, datalen);
+ memcpy(buf->virt, prep->data, datalen);
/* generate random key */
enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
@@ -176,7 +241,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
goto err_enckey;
/* encrypt aligned data */
- ret = big_key_crypt(BIG_KEY_ENC, data, datalen, enckey);
+ ret = big_key_crypt(BIG_KEY_ENC, buf, datalen, enckey);
if (ret)
goto err_enckey;
@@ -187,7 +252,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
goto err_enckey;
}
- written = kernel_write(file, data, enclen, &pos);
+ written = kernel_write(file, buf->virt, enclen, &pos);
if (written != enclen) {
ret = written;
if (written >= 0)
@@ -202,7 +267,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
*path = file->f_path;
path_get(path);
fput(file);
- kzfree(data);
+ big_key_free_buffer(buf);
} else {
/* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL);
@@ -220,7 +285,7 @@ err_fput:
err_enckey:
kzfree(enckey);
error:
- kzfree(data);
+ big_key_free_buffer(buf);
return ret;
}
@@ -298,15 +363,15 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
return datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) {
+ struct big_key_buf *buf;
struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file;
- u8 *data;
u8 *enckey = (u8 *)key->payload.data[big_key_data];
size_t enclen = datalen + ENC_AUTHTAG_SIZE;
loff_t pos = 0;
- data = kmalloc(enclen, GFP_KERNEL);
- if (!data)
+ buf = big_key_alloc_buffer(enclen);
+ if (!buf)
return -ENOMEM;
file = dentry_open(path, O_RDONLY, current_cred());
@@ -316,26 +381,26 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
}
/* read file to kernel and decrypt */
- ret = kernel_read(file, data, enclen, &pos);
+ ret = kernel_read(file, buf->virt, enclen, &pos);
if (ret >= 0 && ret != enclen) {
ret = -EIO;
goto err_fput;
}
- ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey);
+ ret = big_key_crypt(BIG_KEY_DEC, buf, enclen, enckey);
if (ret)
goto err_fput;
ret = datalen;
/* copy decrypted data to user */
- if (copy_to_user(buffer, data, datalen) != 0)
+ if (copy_to_user(buffer, buf->virt, datalen) != 0)
ret = -EFAULT;
err_fput:
fput(file);
error:
- kzfree(data);
+ big_key_free_buffer(buf);
} else {
ret = datalen;
if (copy_to_user(buffer, key->payload.data[big_key_data],
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index d0bccebbd3b5..41bcf57e96f2 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -713,7 +713,6 @@ descend_to_keyring:
* doesn't contain any keyring pointers.
*/
shortcut = assoc_array_ptr_to_shortcut(ptr);
- smp_read_barrier_depends();
if ((shortcut->index_key[0] & ASSOC_ARRAY_FAN_MASK) != 0)
goto not_this_keyring;
@@ -723,8 +722,6 @@ descend_to_keyring:
}
node = assoc_array_ptr_to_node(ptr);
- smp_read_barrier_depends();
-
ptr = node->slots[0];
if (!assoc_array_ptr_is_meta(ptr))
goto begin_node;
@@ -736,7 +733,6 @@ descend_to_node:
kdebug("descend");
if (assoc_array_ptr_is_shortcut(ptr)) {
shortcut = assoc_array_ptr_to_shortcut(ptr);
- smp_read_barrier_depends();
ptr = READ_ONCE(shortcut->next_node);
BUG_ON(!assoc_array_ptr_is_node(ptr));
}
@@ -744,7 +740,6 @@ descend_to_node:
begin_node:
kdebug("begin_node");
- smp_read_barrier_depends();
slot = 0;
ascend_to_node:
/* Go through the slots in a node */
@@ -792,14 +787,12 @@ ascend_to_node:
if (ptr && assoc_array_ptr_is_shortcut(ptr)) {
shortcut = assoc_array_ptr_to_shortcut(ptr);
- smp_read_barrier_depends();
ptr = READ_ONCE(shortcut->back_pointer);
slot = shortcut->parent_slot;
}
if (!ptr)
goto not_this_keyring;
node = assoc_array_ptr_to_node(ptr);
- smp_read_barrier_depends();
slot++;
/* If we've ascended to the root (zero backpointer), we must have just
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 98aa89ff7bfd..423776682025 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -355,13 +355,12 @@ out:
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
-static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
- size_t buflen)
+static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
{
int rc;
dump_tpm_buf(cmd);
- rc = tpm_send(chip_num, cmd, buflen);
+ rc = tpm_send(NULL, cmd, buflen);
dump_tpm_buf(cmd);
if (rc > 0)
/* Can't return positive return codes values to keyctl */
@@ -382,10 +381,10 @@ static int pcrlock(const int pcrnum)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE);
+ ret = tpm_get_random(NULL, hash, SHA1_DIGEST_SIZE);
if (ret != SHA1_DIGEST_SIZE)
return ret;
- return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
+ return tpm_pcr_extend(NULL, pcrnum, hash) ? -EINVAL : 0;
}
/*
@@ -398,7 +397,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
unsigned char ononce[TPM_NONCE_SIZE];
int ret;
- ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE);
+ ret = tpm_get_random(NULL, ononce, TPM_NONCE_SIZE);
if (ret != TPM_NONCE_SIZE)
return ret;
@@ -410,7 +409,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
store32(tb, handle);
storebytes(tb, ononce, TPM_NONCE_SIZE);
- ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
if (ret < 0)
return ret;
@@ -434,7 +433,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_OIAP_SIZE);
store32(tb, TPM_ORD_OIAP);
- ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
if (ret < 0)
return ret;
@@ -493,7 +492,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
if (ret < 0)
goto out;
- ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE);
+ ret = tpm_get_random(NULL, td->nonceodd, TPM_NONCE_SIZE);
if (ret != TPM_NONCE_SIZE)
goto out;
ordinal = htonl(TPM_ORD_SEAL);
@@ -542,7 +541,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
store8(tb, cont);
storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE);
- ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
if (ret < 0)
goto out;
@@ -603,7 +602,7 @@ static int tpm_unseal(struct tpm_buf *tb,
ordinal = htonl(TPM_ORD_UNSEAL);
keyhndl = htonl(SRKHANDLE);
- ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE);
+ ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
if (ret != TPM_NONCE_SIZE) {
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
return ret;
@@ -635,7 +634,7 @@ static int tpm_unseal(struct tpm_buf *tb,
store8(tb, cont);
storebytes(tb, authdata2, SHA1_DIGEST_SIZE);
- ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
if (ret < 0) {
pr_info("trusted_key: authhmac failed (%d)\n", ret);
return ret;
@@ -748,7 +747,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
int i;
int tpm2;
- tpm2 = tpm_is_tpm2(TPM_ANY_NUM);
+ tpm2 = tpm_is_tpm2(NULL);
if (tpm2 < 0)
return tpm2;
@@ -917,7 +916,7 @@ static struct trusted_key_options *trusted_options_alloc(void)
struct trusted_key_options *options;
int tpm2;
- tpm2 = tpm_is_tpm2(TPM_ANY_NUM);
+ tpm2 = tpm_is_tpm2(NULL);
if (tpm2 < 0)
return NULL;
@@ -967,7 +966,7 @@ static int trusted_instantiate(struct key *key,
size_t key_len;
int tpm2;
- tpm2 = tpm_is_tpm2(TPM_ANY_NUM);
+ tpm2 = tpm_is_tpm2(NULL);
if (tpm2 < 0)
return tpm2;
@@ -1008,7 +1007,7 @@ static int trusted_instantiate(struct key *key,
switch (key_cmd) {
case Opt_load:
if (tpm2)
- ret = tpm_unseal_trusted(TPM_ANY_NUM, payload, options);
+ ret = tpm_unseal_trusted(NULL, payload, options);
else
ret = key_unseal(payload, options);
dump_payload(payload);
@@ -1018,13 +1017,13 @@ static int trusted_instantiate(struct key *key,
break;
case Opt_new:
key_len = payload->key_len;
- ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len);
+ ret = tpm_get_random(NULL, payload->key, key_len);
if (ret != key_len) {
pr_info("trusted_key: key_create failed (%d)\n", ret);
goto out;
}
if (tpm2)
- ret = tpm_seal_trusted(TPM_ANY_NUM, payload, options);
+ ret = tpm_seal_trusted(NULL, payload, options);
else
ret = key_seal(payload, options);
if (ret < 0)
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index dbe6efde77a0..5fa191252c8f 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -19,7 +19,6 @@
#include <linux/module.h>
#include <linux/fs.h>
-#include <linux/fs_struct.h>
#include <linux/lsm_hooks.h>
#include <linux/mount.h>
#include <linux/path.h>
diff --git a/security/security.c b/security/security.c
index 1cd8526cb0b7..7bc2fde023a7 100644
--- a/security/security.c
+++ b/security/security.c
@@ -30,6 +30,8 @@
#include <linux/string.h>
#include <net/flow.h>
+#include <trace/events/initcall.h>
+
#define MAX_LSM_EVM_XATTR 2
/* Maximum number of letters for an LSM name string */
@@ -45,10 +47,14 @@ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
static void __init do_security_initcalls(void)
{
+ int ret;
initcall_t *call;
call = __security_initcall_start;
+ trace_initcall_level("security");
while (call < __security_initcall_end) {
- (*call) ();
+ trace_initcall_start((*call));
+ ret = (*call) ();
+ trace_initcall_finish((*call), ret);
call++;
}
}
@@ -61,11 +67,11 @@ static void __init do_security_initcalls(void)
int __init security_init(void)
{
int i;
- struct list_head *list = (struct list_head *) &security_hook_heads;
+ struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
- for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct list_head);
+ for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
i++)
- INIT_LIST_HEAD(&list[i]);
+ INIT_HLIST_HEAD(&list[i]);
pr_info("Security Framework initialized\n");
/*
@@ -163,7 +169,7 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
for (i = 0; i < count; i++) {
hooks[i].lsm = lsm;
- list_add_tail_rcu(&hooks[i].list, hooks[i].head);
+ hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
}
if (lsm_append(lsm, &lsm_names) < 0)
panic("%s - Cannot get early memory.\n", __func__);
@@ -201,7 +207,7 @@ EXPORT_SYMBOL(unregister_lsm_notifier);
do { \
struct security_hook_list *P; \
\
- list_for_each_entry(P, &security_hook_heads.FUNC, list) \
+ hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \
P->hook.FUNC(__VA_ARGS__); \
} while (0)
@@ -210,7 +216,7 @@ EXPORT_SYMBOL(unregister_lsm_notifier);
do { \
struct security_hook_list *P; \
\
- list_for_each_entry(P, &security_hook_heads.FUNC, list) { \
+ hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
RC = P->hook.FUNC(__VA_ARGS__); \
if (RC != 0) \
break; \
@@ -317,7 +323,7 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
* agree that it should be set it will. If any module
* thinks it should not be set it won't.
*/
- list_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
+ hlist_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
rc = hp->hook.vm_enough_memory(mm, pages);
if (rc <= 0) {
cap_sys_admin = 0;
@@ -805,7 +811,7 @@ int security_inode_getsecurity(struct inode *inode, const char *name, void **buf
/*
* Only one module will provide an attribute with a given name.
*/
- list_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
+ hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
if (rc != -EOPNOTSUPP)
return rc;
@@ -823,7 +829,7 @@ int security_inode_setsecurity(struct inode *inode, const char *name, const void
/*
* Only one module will provide an attribute with a given name.
*/
- list_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
+ hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
rc = hp->hook.inode_setsecurity(inode, name, value, size,
flags);
if (rc != -EOPNOTSUPP)
@@ -1005,6 +1011,13 @@ void security_transfer_creds(struct cred *new, const struct cred *old)
call_void_hook(cred_transfer, new, old);
}
+void security_cred_getsecid(const struct cred *c, u32 *secid)
+{
+ *secid = 0;
+ call_void_hook(cred_getsecid, c, secid);
+}
+EXPORT_SYMBOL(security_cred_getsecid);
+
int security_kernel_act_as(struct cred *new, u32 secid)
{
return call_int_hook(kernel_act_as, 0, new, secid);
@@ -1114,9 +1127,9 @@ int security_task_movememory(struct task_struct *p)
}
int security_task_kill(struct task_struct *p, struct siginfo *info,
- int sig, u32 secid)
+ int sig, const struct cred *cred)
{
- return call_int_hook(task_kill, 0, p, info, sig, secid);
+ return call_int_hook(task_kill, 0, p, info, sig, cred);
}
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
@@ -1126,7 +1139,7 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
int rc = -ENOSYS;
struct security_hook_list *hp;
- list_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
+ hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != -ENOSYS) {
rc = thisrc;
@@ -1163,84 +1176,84 @@ void security_msg_msg_free(struct msg_msg *msg)
call_void_hook(msg_msg_free_security, msg);
}
-int security_msg_queue_alloc(struct msg_queue *msq)
+int security_msg_queue_alloc(struct kern_ipc_perm *msq)
{
return call_int_hook(msg_queue_alloc_security, 0, msq);
}
-void security_msg_queue_free(struct msg_queue *msq)
+void security_msg_queue_free(struct kern_ipc_perm *msq)
{
call_void_hook(msg_queue_free_security, msq);
}
-int security_msg_queue_associate(struct msg_queue *msq, int msqflg)
+int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
{
return call_int_hook(msg_queue_associate, 0, msq, msqflg);
}
-int security_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+int security_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)
{
return call_int_hook(msg_queue_msgctl, 0, msq, cmd);
}
-int security_msg_queue_msgsnd(struct msg_queue *msq,
+int security_msg_queue_msgsnd(struct kern_ipc_perm *msq,
struct msg_msg *msg, int msqflg)
{
return call_int_hook(msg_queue_msgsnd, 0, msq, msg, msqflg);
}
-int security_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+int security_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg,
struct task_struct *target, long type, int mode)
{
return call_int_hook(msg_queue_msgrcv, 0, msq, msg, target, type, mode);
}
-int security_shm_alloc(struct shmid_kernel *shp)
+int security_shm_alloc(struct kern_ipc_perm *shp)
{
return call_int_hook(shm_alloc_security, 0, shp);
}
-void security_shm_free(struct shmid_kernel *shp)
+void security_shm_free(struct kern_ipc_perm *shp)
{
call_void_hook(shm_free_security, shp);
}
-int security_shm_associate(struct shmid_kernel *shp, int shmflg)
+int security_shm_associate(struct kern_ipc_perm *shp, int shmflg)
{
return call_int_hook(shm_associate, 0, shp, shmflg);
}
-int security_shm_shmctl(struct shmid_kernel *shp, int cmd)
+int security_shm_shmctl(struct kern_ipc_perm *shp, int cmd)
{
return call_int_hook(shm_shmctl, 0, shp, cmd);
}
-int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg)
+int security_shm_shmat(struct kern_ipc_perm *shp, char __user *shmaddr, int shmflg)
{
return call_int_hook(shm_shmat, 0, shp, shmaddr, shmflg);
}
-int security_sem_alloc(struct sem_array *sma)
+int security_sem_alloc(struct kern_ipc_perm *sma)
{
return call_int_hook(sem_alloc_security, 0, sma);
}
-void security_sem_free(struct sem_array *sma)
+void security_sem_free(struct kern_ipc_perm *sma)
{
call_void_hook(sem_free_security, sma);
}
-int security_sem_associate(struct sem_array *sma, int semflg)
+int security_sem_associate(struct kern_ipc_perm *sma, int semflg)
{
return call_int_hook(sem_associate, 0, sma, semflg);
}
-int security_sem_semctl(struct sem_array *sma, int cmd)
+int security_sem_semctl(struct kern_ipc_perm *sma, int cmd)
{
return call_int_hook(sem_semctl, 0, sma, cmd);
}
-int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
+int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
unsigned nsops, int alter)
{
return call_int_hook(sem_semop, 0, sma, sops, nsops, alter);
@@ -1473,6 +1486,7 @@ void security_inet_conn_established(struct sock *sk,
{
call_void_hook(inet_conn_established, sk, skb);
}
+EXPORT_SYMBOL(security_inet_conn_established);
int security_secmark_relabel_packet(u32 secid)
{
@@ -1528,6 +1542,27 @@ int security_tun_dev_open(void *security)
}
EXPORT_SYMBOL(security_tun_dev_open);
+int security_sctp_assoc_request(struct sctp_endpoint *ep, struct sk_buff *skb)
+{
+ return call_int_hook(sctp_assoc_request, 0, ep, skb);
+}
+EXPORT_SYMBOL(security_sctp_assoc_request);
+
+int security_sctp_bind_connect(struct sock *sk, int optname,
+ struct sockaddr *address, int addrlen)
+{
+ return call_int_hook(sctp_bind_connect, 0, sk, optname,
+ address, addrlen);
+}
+EXPORT_SYMBOL(security_sctp_bind_connect);
+
+void security_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
+ struct sock *newsk)
+{
+ call_void_hook(sctp_sk_clone, ep, sk, newsk);
+}
+EXPORT_SYMBOL(security_sctp_sk_clone);
+
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
@@ -1629,7 +1664,7 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
* For speed optimization, we explicitly break the loop rather than
* using the macro
*/
- list_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
+ hlist_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
list) {
rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl);
break;
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 2380b8d72cec..f3aedf077509 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -82,14 +82,42 @@ struct avc_callback_node {
struct avc_callback_node *next;
};
-/* Exported via selinufs */
-unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
-
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
#endif
-static struct avc_cache avc_cache;
+struct selinux_avc {
+ unsigned int avc_cache_threshold;
+ struct avc_cache avc_cache;
+};
+
+static struct selinux_avc selinux_avc;
+
+void selinux_avc_init(struct selinux_avc **avc)
+{
+ int i;
+
+ selinux_avc.avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ INIT_HLIST_HEAD(&selinux_avc.avc_cache.slots[i]);
+ spin_lock_init(&selinux_avc.avc_cache.slots_lock[i]);
+ }
+ atomic_set(&selinux_avc.avc_cache.active_nodes, 0);
+ atomic_set(&selinux_avc.avc_cache.lru_hint, 0);
+ *avc = &selinux_avc;
+}
+
+unsigned int avc_get_cache_threshold(struct selinux_avc *avc)
+{
+ return avc->avc_cache_threshold;
+}
+
+void avc_set_cache_threshold(struct selinux_avc *avc,
+ unsigned int cache_threshold)
+{
+ avc->avc_cache_threshold = cache_threshold;
+}
+
static struct avc_callback_node *avc_callbacks;
static struct kmem_cache *avc_node_cachep;
static struct kmem_cache *avc_xperms_data_cachep;
@@ -143,13 +171,14 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
* @tsid: target security identifier
* @tclass: target security class
*/
-static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
+static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass)
{
int rc;
char *scontext;
u32 scontext_len;
- rc = security_sid_to_context(ssid, &scontext, &scontext_len);
+ rc = security_sid_to_context(state, ssid, &scontext, &scontext_len);
if (rc)
audit_log_format(ab, "ssid=%d", ssid);
else {
@@ -157,7 +186,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
kfree(scontext);
}
- rc = security_sid_to_context(tsid, &scontext, &scontext_len);
+ rc = security_sid_to_context(state, tsid, &scontext, &scontext_len);
if (rc)
audit_log_format(ab, " tsid=%d", tsid);
else {
@@ -176,15 +205,6 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
*/
void __init avc_init(void)
{
- int i;
-
- for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- INIT_HLIST_HEAD(&avc_cache.slots[i]);
- spin_lock_init(&avc_cache.slots_lock[i]);
- }
- atomic_set(&avc_cache.active_nodes, 0);
- atomic_set(&avc_cache.lru_hint, 0);
-
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
0, SLAB_PANIC, NULL);
avc_xperms_cachep = kmem_cache_create("avc_xperms_node",
@@ -199,7 +219,7 @@ void __init avc_init(void)
0, SLAB_PANIC, NULL);
}
-int avc_get_hash_stats(char *page)
+int avc_get_hash_stats(struct selinux_avc *avc, char *page)
{
int i, chain_len, max_chain_len, slots_used;
struct avc_node *node;
@@ -210,7 +230,7 @@ int avc_get_hash_stats(char *page)
slots_used = 0;
max_chain_len = 0;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- head = &avc_cache.slots[i];
+ head = &avc->avc_cache.slots[i];
if (!hlist_empty(head)) {
slots_used++;
chain_len = 0;
@@ -225,7 +245,7 @@ int avc_get_hash_stats(char *page)
return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
"longest chain: %d\n",
- atomic_read(&avc_cache.active_nodes),
+ atomic_read(&avc->avc_cache.active_nodes),
slots_used, AVC_CACHE_SLOTS, max_chain_len);
}
@@ -462,11 +482,12 @@ static inline u32 avc_xperms_audit_required(u32 requested,
return audited;
}
-static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass,
- u32 requested, struct av_decision *avd,
- struct extended_perms_decision *xpd,
- u8 perm, int result,
- struct common_audit_data *ad)
+static inline int avc_xperms_audit(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct av_decision *avd,
+ struct extended_perms_decision *xpd,
+ u8 perm, int result,
+ struct common_audit_data *ad)
{
u32 audited, denied;
@@ -474,7 +495,7 @@ static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass,
requested, avd, xpd, perm, result, &denied);
if (likely(!audited))
return 0;
- return slow_avc_audit(ssid, tsid, tclass, requested,
+ return slow_avc_audit(state, ssid, tsid, tclass, requested,
audited, denied, result, ad, 0);
}
@@ -486,29 +507,30 @@ static void avc_node_free(struct rcu_head *rhead)
avc_cache_stats_incr(frees);
}
-static void avc_node_delete(struct avc_node *node)
+static void avc_node_delete(struct selinux_avc *avc, struct avc_node *node)
{
hlist_del_rcu(&node->list);
call_rcu(&node->rhead, avc_node_free);
- atomic_dec(&avc_cache.active_nodes);
+ atomic_dec(&avc->avc_cache.active_nodes);
}
-static void avc_node_kill(struct avc_node *node)
+static void avc_node_kill(struct selinux_avc *avc, struct avc_node *node)
{
avc_xperms_free(node->ae.xp_node);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
- atomic_dec(&avc_cache.active_nodes);
+ atomic_dec(&avc->avc_cache.active_nodes);
}
-static void avc_node_replace(struct avc_node *new, struct avc_node *old)
+static void avc_node_replace(struct selinux_avc *avc,
+ struct avc_node *new, struct avc_node *old)
{
hlist_replace_rcu(&old->list, &new->list);
call_rcu(&old->rhead, avc_node_free);
- atomic_dec(&avc_cache.active_nodes);
+ atomic_dec(&avc->avc_cache.active_nodes);
}
-static inline int avc_reclaim_node(void)
+static inline int avc_reclaim_node(struct selinux_avc *avc)
{
struct avc_node *node;
int hvalue, try, ecx;
@@ -517,16 +539,17 @@ static inline int avc_reclaim_node(void)
spinlock_t *lock;
for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
- hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
- head = &avc_cache.slots[hvalue];
- lock = &avc_cache.slots_lock[hvalue];
+ hvalue = atomic_inc_return(&avc->avc_cache.lru_hint) &
+ (AVC_CACHE_SLOTS - 1);
+ head = &avc->avc_cache.slots[hvalue];
+ lock = &avc->avc_cache.slots_lock[hvalue];
if (!spin_trylock_irqsave(lock, flags))
continue;
rcu_read_lock();
hlist_for_each_entry(node, head, list) {
- avc_node_delete(node);
+ avc_node_delete(avc, node);
avc_cache_stats_incr(reclaims);
ecx++;
if (ecx >= AVC_CACHE_RECLAIM) {
@@ -542,7 +565,7 @@ out:
return ecx;
}
-static struct avc_node *avc_alloc_node(void)
+static struct avc_node *avc_alloc_node(struct selinux_avc *avc)
{
struct avc_node *node;
@@ -553,8 +576,9 @@ static struct avc_node *avc_alloc_node(void)
INIT_HLIST_NODE(&node->list);
avc_cache_stats_incr(allocations);
- if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
- avc_reclaim_node();
+ if (atomic_inc_return(&avc->avc_cache.active_nodes) >
+ avc->avc_cache_threshold)
+ avc_reclaim_node(avc);
out:
return node;
@@ -568,14 +592,15 @@ static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tcl
memcpy(&node->ae.avd, avd, sizeof(node->ae.avd));
}
-static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
+static inline struct avc_node *avc_search_node(struct selinux_avc *avc,
+ u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node, *ret = NULL;
int hvalue;
struct hlist_head *head;
hvalue = avc_hash(ssid, tsid, tclass);
- head = &avc_cache.slots[hvalue];
+ head = &avc->avc_cache.slots[hvalue];
hlist_for_each_entry_rcu(node, head, list) {
if (ssid == node->ae.ssid &&
tclass == node->ae.tclass &&
@@ -600,12 +625,13 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
* then this function returns the avc_node.
* Otherwise, this function returns NULL.
*/
-static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
+static struct avc_node *avc_lookup(struct selinux_avc *avc,
+ u32 ssid, u32 tsid, u16 tclass)
{
struct avc_node *node;
avc_cache_stats_incr(lookups);
- node = avc_search_node(ssid, tsid, tclass);
+ node = avc_search_node(avc, ssid, tsid, tclass);
if (node)
return node;
@@ -614,7 +640,8 @@ static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
return NULL;
}
-static int avc_latest_notif_update(int seqno, int is_insert)
+static int avc_latest_notif_update(struct selinux_avc *avc,
+ int seqno, int is_insert)
{
int ret = 0;
static DEFINE_SPINLOCK(notif_lock);
@@ -622,14 +649,14 @@ static int avc_latest_notif_update(int seqno, int is_insert)
spin_lock_irqsave(&notif_lock, flag);
if (is_insert) {
- if (seqno < avc_cache.latest_notif) {
+ if (seqno < avc->avc_cache.latest_notif) {
printk(KERN_WARNING "SELinux: avc: seqno %d < latest_notif %d\n",
- seqno, avc_cache.latest_notif);
+ seqno, avc->avc_cache.latest_notif);
ret = -EAGAIN;
}
} else {
- if (seqno > avc_cache.latest_notif)
- avc_cache.latest_notif = seqno;
+ if (seqno > avc->avc_cache.latest_notif)
+ avc->avc_cache.latest_notif = seqno;
}
spin_unlock_irqrestore(&notif_lock, flag);
@@ -654,18 +681,19 @@ static int avc_latest_notif_update(int seqno, int is_insert)
* the access vectors into a cache entry, returns
* avc_node inserted. Otherwise, this function returns NULL.
*/
-static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
- struct av_decision *avd,
- struct avc_xperms_node *xp_node)
+static struct avc_node *avc_insert(struct selinux_avc *avc,
+ u32 ssid, u32 tsid, u16 tclass,
+ struct av_decision *avd,
+ struct avc_xperms_node *xp_node)
{
struct avc_node *pos, *node = NULL;
int hvalue;
unsigned long flag;
- if (avc_latest_notif_update(avd->seqno, 1))
+ if (avc_latest_notif_update(avc, avd->seqno, 1))
goto out;
- node = avc_alloc_node();
+ node = avc_alloc_node(avc);
if (node) {
struct hlist_head *head;
spinlock_t *lock;
@@ -678,15 +706,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
kmem_cache_free(avc_node_cachep, node);
return NULL;
}
- head = &avc_cache.slots[hvalue];
- lock = &avc_cache.slots_lock[hvalue];
+ head = &avc->avc_cache.slots[hvalue];
+ lock = &avc->avc_cache.slots_lock[hvalue];
spin_lock_irqsave(lock, flag);
hlist_for_each_entry(pos, head, list) {
if (pos->ae.ssid == ssid &&
pos->ae.tsid == tsid &&
pos->ae.tclass == tclass) {
- avc_node_replace(node, pos);
+ avc_node_replace(avc, node, pos);
goto found;
}
}
@@ -724,9 +752,10 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
audit_log_format(ab, " ");
- avc_dump_query(ab, ad->selinux_audit_data->ssid,
- ad->selinux_audit_data->tsid,
- ad->selinux_audit_data->tclass);
+ avc_dump_query(ab, ad->selinux_audit_data->state,
+ ad->selinux_audit_data->ssid,
+ ad->selinux_audit_data->tsid,
+ ad->selinux_audit_data->tclass);
if (ad->selinux_audit_data->denied) {
audit_log_format(ab, " permissive=%u",
ad->selinux_audit_data->result ? 0 : 1);
@@ -734,10 +763,11 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
}
/* This is the slow part of avc audit with big stack footprint */
-noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
- u32 requested, u32 audited, u32 denied, int result,
- struct common_audit_data *a,
- unsigned flags)
+noinline int slow_avc_audit(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, u32 audited, u32 denied, int result,
+ struct common_audit_data *a,
+ unsigned int flags)
{
struct common_audit_data stack_data;
struct selinux_audit_data sad;
@@ -765,6 +795,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
sad.audited = audited;
sad.denied = denied;
sad.result = result;
+ sad.state = state;
a->selinux_audit_data = &sad;
@@ -813,10 +844,11 @@ out:
* otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
-static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
- u32 tsid, u16 tclass, u32 seqno,
- struct extended_perms_decision *xpd,
- u32 flags)
+static int avc_update_node(struct selinux_avc *avc,
+ u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
+ u32 tsid, u16 tclass, u32 seqno,
+ struct extended_perms_decision *xpd,
+ u32 flags)
{
int hvalue, rc = 0;
unsigned long flag;
@@ -824,7 +856,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
struct hlist_head *head;
spinlock_t *lock;
- node = avc_alloc_node();
+ node = avc_alloc_node(avc);
if (!node) {
rc = -ENOMEM;
goto out;
@@ -833,8 +865,8 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
/* Lock the target slot */
hvalue = avc_hash(ssid, tsid, tclass);
- head = &avc_cache.slots[hvalue];
- lock = &avc_cache.slots_lock[hvalue];
+ head = &avc->avc_cache.slots[hvalue];
+ lock = &avc->avc_cache.slots_lock[hvalue];
spin_lock_irqsave(lock, flag);
@@ -850,7 +882,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
if (!orig) {
rc = -ENOENT;
- avc_node_kill(node);
+ avc_node_kill(avc, node);
goto out_unlock;
}
@@ -894,7 +926,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
avc_add_xperms_decision(node, xpd);
break;
}
- avc_node_replace(node, orig);
+ avc_node_replace(avc, node, orig);
out_unlock:
spin_unlock_irqrestore(lock, flag);
out:
@@ -904,7 +936,7 @@ out:
/**
* avc_flush - Flush the cache
*/
-static void avc_flush(void)
+static void avc_flush(struct selinux_avc *avc)
{
struct hlist_head *head;
struct avc_node *node;
@@ -913,8 +945,8 @@ static void avc_flush(void)
int i;
for (i = 0; i < AVC_CACHE_SLOTS; i++) {
- head = &avc_cache.slots[i];
- lock = &avc_cache.slots_lock[i];
+ head = &avc->avc_cache.slots[i];
+ lock = &avc->avc_cache.slots_lock[i];
spin_lock_irqsave(lock, flag);
/*
@@ -923,7 +955,7 @@ static void avc_flush(void)
*/
rcu_read_lock();
hlist_for_each_entry(node, head, list)
- avc_node_delete(node);
+ avc_node_delete(avc, node);
rcu_read_unlock();
spin_unlock_irqrestore(lock, flag);
}
@@ -933,12 +965,12 @@ static void avc_flush(void)
* avc_ss_reset - Flush the cache and revalidate migrated permissions.
* @seqno: policy sequence number
*/
-int avc_ss_reset(u32 seqno)
+int avc_ss_reset(struct selinux_avc *avc, u32 seqno)
{
struct avc_callback_node *c;
int rc = 0, tmprc;
- avc_flush();
+ avc_flush(avc);
for (c = avc_callbacks; c; c = c->next) {
if (c->events & AVC_CALLBACK_RESET) {
@@ -950,7 +982,7 @@ int avc_ss_reset(u32 seqno)
}
}
- avc_latest_notif_update(seqno, 0);
+ avc_latest_notif_update(avc, seqno, 0);
return rc;
}
@@ -963,30 +995,34 @@ int avc_ss_reset(u32 seqno)
* Don't inline this, since it's the slow-path and just
* results in a bigger stack frame.
*/
-static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
- u16 tclass, struct av_decision *avd,
- struct avc_xperms_node *xp_node)
+static noinline
+struct avc_node *avc_compute_av(struct selinux_state *state,
+ u32 ssid, u32 tsid,
+ u16 tclass, struct av_decision *avd,
+ struct avc_xperms_node *xp_node)
{
rcu_read_unlock();
INIT_LIST_HEAD(&xp_node->xpd_head);
- security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp);
+ security_compute_av(state, ssid, tsid, tclass, avd, &xp_node->xp);
rcu_read_lock();
- return avc_insert(ssid, tsid, tclass, avd, xp_node);
+ return avc_insert(state->avc, ssid, tsid, tclass, avd, xp_node);
}
-static noinline int avc_denied(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- u8 driver, u8 xperm, unsigned flags,
- struct av_decision *avd)
+static noinline int avc_denied(struct selinux_state *state,
+ u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ u8 driver, u8 xperm, unsigned int flags,
+ struct av_decision *avd)
{
if (flags & AVC_STRICT)
return -EACCES;
- if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
+ if (enforcing_enabled(state) &&
+ !(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES;
- avc_update_node(AVC_CALLBACK_GRANT, requested, driver, xperm, ssid,
- tsid, tclass, avd->seqno, NULL, flags);
+ avc_update_node(state->avc, AVC_CALLBACK_GRANT, requested, driver,
+ xperm, ssid, tsid, tclass, avd->seqno, NULL, flags);
return 0;
}
@@ -997,8 +1033,9 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
* as-is the case with ioctls, then multiple may be chained together and the
* driver field is used to specify which set contains the permission.
*/
-int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
- u8 driver, u8 xperm, struct common_audit_data *ad)
+int avc_has_extended_perms(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ u8 driver, u8 xperm, struct common_audit_data *ad)
{
struct avc_node *node;
struct av_decision avd;
@@ -1017,9 +1054,9 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
rcu_read_lock();
- node = avc_lookup(ssid, tsid, tclass);
+ node = avc_lookup(state->avc, ssid, tsid, tclass);
if (unlikely(!node)) {
- node = avc_compute_av(ssid, tsid, tclass, &avd, xp_node);
+ node = avc_compute_av(state, ssid, tsid, tclass, &avd, xp_node);
} else {
memcpy(&avd, &node->ae.avd, sizeof(avd));
xp_node = node->ae.xp_node;
@@ -1043,11 +1080,12 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
goto decision;
}
rcu_read_unlock();
- security_compute_xperms_decision(ssid, tsid, tclass, driver,
- &local_xpd);
+ security_compute_xperms_decision(state, ssid, tsid, tclass,
+ driver, &local_xpd);
rcu_read_lock();
- avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, xperm,
- ssid, tsid, tclass, avd.seqno, &local_xpd, 0);
+ avc_update_node(state->avc, AVC_CALLBACK_ADD_XPERMS, requested,
+ driver, xperm, ssid, tsid, tclass, avd.seqno,
+ &local_xpd, 0);
} else {
avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
}
@@ -1059,12 +1097,12 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
decision:
denied = requested & ~(avd.allowed);
if (unlikely(denied))
- rc = avc_denied(ssid, tsid, tclass, requested, driver, xperm,
- AVC_EXTENDED_PERMS, &avd);
+ rc = avc_denied(state, ssid, tsid, tclass, requested,
+ driver, xperm, AVC_EXTENDED_PERMS, &avd);
rcu_read_unlock();
- rc2 = avc_xperms_audit(ssid, tsid, tclass, requested,
+ rc2 = avc_xperms_audit(state, ssid, tsid, tclass, requested,
&avd, xpd, xperm, rc, ad);
if (rc2)
return rc2;
@@ -1091,10 +1129,11 @@ decision:
* auditing, e.g. in cases where a lock must be held for the check but
* should be released for the auditing.
*/
-inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- unsigned flags,
- struct av_decision *avd)
+inline int avc_has_perm_noaudit(struct selinux_state *state,
+ u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ unsigned int flags,
+ struct av_decision *avd)
{
struct avc_node *node;
struct avc_xperms_node xp_node;
@@ -1105,15 +1144,16 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
rcu_read_lock();
- node = avc_lookup(ssid, tsid, tclass);
+ node = avc_lookup(state->avc, ssid, tsid, tclass);
if (unlikely(!node))
- node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
+ node = avc_compute_av(state, ssid, tsid, tclass, avd, &xp_node);
else
memcpy(avd, &node->ae.avd, sizeof(*avd));
denied = requested & ~(avd->allowed);
if (unlikely(denied))
- rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd);
+ rc = avc_denied(state, ssid, tsid, tclass, requested, 0, 0,
+ flags, avd);
rcu_read_unlock();
return rc;
@@ -1135,39 +1175,43 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
* permissions are granted, -%EACCES if any permissions are denied, or
* another -errno upon other errors.
*/
-int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
+int avc_has_perm(struct selinux_state *state, u32 ssid, u32 tsid, u16 tclass,
u32 requested, struct common_audit_data *auditdata)
{
struct av_decision avd;
int rc, rc2;
- rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
+ rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0,
+ &avd);
- rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, 0);
+ rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
+ auditdata, 0);
if (rc2)
return rc2;
return rc;
}
-int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
- u32 requested, struct common_audit_data *auditdata,
+int avc_has_perm_flags(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ struct common_audit_data *auditdata,
int flags)
{
struct av_decision avd;
int rc, rc2;
- rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
+ rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0,
+ &avd);
- rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc,
+ rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
auditdata, flags);
if (rc2)
return rc2;
return rc;
}
-u32 avc_policy_seqno(void)
+u32 avc_policy_seqno(struct selinux_state *state)
{
- return avc_cache.latest_notif;
+ return state->avc->avc_cache.latest_notif;
}
void avc_disable(void)
@@ -1184,7 +1228,7 @@ void avc_disable(void)
* the cache and get that memory back.
*/
if (avc_node_cachep) {
- avc_flush();
+ avc_flush(selinux_state.avc);
/* kmem_cache_destroy(avc_node_cachep); */
}
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8644d864e3c1..4cafe6a19167 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -67,6 +67,8 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/dccp.h>
+#include <linux/sctp.h>
+#include <net/sctp/structs.h>
#include <linux/quota.h>
#include <linux/un.h> /* for Unix socket types */
#include <net/af_unix.h> /* for Unix socket types */
@@ -98,20 +100,24 @@
#include "audit.h"
#include "avc_ss.h"
+struct selinux_state selinux_state;
+
/* SECMARK reference count */
static atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
-int selinux_enforcing;
+static int selinux_enforcing_boot;
static int __init enforcing_setup(char *str)
{
unsigned long enforcing;
if (!kstrtoul(str, 0, &enforcing))
- selinux_enforcing = enforcing ? 1 : 0;
+ selinux_enforcing_boot = enforcing ? 1 : 0;
return 1;
}
__setup("enforcing=", enforcing_setup);
+#else
+#define selinux_enforcing_boot 1
#endif
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
@@ -129,6 +135,19 @@ __setup("selinux=", selinux_enabled_setup);
int selinux_enabled = 1;
#endif
+static unsigned int selinux_checkreqprot_boot =
+ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
+
+static int __init checkreqprot_setup(char *str)
+{
+ unsigned long checkreqprot;
+
+ if (!kstrtoul(str, 0, &checkreqprot))
+ selinux_checkreqprot_boot = checkreqprot ? 1 : 0;
+ return 1;
+}
+__setup("checkreqprot=", checkreqprot_setup);
+
static struct kmem_cache *sel_inode_cache;
static struct kmem_cache *file_security_cache;
@@ -145,7 +164,8 @@ static struct kmem_cache *file_security_cache;
*/
static int selinux_secmark_enabled(void)
{
- return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount));
+ return (selinux_policycap_alwaysnetwork() ||
+ atomic_read(&selinux_secmark_refcount));
}
/**
@@ -160,7 +180,8 @@ static int selinux_secmark_enabled(void)
*/
static int selinux_peerlbl_enabled(void)
{
- return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());
+ return (selinux_policycap_alwaysnetwork() ||
+ netlbl_enabled() || selinux_xfrm_enabled());
}
static int selinux_netcache_avc_callback(u32 event)
@@ -264,7 +285,8 @@ static int __inode_security_revalidate(struct inode *inode,
might_sleep_if(may_sleep);
- if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
+ if (selinux_state.initialized &&
+ isec->initialized != LABEL_INITIALIZED) {
if (!may_sleep)
return -ECHILD;
@@ -446,12 +468,14 @@ static int may_context_mount_sb_relabel(u32 sid,
const struct task_security_struct *tsec = cred->security;
int rc;
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(&selinux_state,
+ tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELFROM, NULL);
if (rc)
return rc;
- rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(&selinux_state,
+ tsec->sid, sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELTO, NULL);
return rc;
}
@@ -462,12 +486,14 @@ static int may_context_mount_inode_relabel(u32 sid,
{
const struct task_security_struct *tsec = cred->security;
int rc;
- rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(&selinux_state,
+ tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELFROM, NULL);
if (rc)
return rc;
- rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ rc = avc_has_perm(&selinux_state,
+ sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE, NULL);
return rc;
}
@@ -486,7 +512,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "rootfs") ||
- (selinux_policycap_cgroupseclabel &&
+ (selinux_policycap_cgroupseclabel() &&
(!strcmp(sb->s_type->name, "cgroup") ||
!strcmp(sb->s_type->name, "cgroup2")));
}
@@ -586,7 +612,7 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
if (!(sbsec->flags & SE_SBINITIALIZED))
return -EINVAL;
- if (!ss_initialized)
+ if (!selinux_state.initialized)
return -EINVAL;
/* make sure we always check enough bits to cover the mask */
@@ -617,21 +643,25 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
i = 0;
if (sbsec->flags & FSCONTEXT_MNT) {
- rc = security_sid_to_context(sbsec->sid, &context, &len);
+ rc = security_sid_to_context(&selinux_state, sbsec->sid,
+ &context, &len);
if (rc)
goto out_free;
opts->mnt_opts[i] = context;
opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
}
if (sbsec->flags & CONTEXT_MNT) {
- rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
+ rc = security_sid_to_context(&selinux_state,
+ sbsec->mntpoint_sid,
+ &context, &len);
if (rc)
goto out_free;
opts->mnt_opts[i] = context;
opts->mnt_opts_flags[i++] = CONTEXT_MNT;
}
if (sbsec->flags & DEFCONTEXT_MNT) {
- rc = security_sid_to_context(sbsec->def_sid, &context, &len);
+ rc = security_sid_to_context(&selinux_state, sbsec->def_sid,
+ &context, &len);
if (rc)
goto out_free;
opts->mnt_opts[i] = context;
@@ -641,7 +671,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
struct dentry *root = sbsec->sb->s_root;
struct inode_security_struct *isec = backing_inode_security(root);
- rc = security_sid_to_context(isec->sid, &context, &len);
+ rc = security_sid_to_context(&selinux_state, isec->sid,
+ &context, &len);
if (rc)
goto out_free;
opts->mnt_opts[i] = context;
@@ -704,7 +735,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
mutex_lock(&sbsec->lock);
- if (!ss_initialized) {
+ if (!selinux_state.initialized) {
if (!num_opts) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
@@ -750,7 +781,9 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (flags[i] == SBLABEL_MNT)
continue;
- rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL);
+ rc = security_context_str_to_sid(&selinux_state,
+ mount_options[i], &sid,
+ GFP_KERNEL);
if (rc) {
printk(KERN_WARNING "SELinux: security_context_str_to_sid"
"(%s) failed for (dev %s, type %s) errno=%d\n",
@@ -826,7 +859,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
* Determine the labeling behavior to use for this
* filesystem type.
*/
- rc = security_fs_use(sb);
+ rc = security_fs_use(&selinux_state, sb);
if (rc) {
printk(KERN_WARNING
"%s: security_fs_use(%s) returned %d\n",
@@ -851,7 +884,9 @@ static int selinux_set_mnt_opts(struct super_block *sb,
}
if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
- rc = security_transition_sid(current_sid(), current_sid(),
+ rc = security_transition_sid(&selinux_state,
+ current_sid(),
+ current_sid(),
SECCLASS_FILE, NULL,
&sbsec->mntpoint_sid);
if (rc)
@@ -987,7 +1022,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
* if the parent was able to be mounted it clearly had no special lsm
* mount options. thus we can safely deal with this superblock later
*/
- if (!ss_initialized)
+ if (!selinux_state.initialized)
return 0;
/*
@@ -1014,7 +1049,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
if (newsbsec->behavior == SECURITY_FS_USE_NATIVE &&
!(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) {
- rc = security_fs_use(newsb);
+ rc = security_fs_use(&selinux_state, newsb);
if (rc)
goto out;
}
@@ -1297,7 +1332,7 @@ static inline int default_protocol_dgram(int protocol)
static inline u16 socket_type_to_security_class(int family, int type, int protocol)
{
- int extsockclass = selinux_policycap_extsockclass;
+ int extsockclass = selinux_policycap_extsockclass();
switch (family) {
case PF_UNIX:
@@ -1471,7 +1506,8 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
path++;
}
}
- rc = security_genfs_sid(sb->s_type->name, path, tclass, sid);
+ rc = security_genfs_sid(&selinux_state, sb->s_type->name,
+ path, tclass, sid);
}
free_page((unsigned long)buffer);
return rc;
@@ -1589,7 +1625,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
sid = sbsec->def_sid;
rc = 0;
} else {
- rc = security_context_to_sid_default(context, rc, &sid,
+ rc = security_context_to_sid_default(&selinux_state,
+ context, rc, &sid,
sbsec->def_sid,
GFP_NOFS);
if (rc) {
@@ -1622,7 +1659,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
sid = sbsec->sid;
/* Try to obtain a transition SID. */
- rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid);
+ rc = security_transition_sid(&selinux_state, task_sid, sid,
+ sclass, NULL, &sid);
if (rc)
goto out;
break;
@@ -1740,9 +1778,11 @@ static int cred_has_capability(const struct cred *cred,
return -EINVAL;
}
- rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
+ rc = avc_has_perm_noaudit(&selinux_state,
+ sid, sid, sclass, av, 0, &avd);
if (audit == SECURITY_CAP_AUDIT) {
- int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
+ int rc2 = avc_audit(&selinux_state,
+ sid, sid, sclass, av, &avd, rc, &ad, 0);
if (rc2)
return rc2;
}
@@ -1768,7 +1808,8 @@ static int inode_has_perm(const struct cred *cred,
sid = cred_sid(cred);
isec = inode->i_security;
- return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
@@ -1841,7 +1882,8 @@ static int file_has_perm(const struct cred *cred,
ad.u.file = file;
if (sid != fsec->sid) {
- rc = avc_has_perm(sid, fsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, fsec->sid,
SECCLASS_FD,
FD__USE,
&ad);
@@ -1883,7 +1925,8 @@ selinux_determine_inode_label(const struct task_security_struct *tsec,
*_new_isid = tsec->create_sid;
} else {
const struct inode_security_struct *dsec = inode_security(dir);
- return security_transition_sid(tsec->sid, dsec->sid, tclass,
+ return security_transition_sid(&selinux_state, tsec->sid,
+ dsec->sid, tclass,
name, _new_isid);
}
@@ -1910,7 +1953,8 @@ static int may_create(struct inode *dir,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(&selinux_state,
+ sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
&ad);
if (rc)
@@ -1921,11 +1965,13 @@ static int may_create(struct inode *dir,
if (rc)
return rc;
- rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
+ rc = avc_has_perm(&selinux_state,
+ sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;
- return avc_has_perm(newsid, sbsec->sid,
+ return avc_has_perm(&selinux_state,
+ newsid, sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE, &ad);
}
@@ -1954,7 +2000,8 @@ static int may_link(struct inode *dir,
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
- rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad);
+ rc = avc_has_perm(&selinux_state,
+ sid, dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
@@ -1974,7 +2021,8 @@ static int may_link(struct inode *dir,
return 0;
}
- rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass, av, &ad);
return rc;
}
@@ -1998,16 +2046,19 @@ static inline int may_rename(struct inode *old_dir,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = old_dentry;
- rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(&selinux_state,
+ sid, old_dsec->sid, SECCLASS_DIR,
DIR__REMOVE_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
- rc = avc_has_perm(sid, old_isec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, old_isec->sid,
old_isec->sclass, FILE__RENAME, &ad);
if (rc)
return rc;
if (old_is_dir && new_dir != old_dir) {
- rc = avc_has_perm(sid, old_isec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, old_isec->sid,
old_isec->sclass, DIR__REPARENT, &ad);
if (rc)
return rc;
@@ -2017,13 +2068,15 @@ static inline int may_rename(struct inode *old_dir,
av = DIR__ADD_NAME | DIR__SEARCH;
if (d_is_positive(new_dentry))
av |= DIR__REMOVE_NAME;
- rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+ rc = avc_has_perm(&selinux_state,
+ sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
if (d_is_positive(new_dentry)) {
new_isec = backing_inode_security(new_dentry);
new_is_dir = d_is_dir(new_dentry);
- rc = avc_has_perm(sid, new_isec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, new_isec->sid,
new_isec->sclass,
(new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
if (rc)
@@ -2043,7 +2096,8 @@ static int superblock_has_perm(const struct cred *cred,
u32 sid = cred_sid(cred);
sbsec = sb->s_security;
- return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
+ return avc_has_perm(&selinux_state,
+ sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
}
/* Convert a Linux mode and permission mask to an access vector. */
@@ -2106,7 +2160,8 @@ static inline u32 open_file_to_av(struct file *file)
u32 av = file_to_av(file);
struct inode *inode = file_inode(file);
- if (selinux_policycap_openperm && inode->i_sb->s_magic != SOCKFS_MAGIC)
+ if (selinux_policycap_openperm() &&
+ inode->i_sb->s_magic != SOCKFS_MAGIC)
av |= FILE__OPEN;
return av;
@@ -2119,7 +2174,8 @@ static int selinux_binder_set_context_mgr(struct task_struct *mgr)
u32 mysid = current_sid();
u32 mgrsid = task_sid(mgr);
- return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER,
+ return avc_has_perm(&selinux_state,
+ mysid, mgrsid, SECCLASS_BINDER,
BINDER__SET_CONTEXT_MGR, NULL);
}
@@ -2132,13 +2188,15 @@ static int selinux_binder_transaction(struct task_struct *from,
int rc;
if (mysid != fromsid) {
- rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER,
+ rc = avc_has_perm(&selinux_state,
+ mysid, fromsid, SECCLASS_BINDER,
BINDER__IMPERSONATE, NULL);
if (rc)
return rc;
}
- return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL,
+ return avc_has_perm(&selinux_state,
+ fromsid, tosid, SECCLASS_BINDER, BINDER__CALL,
NULL);
}
@@ -2148,7 +2206,8 @@ static int selinux_binder_transfer_binder(struct task_struct *from,
u32 fromsid = task_sid(from);
u32 tosid = task_sid(to);
- return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER,
+ return avc_has_perm(&selinux_state,
+ fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER,
NULL);
}
@@ -2167,7 +2226,8 @@ static int selinux_binder_transfer_file(struct task_struct *from,
ad.u.path = file->f_path;
if (sid != fsec->sid) {
- rc = avc_has_perm(sid, fsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, fsec->sid,
SECCLASS_FD,
FD__USE,
&ad);
@@ -2185,7 +2245,8 @@ static int selinux_binder_transfer_file(struct task_struct *from,
return 0;
isec = backing_inode_security(dentry);
- return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass, file_to_av(file),
&ad);
}
@@ -2196,21 +2257,25 @@ static int selinux_ptrace_access_check(struct task_struct *child,
u32 csid = task_sid(child);
if (mode & PTRACE_MODE_READ)
- return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
+ return avc_has_perm(&selinux_state,
+ sid, csid, SECCLASS_FILE, FILE__READ, NULL);
- return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL);
+ return avc_has_perm(&selinux_state,
+ sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL);
}
static int selinux_ptrace_traceme(struct task_struct *parent)
{
- return avc_has_perm(task_sid(parent), current_sid(), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ task_sid(parent), current_sid(), SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- return avc_has_perm(current_sid(), task_sid(target), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(target), SECCLASS_PROCESS,
PROCESS__GETCAP, NULL);
}
@@ -2219,7 +2284,8 @@ static int selinux_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
- return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ cred_sid(old), cred_sid(new), SECCLASS_PROCESS,
PROCESS__SETCAP, NULL);
}
@@ -2279,18 +2345,21 @@ static int selinux_syslog(int type)
switch (type) {
case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */
case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */
- return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL);
case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */
/* Set level of messages printed to console */
case SYSLOG_ACTION_CONSOLE_LEVEL:
- return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE,
NULL);
}
/* All other syslog types */
- return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL);
}
@@ -2351,13 +2420,14 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
* policy allows the corresponding permission between
* the old and new contexts.
*/
- if (selinux_policycap_nnp_nosuid_transition) {
+ if (selinux_policycap_nnp_nosuid_transition()) {
av = 0;
if (nnp)
av |= PROCESS2__NNP_TRANSITION;
if (nosuid)
av |= PROCESS2__NOSUID_TRANSITION;
- rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS2, av, NULL);
if (!rc)
return 0;
@@ -2368,7 +2438,8 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
* i.e. SIDs that are guaranteed to only be allowed a subset
* of the permissions of the current SID.
*/
- rc = security_bounded_transition(old_tsec->sid, new_tsec->sid);
+ rc = security_bounded_transition(&selinux_state, old_tsec->sid,
+ new_tsec->sid);
if (!rc)
return 0;
@@ -2420,8 +2491,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
return rc;
} else {
/* Check for a default transition on this program. */
- rc = security_transition_sid(old_tsec->sid, isec->sid,
- SECCLASS_PROCESS, NULL,
+ rc = security_transition_sid(&selinux_state, old_tsec->sid,
+ isec->sid, SECCLASS_PROCESS, NULL,
&new_tsec->sid);
if (rc)
return rc;
@@ -2439,25 +2510,29 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
ad.u.file = bprm->file;
if (new_tsec->sid == old_tsec->sid) {
- rc = avc_has_perm(old_tsec->sid, isec->sid,
+ rc = avc_has_perm(&selinux_state,
+ old_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
- rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
- rc = avc_has_perm(new_tsec->sid, isec->sid,
+ rc = avc_has_perm(&selinux_state,
+ new_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
/* Check for shared state */
if (bprm->unsafe & LSM_UNSAFE_SHARE) {
- rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS, PROCESS__SHARE,
NULL);
if (rc)
@@ -2469,7 +2544,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->unsafe & LSM_UNSAFE_PTRACE) {
u32 ptsid = ptrace_parent_sid();
if (ptsid != 0) {
- rc = avc_has_perm(ptsid, new_tsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ ptsid, new_tsec->sid,
SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
if (rc)
@@ -2483,7 +2559,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
/* Enable secure mode for SIDs transitions unless
the noatsecure permission is granted between
the two SIDs, i.e. ahp returns 0. */
- rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS, PROCESS__NOATSECURE,
NULL);
bprm->secureexec |= !!rc;
@@ -2575,7 +2652,8 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
* higher than the default soft limit for cases where the default is
* lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK.
*/
- rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS,
+ rc = avc_has_perm(&selinux_state,
+ new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS,
PROCESS__RLIMITINH, NULL);
if (rc) {
/* protect against do_prlimit() */
@@ -2615,7 +2693,8 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
* This must occur _after_ the task SID has been updated so that any
* kill done after the flush will be checked against the new SID.
*/
- rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
+ rc = avc_has_perm(&selinux_state,
+ osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
if (rc) {
if (IS_ENABLED(CONFIG_POSIX_TIMERS)) {
memset(&itimer, 0, sizeof itimer);
@@ -2779,7 +2858,9 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
if (flags[i] == SBLABEL_MNT)
continue;
- rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL);
+ rc = security_context_str_to_sid(&selinux_state,
+ mount_options[i], &sid,
+ GFP_KERNEL);
if (rc) {
printk(KERN_WARNING "SELinux: security_context_str_to_sid"
"(%s) failed for (dev %s, type %s) errno=%d\n",
@@ -2904,7 +2985,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
if (rc)
return rc;
- return security_sid_to_context(newsid, (char **)ctx, ctxlen);
+ return security_sid_to_context(&selinux_state, newsid, (char **)ctx,
+ ctxlen);
}
static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
@@ -2958,14 +3040,15 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
isec->initialized = LABEL_INITIALIZED;
}
- if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))
+ if (!selinux_state.initialized || !(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;
if (name)
*name = XATTR_SELINUX_SUFFIX;
if (value && len) {
- rc = security_sid_to_context_force(newsid, &context, &clen);
+ rc = security_sid_to_context_force(&selinux_state, newsid,
+ &context, &clen);
if (rc)
return rc;
*value = context;
@@ -3040,7 +3123,8 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
if (IS_ERR(isec))
return PTR_ERR(isec);
- return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
+ return avc_has_perm_flags(&selinux_state,
+ sid, isec->sid, isec->sclass, FILE__READ, &ad,
rcu ? MAY_NOT_BLOCK : 0);
}
@@ -3056,7 +3140,8 @@ static noinline int audit_inode_permission(struct inode *inode,
ad.type = LSM_AUDIT_DATA_INODE;
ad.u.inode = inode;
- rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms,
+ rc = slow_avc_audit(&selinux_state,
+ current_sid(), isec->sid, isec->sclass, perms,
audited, denied, result, &ad, flags);
if (rc)
return rc;
@@ -3094,7 +3179,8 @@ static int selinux_inode_permission(struct inode *inode, int mask)
if (IS_ERR(isec))
return PTR_ERR(isec);
- rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
+ rc = avc_has_perm_noaudit(&selinux_state,
+ sid, isec->sid, isec->sclass, perms, 0, &avd);
audited = avc_audit_required(perms, &avd, rc,
from_access ? FILE__AUDIT_ACCESS : 0,
&denied);
@@ -3126,7 +3212,7 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, dentry, FILE__SETATTR);
- if (selinux_policycap_openperm &&
+ if (selinux_policycap_openperm() &&
inode->i_sb->s_magic != SOCKFS_MAGIC &&
(ia_valid & ATTR_SIZE) &&
!(ia_valid & ATTR_FILE))
@@ -3183,12 +3269,14 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
ad.u.dentry = dentry;
isec = backing_inode_security(dentry);
- rc = avc_has_perm(sid, isec->sid, isec->sclass,
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
return rc;
- rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);
+ rc = security_context_to_sid(&selinux_state, value, size, &newsid,
+ GFP_KERNEL);
if (rc == -EINVAL) {
if (!has_cap_mac_admin(true)) {
struct audit_buffer *ab;
@@ -3213,22 +3301,25 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
return rc;
}
- rc = security_context_to_sid_force(value, size, &newsid);
+ rc = security_context_to_sid_force(&selinux_state, value,
+ size, &newsid);
}
if (rc)
return rc;
- rc = avc_has_perm(sid, newsid, isec->sclass,
+ rc = avc_has_perm(&selinux_state,
+ sid, newsid, isec->sclass,
FILE__RELABELTO, &ad);
if (rc)
return rc;
- rc = security_validate_transition(isec->sid, newsid, sid,
- isec->sclass);
+ rc = security_validate_transition(&selinux_state, isec->sid, newsid,
+ sid, isec->sclass);
if (rc)
return rc;
- return avc_has_perm(newsid,
+ return avc_has_perm(&selinux_state,
+ newsid,
sbsec->sid,
SECCLASS_FILESYSTEM,
FILESYSTEM__ASSOCIATE,
@@ -3249,7 +3340,8 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
return;
}
- rc = security_context_to_sid_force(value, size, &newsid);
+ rc = security_context_to_sid_force(&selinux_state, value, size,
+ &newsid);
if (rc) {
printk(KERN_ERR "SELinux: unable to map context to SID"
"for (%s, %lu), rc=%d\n",
@@ -3324,10 +3416,12 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
*/
isec = inode_security(inode);
if (has_cap_mac_admin(false))
- error = security_sid_to_context_force(isec->sid, &context,
+ error = security_sid_to_context_force(&selinux_state,
+ isec->sid, &context,
&size);
else
- error = security_sid_to_context(isec->sid, &context, &size);
+ error = security_sid_to_context(&selinux_state, isec->sid,
+ &context, &size);
if (error)
return error;
error = size;
@@ -3353,7 +3447,8 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
if (!value || !size)
return -EACCES;
- rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);
+ rc = security_context_to_sid(&selinux_state, value, size, &newsid,
+ GFP_KERNEL);
if (rc)
return rc;
@@ -3442,7 +3537,7 @@ static int selinux_file_permission(struct file *file, int mask)
isec = inode_security(inode);
if (sid == fsec->sid && fsec->isid == isec->sid &&
- fsec->pseqno == avc_policy_seqno())
+ fsec->pseqno == avc_policy_seqno(&selinux_state))
/* No change since file_open check. */
return 0;
@@ -3482,7 +3577,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
ad.u.op->path = file->f_path;
if (ssid != fsec->sid) {
- rc = avc_has_perm(ssid, fsec->sid,
+ rc = avc_has_perm(&selinux_state,
+ ssid, fsec->sid,
SECCLASS_FD,
FD__USE,
&ad);
@@ -3494,8 +3590,9 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
return 0;
isec = inode_security(inode);
- rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
- requested, driver, xperm, &ad);
+ rc = avc_has_extended_perms(&selinux_state,
+ ssid, isec->sid, isec->sclass,
+ requested, driver, xperm, &ad);
out:
return rc;
}
@@ -3563,7 +3660,8 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
* private file mapping that will also be writable.
* This has an additional check.
*/
- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ rc = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_PROCESS,
PROCESS__EXECMEM, NULL);
if (rc)
goto error;
@@ -3593,7 +3691,8 @@ static int selinux_mmap_addr(unsigned long addr)
if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
u32 sid = current_sid();
- rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
+ rc = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_MEMPROTECT,
MEMPROTECT__MMAP_ZERO, NULL);
}
@@ -3615,7 +3714,7 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot,
return rc;
}
- if (selinux_checkreqprot)
+ if (selinux_state.checkreqprot)
prot = reqprot;
return file_map_prot_check(file, prot,
@@ -3629,7 +3728,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
- if (selinux_checkreqprot)
+ if (selinux_state.checkreqprot)
prot = reqprot;
if (default_noexec &&
@@ -3637,13 +3736,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ rc = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
} else if (!vma->vm_file &&
((vma->vm_start <= vma->vm_mm->start_stack &&
vma->vm_end >= vma->vm_mm->start_stack) ||
vma_is_stack_for_current(vma))) {
- rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
+ rc = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_PROCESS,
PROCESS__EXECSTACK, NULL);
} else if (vma->vm_file && vma->anon_vma) {
/*
@@ -3735,7 +3836,8 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
else
perm = signal_to_av(signum);
- return avc_has_perm(fsec->fown_sid, sid,
+ return avc_has_perm(&selinux_state,
+ fsec->fown_sid, sid,
SECCLASS_PROCESS, perm, NULL);
}
@@ -3761,7 +3863,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
* struct as its SID.
*/
fsec->isid = isec->sid;
- fsec->pseqno = avc_policy_seqno();
+ fsec->pseqno = avc_policy_seqno(&selinux_state);
/*
* Since the inode label or policy seqno may have changed
* between the selinux_inode_permission check and the saving
@@ -3780,7 +3882,8 @@ static int selinux_task_alloc(struct task_struct *task,
{
u32 sid = current_sid();
- return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
+ return avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL);
}
/*
@@ -3844,6 +3947,11 @@ static void selinux_cred_transfer(struct cred *new, const struct cred *old)
*tsec = *old_tsec;
}
+static void selinux_cred_getsecid(const struct cred *c, u32 *secid)
+{
+ *secid = cred_sid(c);
+}
+
/*
* set the security data for a kernel service
* - all the creation contexts are set to unlabelled
@@ -3854,7 +3962,8 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid)
u32 sid = current_sid();
int ret;
- ret = avc_has_perm(sid, secid,
+ ret = avc_has_perm(&selinux_state,
+ sid, secid,
SECCLASS_KERNEL_SERVICE,
KERNEL_SERVICE__USE_AS_OVERRIDE,
NULL);
@@ -3878,7 +3987,8 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
u32 sid = current_sid();
int ret;
- ret = avc_has_perm(sid, isec->sid,
+ ret = avc_has_perm(&selinux_state,
+ sid, isec->sid,
SECCLASS_KERNEL_SERVICE,
KERNEL_SERVICE__CREATE_FILES_AS,
NULL);
@@ -3895,7 +4005,8 @@ static int selinux_kernel_module_request(char *kmod_name)
ad.type = LSM_AUDIT_DATA_KMOD;
ad.u.kmod_name = kmod_name;
- return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM,
SYSTEM__MODULE_REQUEST, &ad);
}
@@ -3909,7 +4020,8 @@ static int selinux_kernel_module_from_file(struct file *file)
/* init_module */
if (file == NULL)
- return avc_has_perm(sid, sid, SECCLASS_SYSTEM,
+ return avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_SYSTEM,
SYSTEM__MODULE_LOAD, NULL);
/* finit_module */
@@ -3919,13 +4031,15 @@ static int selinux_kernel_module_from_file(struct file *file)
fsec = file->f_security;
if (sid != fsec->sid) {
- rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
+ rc = avc_has_perm(&selinux_state,
+ sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
if (rc)
return rc;
}
isec = inode_security(file_inode(file));
- return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_SYSTEM,
SYSTEM__MODULE_LOAD, &ad);
}
@@ -3947,19 +4061,22 @@ static int selinux_kernel_read_file(struct file *file,
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETPGID, NULL);
}
static int selinux_task_getpgid(struct task_struct *p)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETPGID, NULL);
}
static int selinux_task_getsid(struct task_struct *p)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETSESSION, NULL);
}
@@ -3970,19 +4087,22 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
static int selinux_task_setnice(struct task_struct *p, int nice)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
}
static int selinux_task_setioprio(struct task_struct *p, int ioprio)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
}
static int selinux_task_getioprio(struct task_struct *p)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETSCHED, NULL);
}
@@ -3997,7 +4117,8 @@ static int selinux_task_prlimit(const struct cred *cred, const struct cred *tcre
av |= PROCESS__SETRLIMIT;
if (flags & LSM_PRLIMIT_READ)
av |= PROCESS__GETRLIMIT;
- return avc_has_perm(cred_sid(cred), cred_sid(tcred),
+ return avc_has_perm(&selinux_state,
+ cred_sid(cred), cred_sid(tcred),
SECCLASS_PROCESS, av, NULL);
}
@@ -4011,7 +4132,8 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
later be used as a safe reset point for the soft limit
upon context transitions. See selinux_bprm_committing_creds. */
if (old_rlim->rlim_max != new_rlim->rlim_max)
- return avc_has_perm(current_sid(), task_sid(p),
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p),
SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL);
return 0;
@@ -4019,34 +4141,41 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
static int selinux_task_setscheduler(struct task_struct *p)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
}
static int selinux_task_getscheduler(struct task_struct *p)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__GETSCHED, NULL);
}
static int selinux_task_movememory(struct task_struct *p)
{
- return avc_has_perm(current_sid(), task_sid(p), SECCLASS_PROCESS,
+ return avc_has_perm(&selinux_state,
+ current_sid(), task_sid(p), SECCLASS_PROCESS,
PROCESS__SETSCHED, NULL);
}
static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
- int sig, u32 secid)
+ int sig, const struct cred *cred)
{
+ u32 secid;
u32 perm;
if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
perm = signal_to_av(sig);
- if (!secid)
+ if (!cred)
secid = current_sid();
- return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL);
+ else
+ secid = cred_sid(cred);
+ return avc_has_perm(&selinux_state,
+ secid, task_sid(p), SECCLASS_PROCESS, perm, NULL);
}
static void selinux_task_to_inode(struct task_struct *p,
@@ -4134,6 +4263,23 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb,
break;
}
+#if IS_ENABLED(CONFIG_IP_SCTP)
+ case IPPROTO_SCTP: {
+ struct sctphdr _sctph, *sh;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ offset += ihlen;
+ sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
+ if (sh == NULL)
+ break;
+
+ ad->u.net->sport = sh->source;
+ ad->u.net->dport = sh->dest;
+ break;
+ }
+#endif
default:
break;
}
@@ -4207,6 +4353,19 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
break;
}
+#if IS_ENABLED(CONFIG_IP_SCTP)
+ case IPPROTO_SCTP: {
+ struct sctphdr _sctph, *sh;
+
+ sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph);
+ if (sh == NULL)
+ break;
+
+ ad->u.net->sport = sh->source;
+ ad->u.net->dport = sh->dest;
+ break;
+ }
+#endif
/* includes fragments */
default:
break;
@@ -4287,7 +4446,8 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
if (unlikely(err))
return -EACCES;
- err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
+ err = security_net_peersid_resolve(&selinux_state, nlbl_sid,
+ nlbl_type, xfrm_sid, sid);
if (unlikely(err)) {
printk(KERN_WARNING
"SELinux: failure in selinux_skb_peerlbl_sid(),"
@@ -4315,7 +4475,8 @@ static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid)
int err = 0;
if (skb_sid != SECSID_NULL)
- err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid);
+ err = security_sid_mls_copy(&selinux_state, sk_sid, skb_sid,
+ conn_sid);
else
*conn_sid = sk_sid;
@@ -4332,8 +4493,8 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
return 0;
}
- return security_transition_sid(tsec->sid, tsec->sid, secclass, NULL,
- socksid);
+ return security_transition_sid(&selinux_state, tsec->sid, tsec->sid,
+ secclass, NULL, socksid);
}
static int sock_has_perm(struct sock *sk, u32 perms)
@@ -4349,7 +4510,8 @@ static int sock_has_perm(struct sock *sk, u32 perms)
ad.u.net = &net;
ad.u.net->sk = sk;
- return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms,
+ return avc_has_perm(&selinux_state,
+ current_sid(), sksec->sid, sksec->sclass, perms,
&ad);
}
@@ -4369,7 +4531,8 @@ static int selinux_socket_create(int family, int type,
if (rc)
return rc;
- return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL);
+ return avc_has_perm(&selinux_state,
+ tsec->sid, newsid, secclass, SOCKET__CREATE, NULL);
}
static int selinux_socket_post_create(struct socket *sock, int family,
@@ -4396,6 +4559,10 @@ static int selinux_socket_post_create(struct socket *sock, int family,
sksec = sock->sk->sk_security;
sksec->sclass = sclass;
sksec->sid = sid;
+ /* Allows detection of the first association on this socket */
+ if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+ sksec->sctp_assoc_state = SCTP_ASSOC_UNSET;
+
err = selinux_netlbl_socket_post_create(sock->sk, family);
}
@@ -4416,11 +4583,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (err)
goto out;
- /*
- * If PF_INET or PF_INET6, check name_bind permission for the port.
- * Multiple address binding for SCTP is not supported yet: we just
- * check the first address now.
- */
+ /* If PF_INET or PF_INET6, check name_bind permission for the port. */
family = sk->sk_family;
if (family == PF_INET || family == PF_INET6) {
char *addrp;
@@ -4432,22 +4595,35 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
unsigned short snum;
u32 sid, node_perm;
- if (family == PF_INET) {
- if (addrlen < sizeof(struct sockaddr_in)) {
- err = -EINVAL;
- goto out;
- }
+ /*
+ * sctp_bindx(3) calls via selinux_sctp_bind_connect()
+ * that validates multiple binding addresses. Because of this
+ * need to check address->sa_family as it is possible to have
+ * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
+ */
+ switch (address->sa_family) {
+ case AF_INET:
+ if (addrlen < sizeof(struct sockaddr_in))
+ return -EINVAL;
addr4 = (struct sockaddr_in *)address;
snum = ntohs(addr4->sin_port);
addrp = (char *)&addr4->sin_addr.s_addr;
- } else {
- if (addrlen < SIN6_LEN_RFC2133) {
- err = -EINVAL;
- goto out;
- }
+ break;
+ case AF_INET6:
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
addr6 = (struct sockaddr_in6 *)address;
snum = ntohs(addr6->sin6_port);
addrp = (char *)&addr6->sin6_addr.s6_addr;
+ break;
+ default:
+ /* Note that SCTP services expect -EINVAL, whereas
+ * others expect -EAFNOSUPPORT.
+ */
+ if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+ return -EINVAL;
+ else
+ return -EAFNOSUPPORT;
}
if (snum) {
@@ -4465,7 +4641,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
ad.u.net = &net;
ad.u.net->sport = htons(snum);
ad.u.net->family = family;
- err = avc_has_perm(sksec->sid, sid,
+ err = avc_has_perm(&selinux_state,
+ sksec->sid, sid,
sksec->sclass,
SOCKET__NAME_BIND, &ad);
if (err)
@@ -4486,6 +4663,10 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
node_perm = DCCP_SOCKET__NODE_BIND;
break;
+ case SECCLASS_SCTP_SOCKET:
+ node_perm = SCTP_SOCKET__NODE_BIND;
+ break;
+
default:
node_perm = RAWIP_SOCKET__NODE_BIND;
break;
@@ -4500,12 +4681,13 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
ad.u.net->sport = htons(snum);
ad.u.net->family = family;
- if (family == PF_INET)
+ if (address->sa_family == AF_INET)
ad.u.net->v4info.saddr = addr4->sin_addr.s_addr;
else
ad.u.net->v6info.saddr = addr6->sin6_addr;
- err = avc_has_perm(sksec->sid, sid,
+ err = avc_has_perm(&selinux_state,
+ sksec->sid, sid,
sksec->sclass, node_perm, &ad);
if (err)
goto out;
@@ -4514,7 +4696,11 @@ out:
return err;
}
-static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
+/* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
+ * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.txt
+ */
+static int selinux_socket_connect_helper(struct socket *sock,
+ struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
struct sk_security_struct *sksec = sk->sk_security;
@@ -4525,10 +4711,12 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
return err;
/*
- * If a TCP or DCCP socket, check name_connect permission for the port.
+ * If a TCP, DCCP or SCTP socket, check name_connect permission
+ * for the port.
*/
if (sksec->sclass == SECCLASS_TCP_SOCKET ||
- sksec->sclass == SECCLASS_DCCP_SOCKET) {
+ sksec->sclass == SECCLASS_DCCP_SOCKET ||
+ sksec->sclass == SECCLASS_SCTP_SOCKET) {
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
struct sockaddr_in *addr4 = NULL;
@@ -4536,38 +4724,75 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
unsigned short snum;
u32 sid, perm;
- if (sk->sk_family == PF_INET) {
+ /* sctp_connectx(3) calls via selinux_sctp_bind_connect()
+ * that validates multiple connect addresses. Because of this
+ * need to check address->sa_family as it is possible to have
+ * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
+ */
+ switch (address->sa_family) {
+ case AF_INET:
addr4 = (struct sockaddr_in *)address;
if (addrlen < sizeof(struct sockaddr_in))
return -EINVAL;
snum = ntohs(addr4->sin_port);
- } else {
+ break;
+ case AF_INET6:
addr6 = (struct sockaddr_in6 *)address;
if (addrlen < SIN6_LEN_RFC2133)
return -EINVAL;
snum = ntohs(addr6->sin6_port);
+ break;
+ default:
+ /* Note that SCTP services expect -EINVAL, whereas
+ * others expect -EAFNOSUPPORT.
+ */
+ if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+ return -EINVAL;
+ else
+ return -EAFNOSUPPORT;
}
err = sel_netport_sid(sk->sk_protocol, snum, &sid);
if (err)
- goto out;
+ return err;
- perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
- TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
+ switch (sksec->sclass) {
+ case SECCLASS_TCP_SOCKET:
+ perm = TCP_SOCKET__NAME_CONNECT;
+ break;
+ case SECCLASS_DCCP_SOCKET:
+ perm = DCCP_SOCKET__NAME_CONNECT;
+ break;
+ case SECCLASS_SCTP_SOCKET:
+ perm = SCTP_SOCKET__NAME_CONNECT;
+ break;
+ }
ad.type = LSM_AUDIT_DATA_NET;
ad.u.net = &net;
ad.u.net->dport = htons(snum);
ad.u.net->family = sk->sk_family;
- err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad);
+ err = avc_has_perm(&selinux_state,
+ sksec->sid, sid, sksec->sclass, perm, &ad);
if (err)
- goto out;
+ return err;
}
- err = selinux_netlbl_socket_connect(sk, address);
+ return 0;
+}
-out:
- return err;
+/* Supports connect(2), see comments in selinux_socket_connect_helper() */
+static int selinux_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ int err;
+ struct sock *sk = sock->sk;
+
+ err = selinux_socket_connect_helper(sock, address, addrlen);
+ if (err)
+ return err;
+
+ return selinux_netlbl_socket_connect(sk, address);
}
static int selinux_socket_listen(struct socket *sock, int backlog)
@@ -4660,7 +4885,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
ad.u.net = &net;
ad.u.net->sk = other;
- err = avc_has_perm(sksec_sock->sid, sksec_other->sid,
+ err = avc_has_perm(&selinux_state,
+ sksec_sock->sid, sksec_other->sid,
sksec_other->sclass,
UNIX_STREAM_SOCKET__CONNECTTO, &ad);
if (err)
@@ -4668,8 +4894,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
/* server child socket */
sksec_new->peer_sid = sksec_sock->sid;
- err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid,
- &sksec_new->sid);
+ err = security_sid_mls_copy(&selinux_state, sksec_other->sid,
+ sksec_sock->sid, &sksec_new->sid);
if (err)
return err;
@@ -4691,7 +4917,8 @@ static int selinux_socket_unix_may_send(struct socket *sock,
ad.u.net = &net;
ad.u.net->sk = other->sk;
- return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO,
+ return avc_has_perm(&selinux_state,
+ ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO,
&ad);
}
@@ -4706,7 +4933,8 @@ static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex,
err = sel_netif_sid(ns, ifindex, &if_sid);
if (err)
return err;
- err = avc_has_perm(peer_sid, if_sid,
+ err = avc_has_perm(&selinux_state,
+ peer_sid, if_sid,
SECCLASS_NETIF, NETIF__INGRESS, ad);
if (err)
return err;
@@ -4714,7 +4942,8 @@ static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex,
err = sel_netnode_sid(addrp, family, &node_sid);
if (err)
return err;
- return avc_has_perm(peer_sid, node_sid,
+ return avc_has_perm(&selinux_state,
+ peer_sid, node_sid,
SECCLASS_NODE, NODE__RECVFROM, ad);
}
@@ -4737,7 +4966,8 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
return err;
if (selinux_secmark_enabled()) {
- err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ err = avc_has_perm(&selinux_state,
+ sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
return err;
@@ -4774,7 +5004,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
* to the selinux_sock_rcv_skb_compat() function to deal with the
* special handling. We do this in an attempt to keep this function
* as fast and as clean as possible. */
- if (!selinux_policycap_netpeer)
+ if (!selinux_policycap_netpeer())
return selinux_sock_rcv_skb_compat(sk, skb, family);
secmark_active = selinux_secmark_enabled();
@@ -4802,7 +5032,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
selinux_netlbl_err(skb, family, err, 0);
return err;
}
- err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
+ err = avc_has_perm(&selinux_state,
+ sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
if (err) {
selinux_netlbl_err(skb, family, err, 0);
@@ -4811,7 +5042,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
}
if (secmark_active) {
- err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ err = avc_has_perm(&selinux_state,
+ sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
return err;
@@ -4830,12 +5062,14 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
u32 peer_sid = SECSID_NULL;
if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
- sksec->sclass == SECCLASS_TCP_SOCKET)
+ sksec->sclass == SECCLASS_TCP_SOCKET ||
+ sksec->sclass == SECCLASS_SCTP_SOCKET)
peer_sid = sksec->peer_sid;
if (peer_sid == SECSID_NULL)
return -ENOPROTOOPT;
- err = security_sid_to_context(peer_sid, &scontext, &scontext_len);
+ err = security_sid_to_context(&selinux_state, peer_sid, &scontext,
+ &scontext_len);
if (err)
return err;
@@ -4943,6 +5177,172 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
sksec->sclass = isec->sclass;
}
+/* Called whenever SCTP receives an INIT chunk. This happens when an incoming
+ * connect(2), sctp_connectx(3) or sctp_sendmsg(3) (with no association
+ * already present).
+ */
+static int selinux_sctp_assoc_request(struct sctp_endpoint *ep,
+ struct sk_buff *skb)
+{
+ struct sk_security_struct *sksec = ep->base.sk->sk_security;
+ struct common_audit_data ad;
+ struct lsm_network_audit net = {0,};
+ u8 peerlbl_active;
+ u32 peer_sid = SECINITSID_UNLABELED;
+ u32 conn_sid;
+ int err = 0;
+
+ if (!selinux_policycap_extsockclass())
+ return 0;
+
+ peerlbl_active = selinux_peerlbl_enabled();
+
+ if (peerlbl_active) {
+ /* This will return peer_sid = SECSID_NULL if there are
+ * no peer labels, see security_net_peersid_resolve().
+ */
+ err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family,
+ &peer_sid);
+ if (err)
+ return err;
+
+ if (peer_sid == SECSID_NULL)
+ peer_sid = SECINITSID_UNLABELED;
+ }
+
+ if (sksec->sctp_assoc_state == SCTP_ASSOC_UNSET) {
+ sksec->sctp_assoc_state = SCTP_ASSOC_SET;
+
+ /* Here as first association on socket. As the peer SID
+ * was allowed by peer recv (and the netif/node checks),
+ * then it is approved by policy and used as the primary
+ * peer SID for getpeercon(3).
+ */
+ sksec->peer_sid = peer_sid;
+ } else if (sksec->peer_sid != peer_sid) {
+ /* Other association peer SIDs are checked to enforce
+ * consistency among the peer SIDs.
+ */
+ ad.type = LSM_AUDIT_DATA_NET;
+ ad.u.net = &net;
+ ad.u.net->sk = ep->base.sk;
+ err = avc_has_perm(&selinux_state,
+ sksec->peer_sid, peer_sid, sksec->sclass,
+ SCTP_SOCKET__ASSOCIATION, &ad);
+ if (err)
+ return err;
+ }
+
+ /* Compute the MLS component for the connection and store
+ * the information in ep. This will be used by SCTP TCP type
+ * sockets and peeled off connections as they cause a new
+ * socket to be generated. selinux_sctp_sk_clone() will then
+ * plug this into the new socket.
+ */
+ err = selinux_conn_sid(sksec->sid, peer_sid, &conn_sid);
+ if (err)
+ return err;
+
+ ep->secid = conn_sid;
+ ep->peer_secid = peer_sid;
+
+ /* Set any NetLabel labels including CIPSO/CALIPSO options. */
+ return selinux_netlbl_sctp_assoc_request(ep, skb);
+}
+
+/* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting
+ * based on their @optname.
+ */
+static int selinux_sctp_bind_connect(struct sock *sk, int optname,
+ struct sockaddr *address,
+ int addrlen)
+{
+ int len, err = 0, walk_size = 0;
+ void *addr_buf;
+ struct sockaddr *addr;
+ struct socket *sock;
+
+ if (!selinux_policycap_extsockclass())
+ return 0;
+
+ /* Process one or more addresses that may be IPv4 or IPv6 */
+ sock = sk->sk_socket;
+ addr_buf = address;
+
+ while (walk_size < addrlen) {
+ addr = addr_buf;
+ switch (addr->sa_family) {
+ case AF_INET:
+ len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ return -EAFNOSUPPORT;
+ }
+
+ err = -EINVAL;
+ switch (optname) {
+ /* Bind checks */
+ case SCTP_PRIMARY_ADDR:
+ case SCTP_SET_PEER_PRIMARY_ADDR:
+ case SCTP_SOCKOPT_BINDX_ADD:
+ err = selinux_socket_bind(sock, addr, len);
+ break;
+ /* Connect checks */
+ case SCTP_SOCKOPT_CONNECTX:
+ case SCTP_PARAM_SET_PRIMARY:
+ case SCTP_PARAM_ADD_IP:
+ case SCTP_SENDMSG_CONNECT:
+ err = selinux_socket_connect_helper(sock, addr, len);
+ if (err)
+ return err;
+
+ /* As selinux_sctp_bind_connect() is called by the
+ * SCTP protocol layer, the socket is already locked,
+ * therefore selinux_netlbl_socket_connect_locked() is
+ * is called here. The situations handled are:
+ * sctp_connectx(3), sctp_sendmsg(3), sendmsg(2),
+ * whenever a new IP address is added or when a new
+ * primary address is selected.
+ * Note that an SCTP connect(2) call happens before
+ * the SCTP protocol layer and is handled via
+ * selinux_socket_connect().
+ */
+ err = selinux_netlbl_socket_connect_locked(sk, addr);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ addr_buf += len;
+ walk_size += len;
+ }
+
+ return 0;
+}
+
+/* Called whenever a new socket is created by accept(2) or sctp_peeloff(3). */
+static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
+ struct sock *newsk)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *newsksec = newsk->sk_security;
+
+ /* If policy does not support SECCLASS_SCTP_SOCKET then call
+ * the non-sctp clone version.
+ */
+ if (!selinux_policycap_extsockclass())
+ return selinux_sk_clone_security(sk, newsk);
+
+ newsksec->sid = ep->secid;
+ newsksec->peer_sid = ep->peer_secid;
+ newsksec->sclass = sksec->sclass;
+ selinux_netlbl_sctp_sk_clone(sk, newsk);
+}
+
static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
@@ -5001,7 +5401,9 @@ static int selinux_secmark_relabel_packet(u32 sid)
__tsec = current_security();
tsid = __tsec->sid;
- return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL);
+ return avc_has_perm(&selinux_state,
+ tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO,
+ NULL);
}
static void selinux_secmark_refcount_inc(void)
@@ -5049,7 +5451,8 @@ static int selinux_tun_dev_create(void)
* connections unlike traditional sockets - check the TUN driver to
* get a better understanding of why this socket is special */
- return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE,
+ return avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE,
NULL);
}
@@ -5057,7 +5460,8 @@ static int selinux_tun_dev_attach_queue(void *security)
{
struct tun_security_struct *tunsec = security;
- return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET,
+ return avc_has_perm(&selinux_state,
+ current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET,
TUN_SOCKET__ATTACH_QUEUE, NULL);
}
@@ -5085,11 +5489,13 @@ static int selinux_tun_dev_open(void *security)
u32 sid = current_sid();
int err;
- err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET,
+ err = avc_has_perm(&selinux_state,
+ sid, tunsec->sid, SECCLASS_TUN_SOCKET,
TUN_SOCKET__RELABELFROM, NULL);
if (err)
return err;
- err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET,
+ err = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_TUN_SOCKET,
TUN_SOCKET__RELABELTO, NULL);
if (err)
return err;
@@ -5120,7 +5526,8 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
sk->sk_protocol, nlh->nlmsg_type,
secclass_map[sksec->sclass - 1].name,
task_pid_nr(current), current->comm);
- if (!selinux_enforcing || security_get_allow_unknown())
+ if (!enforcing_enabled(&selinux_state) ||
+ security_get_allow_unknown(&selinux_state))
err = 0;
}
@@ -5150,7 +5557,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
u8 netlbl_active;
u8 peerlbl_active;
- if (!selinux_policycap_netpeer)
+ if (!selinux_policycap_netpeer())
return NF_ACCEPT;
secmark_active = selinux_secmark_enabled();
@@ -5179,7 +5586,8 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
}
if (secmark_active)
- if (avc_has_perm(peer_sid, skb->secmark,
+ if (avc_has_perm(&selinux_state,
+ peer_sid, skb->secmark,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
@@ -5291,7 +5699,8 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
return NF_DROP;
if (selinux_secmark_enabled())
- if (avc_has_perm(sksec->sid, skb->secmark,
+ if (avc_has_perm(&selinux_state,
+ sksec->sid, skb->secmark,
SECCLASS_PACKET, PACKET__SEND, &ad))
return NF_DROP_ERR(-ECONNREFUSED);
@@ -5319,7 +5728,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
* to the selinux_ip_postroute_compat() function to deal with the
* special handling. We do this in an attempt to keep this function
* as fast and as clean as possible. */
- if (!selinux_policycap_netpeer)
+ if (!selinux_policycap_netpeer())
return selinux_ip_postroute_compat(skb, ifindex, family);
secmark_active = selinux_secmark_enabled();
@@ -5414,7 +5823,8 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
return NF_DROP;
if (secmark_active)
- if (avc_has_perm(peer_sid, skb->secmark,
+ if (avc_has_perm(&selinux_state,
+ peer_sid, skb->secmark,
SECCLASS_PACKET, secmark_perm, &ad))
return NF_DROP_ERR(-ECONNREFUSED);
@@ -5424,13 +5834,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid))
return NF_DROP;
- if (avc_has_perm(peer_sid, if_sid,
+ if (avc_has_perm(&selinux_state,
+ peer_sid, if_sid,
SECCLASS_NETIF, NETIF__EGRESS, &ad))
return NF_DROP_ERR(-ECONNREFUSED);
if (sel_netnode_sid(addrp, family, &node_sid))
return NF_DROP;
- if (avc_has_perm(peer_sid, node_sid,
+ if (avc_has_perm(&selinux_state,
+ peer_sid, node_sid,
SECCLASS_NODE, NODE__SENDTO, &ad))
return NF_DROP_ERR(-ECONNREFUSED);
}
@@ -5518,7 +5930,8 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = ipc_perms->key;
- return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass, perms, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -5532,52 +5945,54 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
}
/* message queue security operations */
-static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
+static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(&msq->q_perm, SECCLASS_MSGQ);
+ rc = ipc_alloc_security(msq, SECCLASS_MSGQ);
if (rc)
return rc;
- isec = msq->q_perm.security;
+ isec = msq->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = msq->q_perm.key;
+ ad.u.ipc_id = msq->key;
- rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_MSGQ,
MSGQ__CREATE, &ad);
if (rc) {
- ipc_free_security(&msq->q_perm);
+ ipc_free_security(msq);
return rc;
}
return 0;
}
-static void selinux_msg_queue_free_security(struct msg_queue *msq)
+static void selinux_msg_queue_free_security(struct kern_ipc_perm *msq)
{
- ipc_free_security(&msq->q_perm);
+ ipc_free_security(msq);
}
-static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
+static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
u32 sid = current_sid();
- isec = msq->q_perm.security;
+ isec = msq->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = msq->q_perm.key;
+ ad.u.ipc_id = msq->key;
- return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_MSGQ,
MSGQ__ASSOCIATE, &ad);
}
-static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+static int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)
{
int err;
int perms;
@@ -5586,10 +6001,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
case IPC_INFO:
case MSG_INFO:
/* No specific object, just general system-wide information. */
- return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case IPC_STAT:
case MSG_STAT:
+ case MSG_STAT_ANY:
perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
break;
case IPC_SET:
@@ -5602,11 +6019,11 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
return 0;
}
- err = ipc_has_perm(&msq->q_perm, perms);
+ err = ipc_has_perm(msq, perms);
return err;
}
-static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
+static int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *msg, int msqflg)
{
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
@@ -5614,7 +6031,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
u32 sid = current_sid();
int rc;
- isec = msq->q_perm.security;
+ isec = msq->security;
msec = msg->security;
/*
@@ -5625,31 +6042,34 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
* Compute new sid based on current process and
* message queue this message will be stored in
*/
- rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
- NULL, &msec->sid);
+ rc = security_transition_sid(&selinux_state, sid, isec->sid,
+ SECCLASS_MSG, NULL, &msec->sid);
if (rc)
return rc;
}
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = msq->q_perm.key;
+ ad.u.ipc_id = msq->key;
/* Can this process write to the queue? */
- rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_MSGQ,
MSGQ__WRITE, &ad);
if (!rc)
/* Can this process send the message */
- rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG,
+ rc = avc_has_perm(&selinux_state,
+ sid, msec->sid, SECCLASS_MSG,
MSG__SEND, &ad);
if (!rc)
/* Can the message be put in the queue? */
- rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(&selinux_state,
+ msec->sid, isec->sid, SECCLASS_MSGQ,
MSGQ__ENQUEUE, &ad);
return rc;
}
-static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+static int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg,
struct task_struct *target,
long type, int mode)
{
@@ -5659,68 +6079,72 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
u32 sid = task_sid(target);
int rc;
- isec = msq->q_perm.security;
+ isec = msq->security;
msec = msg->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = msq->q_perm.key;
+ ad.u.ipc_id = msq->key;
- rc = avc_has_perm(sid, isec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid,
SECCLASS_MSGQ, MSGQ__READ, &ad);
if (!rc)
- rc = avc_has_perm(sid, msec->sid,
+ rc = avc_has_perm(&selinux_state,
+ sid, msec->sid,
SECCLASS_MSG, MSG__RECEIVE, &ad);
return rc;
}
/* Shared Memory security operations */
-static int selinux_shm_alloc_security(struct shmid_kernel *shp)
+static int selinux_shm_alloc_security(struct kern_ipc_perm *shp)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(&shp->shm_perm, SECCLASS_SHM);
+ rc = ipc_alloc_security(shp, SECCLASS_SHM);
if (rc)
return rc;
- isec = shp->shm_perm.security;
+ isec = shp->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = shp->shm_perm.key;
+ ad.u.ipc_id = shp->key;
- rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_SHM,
SHM__CREATE, &ad);
if (rc) {
- ipc_free_security(&shp->shm_perm);
+ ipc_free_security(shp);
return rc;
}
return 0;
}
-static void selinux_shm_free_security(struct shmid_kernel *shp)
+static void selinux_shm_free_security(struct kern_ipc_perm *shp)
{
- ipc_free_security(&shp->shm_perm);
+ ipc_free_security(shp);
}
-static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
+static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
u32 sid = current_sid();
- isec = shp->shm_perm.security;
+ isec = shp->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = shp->shm_perm.key;
+ ad.u.ipc_id = shp->key;
- return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_SHM,
SHM__ASSOCIATE, &ad);
}
/* Note, at this point, shp is locked down */
-static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
+static int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd)
{
int perms;
int err;
@@ -5729,10 +6153,12 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
case IPC_INFO:
case SHM_INFO:
/* No specific object, just general system-wide information. */
- return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case IPC_STAT:
case SHM_STAT:
+ case SHM_STAT_ANY:
perms = SHM__GETATTR | SHM__ASSOCIATE;
break;
case IPC_SET:
@@ -5749,11 +6175,11 @@ static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
return 0;
}
- err = ipc_has_perm(&shp->shm_perm, perms);
+ err = ipc_has_perm(shp, perms);
return err;
}
-static int selinux_shm_shmat(struct shmid_kernel *shp,
+static int selinux_shm_shmat(struct kern_ipc_perm *shp,
char __user *shmaddr, int shmflg)
{
u32 perms;
@@ -5763,57 +6189,59 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
else
perms = SHM__READ | SHM__WRITE;
- return ipc_has_perm(&shp->shm_perm, perms);
+ return ipc_has_perm(shp, perms);
}
/* Semaphore security operations */
-static int selinux_sem_alloc_security(struct sem_array *sma)
+static int selinux_sem_alloc_security(struct kern_ipc_perm *sma)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(&sma->sem_perm, SECCLASS_SEM);
+ rc = ipc_alloc_security(sma, SECCLASS_SEM);
if (rc)
return rc;
- isec = sma->sem_perm.security;
+ isec = sma->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = sma->sem_perm.key;
+ ad.u.ipc_id = sma->key;
- rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
+ rc = avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_SEM,
SEM__CREATE, &ad);
if (rc) {
- ipc_free_security(&sma->sem_perm);
+ ipc_free_security(sma);
return rc;
}
return 0;
}
-static void selinux_sem_free_security(struct sem_array *sma)
+static void selinux_sem_free_security(struct kern_ipc_perm *sma)
{
- ipc_free_security(&sma->sem_perm);
+ ipc_free_security(sma);
}
-static int selinux_sem_associate(struct sem_array *sma, int semflg)
+static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg)
{
struct ipc_security_struct *isec;
struct common_audit_data ad;
u32 sid = current_sid();
- isec = sma->sem_perm.security;
+ isec = sma->security;
ad.type = LSM_AUDIT_DATA_IPC;
- ad.u.ipc_id = sma->sem_perm.key;
+ ad.u.ipc_id = sma->key;
- return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, SECCLASS_SEM,
SEM__ASSOCIATE, &ad);
}
/* Note, at this point, sma is locked down */
-static int selinux_sem_semctl(struct sem_array *sma, int cmd)
+static int selinux_sem_semctl(struct kern_ipc_perm *sma, int cmd)
{
int err;
u32 perms;
@@ -5822,7 +6250,8 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd)
case IPC_INFO:
case SEM_INFO:
/* No specific object, just general system-wide information. */
- return avc_has_perm(current_sid(), SECINITSID_KERNEL,
+ return avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_KERNEL,
SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL);
case GETPID:
case GETNCNT:
@@ -5845,17 +6274,18 @@ static int selinux_sem_semctl(struct sem_array *sma, int cmd)
break;
case IPC_STAT:
case SEM_STAT:
+ case SEM_STAT_ANY:
perms = SEM__GETATTR | SEM__ASSOCIATE;
break;
default:
return 0;
}
- err = ipc_has_perm(&sma->sem_perm, perms);
+ err = ipc_has_perm(sma, perms);
return err;
}
-static int selinux_sem_semop(struct sem_array *sma,
+static int selinux_sem_semop(struct kern_ipc_perm *sma,
struct sembuf *sops, unsigned nsops, int alter)
{
u32 perms;
@@ -5865,7 +6295,7 @@ static int selinux_sem_semop(struct sem_array *sma,
else
perms = SEM__READ;
- return ipc_has_perm(&sma->sem_perm, perms);
+ return ipc_has_perm(sma, perms);
}
static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
@@ -5908,7 +6338,8 @@ static int selinux_getprocattr(struct task_struct *p,
__tsec = __task_cred(p)->security;
if (current != p) {
- error = avc_has_perm(current_sid(), __tsec->sid,
+ error = avc_has_perm(&selinux_state,
+ current_sid(), __tsec->sid,
SECCLASS_PROCESS, PROCESS__GETATTR, NULL);
if (error)
goto bad;
@@ -5935,7 +6366,7 @@ static int selinux_getprocattr(struct task_struct *p,
if (!sid)
return 0;
- error = security_sid_to_context(sid, value, &len);
+ error = security_sid_to_context(&selinux_state, sid, value, &len);
if (error)
return error;
return len;
@@ -5957,19 +6388,24 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
* Basic control over ability to set these attributes at all.
*/
if (!strcmp(name, "exec"))
- error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETEXEC, NULL);
else if (!strcmp(name, "fscreate"))
- error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETFSCREATE, NULL);
else if (!strcmp(name, "keycreate"))
- error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETKEYCREATE, NULL);
else if (!strcmp(name, "sockcreate"))
- error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETSOCKCREATE, NULL);
else if (!strcmp(name, "current"))
- error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ mysid, mysid, SECCLASS_PROCESS,
PROCESS__SETCURRENT, NULL);
else
error = -EINVAL;
@@ -5982,7 +6418,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
str[size-1] = 0;
size--;
}
- error = security_context_to_sid(value, size, &sid, GFP_KERNEL);
+ error = security_context_to_sid(&selinux_state, value, size,
+ &sid, GFP_KERNEL);
if (error == -EINVAL && !strcmp(name, "fscreate")) {
if (!has_cap_mac_admin(true)) {
struct audit_buffer *ab;
@@ -6001,8 +6438,9 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
return error;
}
- error = security_context_to_sid_force(value, size,
- &sid);
+ error = security_context_to_sid_force(
+ &selinux_state,
+ value, size, &sid);
}
if (error)
return error;
@@ -6024,7 +6462,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
} else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid;
} else if (!strcmp(name, "keycreate")) {
- error = avc_has_perm(mysid, sid, SECCLASS_KEY, KEY__CREATE,
+ error = avc_has_perm(&selinux_state,
+ mysid, sid, SECCLASS_KEY, KEY__CREATE,
NULL);
if (error)
goto abort_change;
@@ -6039,13 +6478,15 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
/* Only allow single threaded processes to change context */
error = -EPERM;
if (!current_is_single_threaded()) {
- error = security_bounded_transition(tsec->sid, sid);
+ error = security_bounded_transition(&selinux_state,
+ tsec->sid, sid);
if (error)
goto abort_change;
}
/* Check permissions for the transition. */
- error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__DYNTRANSITION, NULL);
if (error)
goto abort_change;
@@ -6054,7 +6495,8 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
Otherwise, leave SID unchanged and fail. */
ptsid = ptrace_parent_sid();
if (ptsid != 0) {
- error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
+ error = avc_has_perm(&selinux_state,
+ ptsid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
if (error)
goto abort_change;
@@ -6081,12 +6523,14 @@ static int selinux_ismaclabel(const char *name)
static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
- return security_sid_to_context(secid, secdata, seclen);
+ return security_sid_to_context(&selinux_state, secid,
+ secdata, seclen);
}
static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
{
- return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL);
+ return security_context_to_sid(&selinux_state, secdata, seclen,
+ secid, GFP_KERNEL);
}
static void selinux_release_secctx(char *secdata, u32 seclen)
@@ -6178,7 +6622,8 @@ static int selinux_key_permission(key_ref_t key_ref,
key = key_ref_to_ptr(key_ref);
ksec = key->security;
- return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);
+ return avc_has_perm(&selinux_state,
+ sid, ksec->sid, SECCLASS_KEY, perm, NULL);
}
static int selinux_key_getsecurity(struct key *key, char **_buffer)
@@ -6188,7 +6633,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
unsigned len;
int rc;
- rc = security_sid_to_context(ksec->sid, &context, &len);
+ rc = security_sid_to_context(&selinux_state, ksec->sid,
+ &context, &len);
if (!rc)
rc = len;
*_buffer = context;
@@ -6213,7 +6659,8 @@ static int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val)
ibpkey.subnet_prefix = subnet_prefix;
ibpkey.pkey = pkey_val;
ad.u.ibpkey = &ibpkey;
- return avc_has_perm(sec->sid, sid,
+ return avc_has_perm(&selinux_state,
+ sec->sid, sid,
SECCLASS_INFINIBAND_PKEY,
INFINIBAND_PKEY__ACCESS, &ad);
}
@@ -6227,7 +6674,8 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name,
struct ib_security_struct *sec = ib_sec;
struct lsm_ibendport_audit ibendport;
- err = security_ib_endport_sid(dev_name, port_num, &sid);
+ err = security_ib_endport_sid(&selinux_state, dev_name, port_num,
+ &sid);
if (err)
return err;
@@ -6236,7 +6684,8 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name,
strncpy(ibendport.dev_name, dev_name, sizeof(ibendport.dev_name));
ibendport.port = port_num;
ad.u.ibendport = &ibendport;
- return avc_has_perm(sec->sid, sid,
+ return avc_has_perm(&selinux_state,
+ sec->sid, sid,
SECCLASS_INFINIBAND_ENDPORT,
INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad);
}
@@ -6269,11 +6718,13 @@ static int selinux_bpf(int cmd, union bpf_attr *attr,
switch (cmd) {
case BPF_MAP_CREATE:
- ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
+ ret = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
NULL);
break;
case BPF_PROG_LOAD:
- ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD,
+ ret = avc_has_perm(&selinux_state,
+ sid, sid, SECCLASS_BPF, BPF__PROG_LOAD,
NULL);
break;
default:
@@ -6313,14 +6764,16 @@ static int bpf_fd_pass(struct file *file, u32 sid)
if (file->f_op == &bpf_map_fops) {
map = file->private_data;
bpfsec = map->security;
- ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ ret = avc_has_perm(&selinux_state,
+ sid, bpfsec->sid, SECCLASS_BPF,
bpf_map_fmode_to_av(file->f_mode), NULL);
if (ret)
return ret;
} else if (file->f_op == &bpf_prog_fops) {
prog = file->private_data;
bpfsec = prog->aux->security;
- ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ ret = avc_has_perm(&selinux_state,
+ sid, bpfsec->sid, SECCLASS_BPF,
BPF__PROG_RUN, NULL);
if (ret)
return ret;
@@ -6334,7 +6787,8 @@ static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
struct bpf_security_struct *bpfsec;
bpfsec = map->security;
- return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ return avc_has_perm(&selinux_state,
+ sid, bpfsec->sid, SECCLASS_BPF,
bpf_map_fmode_to_av(fmode), NULL);
}
@@ -6344,7 +6798,8 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
struct bpf_security_struct *bpfsec;
bpfsec = prog->aux->security;
- return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ return avc_has_perm(&selinux_state,
+ sid, bpfsec->sid, SECCLASS_BPF,
BPF__PROG_RUN, NULL);
}
@@ -6479,6 +6934,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(cred_free, selinux_cred_free),
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
+ LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
@@ -6563,6 +7019,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security),
LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid),
LSM_HOOK_INIT(sock_graft, selinux_sock_graft),
+ LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request),
+ LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone),
+ LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect),
LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request),
LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone),
LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established),
@@ -6638,6 +7097,12 @@ static __init int selinux_init(void)
printk(KERN_INFO "SELinux: Initializing.\n");
+ memset(&selinux_state, 0, sizeof(selinux_state));
+ enforcing_set(&selinux_state, selinux_enforcing_boot);
+ selinux_state.checkreqprot = selinux_checkreqprot_boot;
+ selinux_ss_init(&selinux_state.ss);
+ selinux_avc_init(&selinux_state.avc);
+
/* Set the security state for the initial task. */
cred_init_security();
@@ -6651,6 +7116,12 @@ static __init int selinux_init(void)
0, SLAB_PANIC, NULL);
avc_init();
+ avtab_cache_init();
+
+ ebitmap_cache_init();
+
+ hashtab_cache_init();
+
security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
@@ -6659,7 +7130,7 @@ static __init int selinux_init(void)
if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC LSM notifier callback\n");
- if (selinux_enforcing)
+ if (selinux_enforcing_boot)
printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n");
else
printk(KERN_DEBUG "SELinux: Starting in permissive mode\n");
@@ -6780,23 +7251,22 @@ static void selinux_nf_ip_exit(void)
#endif /* CONFIG_NETFILTER */
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
-static int selinux_disabled;
-
-int selinux_disable(void)
+int selinux_disable(struct selinux_state *state)
{
- if (ss_initialized) {
+ if (state->initialized) {
/* Not permitted after initial policy load. */
return -EINVAL;
}
- if (selinux_disabled) {
+ if (state->disabled) {
/* Only do this once. */
return -EINVAL;
}
+ state->disabled = 1;
+
printk(KERN_INFO "SELinux: Disabled at runtime.\n");
- selinux_disabled = 1;
selinux_enabled = 0;
security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index e3614ee5f1c0..0a4b89d48297 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -152,7 +152,8 @@ static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
return 0;
}
- ret = security_ib_pkey_sid(subnet_prefix, pkey_num, sid);
+ ret = security_ib_pkey_sid(&selinux_state, subnet_prefix, pkey_num,
+ sid);
if (ret)
goto out;
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 57d61cf36500..ef899bcfd2cb 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -20,12 +20,6 @@
#include "av_permissions.h"
#include "security.h"
-#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
-extern int selinux_enforcing;
-#else
-#define selinux_enforcing 1
-#endif
-
/*
* An entry in the AVC.
*/
@@ -58,6 +52,7 @@ struct selinux_audit_data {
u32 audited;
u32 denied;
int result;
+ struct selinux_state *state;
};
/*
@@ -102,7 +97,8 @@ static inline u32 avc_audit_required(u32 requested,
return audited;
}
-int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
+int slow_avc_audit(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied, int result,
struct common_audit_data *a,
unsigned flags);
@@ -127,7 +123,8 @@ int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
* be performed under a lock, to allow the lock to be released
* before calling the auditing code.
*/
-static inline int avc_audit(u32 ssid, u32 tsid,
+static inline int avc_audit(struct selinux_state *state,
+ u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
@@ -138,31 +135,35 @@ static inline int avc_audit(u32 ssid, u32 tsid,
audited = avc_audit_required(requested, avd, result, 0, &denied);
if (likely(!audited))
return 0;
- return slow_avc_audit(ssid, tsid, tclass,
+ return slow_avc_audit(state, ssid, tsid, tclass,
requested, audited, denied, result,
a, flags);
}
#define AVC_STRICT 1 /* Ignore permissive mode. */
#define AVC_EXTENDED_PERMS 2 /* update extended permissions */
-int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+int avc_has_perm_noaudit(struct selinux_state *state,
+ u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
struct av_decision *avd);
-int avc_has_perm(u32 ssid, u32 tsid,
+int avc_has_perm(struct selinux_state *state,
+ u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct common_audit_data *auditdata);
-int avc_has_perm_flags(u32 ssid, u32 tsid,
+int avc_has_perm_flags(struct selinux_state *state,
+ u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct common_audit_data *auditdata,
int flags);
-int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
- u8 driver, u8 perm, struct common_audit_data *ad);
+int avc_has_extended_perms(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ u8 driver, u8 perm, struct common_audit_data *ad);
-u32 avc_policy_seqno(void);
+u32 avc_policy_seqno(struct selinux_state *state);
#define AVC_CALLBACK_GRANT 1
#define AVC_CALLBACK_TRY_REVOKE 2
@@ -177,8 +178,11 @@ u32 avc_policy_seqno(void);
int avc_add_callback(int (*callback)(u32 event), u32 events);
/* Exported to selinuxfs */
-int avc_get_hash_stats(char *page);
-extern unsigned int avc_cache_threshold;
+struct selinux_avc;
+int avc_get_hash_stats(struct selinux_avc *avc, char *page);
+unsigned int avc_get_cache_threshold(struct selinux_avc *avc);
+void avc_set_cache_threshold(struct selinux_avc *avc,
+ unsigned int cache_threshold);
/* Attempt to free avc node cache */
void avc_disable(void);
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h
index 3bcc72769b87..88c384c5c09e 100644
--- a/security/selinux/include/avc_ss.h
+++ b/security/selinux/include/avc_ss.h
@@ -9,7 +9,8 @@
#include "flask.h"
-int avc_ss_reset(u32 seqno);
+struct selinux_avc;
+int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
/* Class/perm mapping support */
struct security_class_mapping {
@@ -19,11 +20,5 @@ struct security_class_mapping {
extern struct security_class_mapping secclass_map[];
-/*
- * The security server must be initialized before
- * any labeling or access decisions can be provided.
- */
-extern int ss_initialized;
-
#endif /* _SELINUX_AVC_SS_H_ */
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index acdee7795297..7f0372426494 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -176,7 +176,7 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_CAP2_PERMS, NULL } },
{ "sctp_socket",
{ COMMON_SOCK_PERMS,
- "node_bind", NULL } },
+ "node_bind", "name_connect", "association", NULL } },
{ "icmp_socket",
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
index ff4fddca9050..0e30eca02c48 100644
--- a/security/selinux/include/conditional.h
+++ b/security/selinux/include/conditional.h
@@ -13,10 +13,15 @@
#ifndef _SELINUX_CONDITIONAL_H_
#define _SELINUX_CONDITIONAL_H_
-int security_get_bools(int *len, char ***names, int **values);
+#include "security.h"
-int security_set_bools(int len, int *values);
+int security_get_bools(struct selinux_state *state,
+ int *len, char ***names, int **values);
-int security_get_bool_value(int index);
+int security_set_bools(struct selinux_state *state,
+ int len, int *values);
+
+int security_get_bool_value(struct selinux_state *state,
+ int index);
#endif
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 75686d53df07..8671de09c363 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -19,8 +19,7 @@
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -33,6 +32,7 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/request_sock.h>
+#include <net/sctp/structs.h>
#include "avc.h"
#include "objsec.h"
@@ -53,9 +53,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
u16 family,
u32 sid);
-
+int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
+ struct sk_buff *skb);
int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family);
void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family);
+void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk);
int selinux_netlbl_socket_post_create(struct sock *sk, u16 family);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb,
@@ -65,6 +67,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
+int selinux_netlbl_socket_connect_locked(struct sock *sk,
+ struct sockaddr *addr);
#else
static inline void selinux_netlbl_cache_invalidate(void)
@@ -114,6 +118,11 @@ static inline int selinux_netlbl_conn_setsid(struct sock *sk,
return 0;
}
+static inline int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
+ struct sk_buff *skb)
+{
+ return 0;
+}
static inline int selinux_netlbl_inet_conn_request(struct request_sock *req,
u16 family)
{
@@ -123,6 +132,11 @@ static inline void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
{
return;
}
+static inline void selinux_netlbl_sctp_sk_clone(struct sock *sk,
+ struct sock *newsk)
+{
+ return;
+}
static inline int selinux_netlbl_socket_post_create(struct sock *sk,
u16 family)
{
@@ -146,6 +160,11 @@ static inline int selinux_netlbl_socket_connect(struct sock *sk,
{
return 0;
}
+static inline int selinux_netlbl_socket_connect_locked(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
#endif /* CONFIG_NETLABEL */
#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 3d54468ce334..cc5e26b0161b 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -130,6 +130,10 @@ struct sk_security_struct {
u32 sid; /* SID of this object */
u32 peer_sid; /* SID of peer */
u16 sclass; /* sock security class */
+ enum { /* SCTP association state */
+ SCTP_ASSOC_UNSET = 0,
+ SCTP_ASSOC_SET,
+ } sctp_assoc_state;
};
struct tun_security_struct {
@@ -154,6 +158,4 @@ struct bpf_security_struct {
u32 sid; /*SID of bpf obj creater*/
};
-extern unsigned int selinux_checkreqprot;
-
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 02f0412d42f2..23e762d529fa 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -13,6 +13,8 @@
#include <linux/dcache.h>
#include <linux/magic.h>
#include <linux/types.h>
+#include <linux/refcount.h>
+#include <linux/workqueue.h>
#include "flask.h"
#define SECSID_NULL 0x00000000 /* unspecified SID */
@@ -81,13 +83,6 @@ enum {
extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
-extern int selinux_policycap_netpeer;
-extern int selinux_policycap_openperm;
-extern int selinux_policycap_extsockclass;
-extern int selinux_policycap_alwaysnetwork;
-extern int selinux_policycap_cgroupseclabel;
-extern int selinux_policycap_nnp_nosuid_transition;
-
/*
* type_datum properties
* available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY
@@ -98,13 +93,98 @@ extern int selinux_policycap_nnp_nosuid_transition;
/* limitation of boundary depth */
#define POLICYDB_BOUNDS_MAXDEPTH 4
-int security_mls_enabled(void);
+struct selinux_avc;
+struct selinux_ss;
+
+struct selinux_state {
+ bool disabled;
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+ bool enforcing;
+#endif
+ bool checkreqprot;
+ bool initialized;
+ bool policycap[__POLICYDB_CAPABILITY_MAX];
+ struct selinux_avc *avc;
+ struct selinux_ss *ss;
+};
+
+void selinux_ss_init(struct selinux_ss **ss);
+void selinux_avc_init(struct selinux_avc **avc);
+
+extern struct selinux_state selinux_state;
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+static inline bool enforcing_enabled(struct selinux_state *state)
+{
+ return state->enforcing;
+}
+
+static inline void enforcing_set(struct selinux_state *state, bool value)
+{
+ state->enforcing = value;
+}
+#else
+static inline bool enforcing_enabled(struct selinux_state *state)
+{
+ return true;
+}
+
+static inline void enforcing_set(struct selinux_state *state, bool value)
+{
+}
+#endif
+
+static inline bool selinux_policycap_netpeer(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->policycap[POLICYDB_CAPABILITY_NETPEER];
+}
+
+static inline bool selinux_policycap_openperm(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->policycap[POLICYDB_CAPABILITY_OPENPERM];
+}
-int security_load_policy(void *data, size_t len);
-int security_read_policy(void **data, size_t *len);
-size_t security_policydb_len(void);
+static inline bool selinux_policycap_extsockclass(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->policycap[POLICYDB_CAPABILITY_EXTSOCKCLASS];
+}
-int security_policycap_supported(unsigned int req_cap);
+static inline bool selinux_policycap_alwaysnetwork(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->policycap[POLICYDB_CAPABILITY_ALWAYSNETWORK];
+}
+
+static inline bool selinux_policycap_cgroupseclabel(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->policycap[POLICYDB_CAPABILITY_CGROUPSECLABEL];
+}
+
+static inline bool selinux_policycap_nnp_nosuid_transition(void)
+{
+ struct selinux_state *state = &selinux_state;
+
+ return state->policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION];
+}
+
+int security_mls_enabled(struct selinux_state *state);
+int security_load_policy(struct selinux_state *state,
+ void *data, size_t len);
+int security_read_policy(struct selinux_state *state,
+ void **data, size_t *len);
+size_t security_policydb_len(struct selinux_state *state);
+
+int security_policycap_supported(struct selinux_state *state,
+ unsigned int req_cap);
#define SEL_VEC_MAX 32
struct av_decision {
@@ -141,76 +221,100 @@ struct extended_perms {
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE 0x0001
-void security_compute_av(u32 ssid, u32 tsid,
+void security_compute_av(struct selinux_state *state,
+ u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd,
struct extended_perms *xperms);
-void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass,
- u8 driver, struct extended_perms_decision *xpermd);
+void security_compute_xperms_decision(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
+ u8 driver,
+ struct extended_perms_decision *xpermd);
-void security_compute_av_user(u32 ssid, u32 tsid,
- u16 tclass, struct av_decision *avd);
+void security_compute_av_user(struct selinux_state *state,
+ u32 ssid, u32 tsid,
+ u16 tclass, struct av_decision *avd);
-int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
+int security_transition_sid(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
const struct qstr *qstr, u32 *out_sid);
-int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass,
+int security_transition_sid_user(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
const char *objname, u32 *out_sid);
-int security_member_sid(u32 ssid, u32 tsid,
- u16 tclass, u32 *out_sid);
+int security_member_sid(struct selinux_state *state, u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
-int security_change_sid(u32 ssid, u32 tsid,
- u16 tclass, u32 *out_sid);
+int security_change_sid(struct selinux_state *state, u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
-int security_sid_to_context(u32 sid, char **scontext,
- u32 *scontext_len);
+int security_sid_to_context(struct selinux_state *state, u32 sid,
+ char **scontext, u32 *scontext_len);
-int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
+int security_sid_to_context_force(struct selinux_state *state,
+ u32 sid, char **scontext, u32 *scontext_len);
-int security_context_to_sid(const char *scontext, u32 scontext_len,
+int security_context_to_sid(struct selinux_state *state,
+ const char *scontext, u32 scontext_len,
u32 *out_sid, gfp_t gfp);
-int security_context_str_to_sid(const char *scontext, u32 *out_sid, gfp_t gfp);
+int security_context_str_to_sid(struct selinux_state *state,
+ const char *scontext, u32 *out_sid, gfp_t gfp);
-int security_context_to_sid_default(const char *scontext, u32 scontext_len,
+int security_context_to_sid_default(struct selinux_state *state,
+ const char *scontext, u32 scontext_len,
u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
-int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+int security_context_to_sid_force(struct selinux_state *state,
+ const char *scontext, u32 scontext_len,
u32 *sid);
-int security_get_user_sids(u32 callsid, char *username,
+int security_get_user_sids(struct selinux_state *state,
+ u32 callsid, char *username,
u32 **sids, u32 *nel);
-int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
+int security_port_sid(struct selinux_state *state,
+ u8 protocol, u16 port, u32 *out_sid);
-int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
+int security_ib_pkey_sid(struct selinux_state *state,
+ u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
-int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid);
+int security_ib_endport_sid(struct selinux_state *state,
+ const char *dev_name, u8 port_num, u32 *out_sid);
-int security_netif_sid(char *name, u32 *if_sid);
+int security_netif_sid(struct selinux_state *state,
+ char *name, u32 *if_sid);
-int security_node_sid(u16 domain, void *addr, u32 addrlen,
- u32 *out_sid);
+int security_node_sid(struct selinux_state *state,
+ u16 domain, void *addr, u32 addrlen,
+ u32 *out_sid);
-int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+int security_validate_transition(struct selinux_state *state,
+ u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
-int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
+int security_validate_transition_user(struct selinux_state *state,
+ u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
-int security_bounded_transition(u32 oldsid, u32 newsid);
+int security_bounded_transition(struct selinux_state *state,
+ u32 oldsid, u32 newsid);
-int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
+int security_sid_mls_copy(struct selinux_state *state,
+ u32 sid, u32 mls_sid, u32 *new_sid);
-int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+int security_net_peersid_resolve(struct selinux_state *state,
+ u32 nlbl_sid, u32 nlbl_type,
u32 xfrm_sid,
u32 *peer_sid);
-int security_get_classes(char ***classes, int *nclasses);
-int security_get_permissions(char *class, char ***perms, int *nperms);
-int security_get_reject_unknown(void);
-int security_get_allow_unknown(void);
+int security_get_classes(struct selinux_state *state,
+ char ***classes, int *nclasses);
+int security_get_permissions(struct selinux_state *state,
+ char *class, char ***perms, int *nperms);
+int security_get_reject_unknown(struct selinux_state *state);
+int security_get_allow_unknown(struct selinux_state *state);
#define SECURITY_FS_USE_XATTR 1 /* use xattr */
#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
@@ -221,27 +325,31 @@ int security_get_allow_unknown(void);
#define SECURITY_FS_USE_NATIVE 7 /* use native label support */
#define SECURITY_FS_USE_MAX 7 /* Highest SECURITY_FS_USE_XXX */
-int security_fs_use(struct super_block *sb);
+int security_fs_use(struct selinux_state *state, struct super_block *sb);
-int security_genfs_sid(const char *fstype, char *name, u16 sclass,
- u32 *sid);
+int security_genfs_sid(struct selinux_state *state,
+ const char *fstype, char *name, u16 sclass,
+ u32 *sid);
#ifdef CONFIG_NETLABEL
-int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+int security_netlbl_secattr_to_sid(struct selinux_state *state,
+ struct netlbl_lsm_secattr *secattr,
u32 *sid);
-int security_netlbl_sid_to_secattr(u32 sid,
+int security_netlbl_sid_to_secattr(struct selinux_state *state,
+ u32 sid,
struct netlbl_lsm_secattr *secattr);
#else
-static inline int security_netlbl_secattr_to_sid(
+static inline int security_netlbl_secattr_to_sid(struct selinux_state *state,
struct netlbl_lsm_secattr *secattr,
u32 *sid)
{
return -EIDRM;
}
-static inline int security_netlbl_sid_to_secattr(u32 sid,
- struct netlbl_lsm_secattr *secattr)
+static inline int security_netlbl_sid_to_secattr(struct selinux_state *state,
+ u32 sid,
+ struct netlbl_lsm_secattr *secattr)
{
return -ENOENT;
}
@@ -252,7 +360,7 @@ const char *security_get_initial_sid_context(u32 sid);
/*
* status notifier using mmap interface
*/
-extern struct page *selinux_kernel_status_page(void);
+extern struct page *selinux_kernel_status_page(struct selinux_state *state);
#define SELINUX_KERNEL_STATUS_VERSION 1
struct selinux_kernel_status {
@@ -266,10 +374,12 @@ struct selinux_kernel_status {
*/
} __packed;
-extern void selinux_status_update_setenforce(int enforcing);
-extern void selinux_status_update_policyload(int seqno);
+extern void selinux_status_update_setenforce(struct selinux_state *state,
+ int enforcing);
+extern void selinux_status_update_policyload(struct selinux_state *state,
+ int seqno);
extern void selinux_complete_init(void);
-extern int selinux_disable(void);
+extern int selinux_disable(struct selinux_state *state);
extern void exit_sel_fs(void);
extern struct path selinux_null;
extern struct vfsmount *selinuxfs_mount;
@@ -277,5 +387,8 @@ extern void selnl_notify_setenforce(int val);
extern void selnl_notify_policyload(u32 seqno);
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
-#endif /* _SELINUX_SECURITY_H_ */
+extern void avtab_cache_init(void);
+extern void ebitmap_cache_init(void);
+extern void hashtab_cache_init(void);
+#endif /* _SELINUX_SECURITY_H_ */
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 1f173a7a4daa..a0b465316292 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -47,10 +47,10 @@ static inline void selinux_xfrm_notify_policyload(void)
{
struct net *net;
- rtnl_lock();
+ down_read(&net_rwsem);
for_each_net(net)
rt_genid_bump_all(net);
- rtnl_unlock();
+ up_read(&net_rwsem);
}
#else
static inline int selinux_xfrm_enabled(void)
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index e607b4473ef6..ac65f7417413 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -163,7 +163,7 @@ static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
ret = -ENOMEM;
goto out;
}
- ret = security_netif_sid(dev->name, &new->nsec.sid);
+ ret = security_netif_sid(&selinux_state, dev->name, &new->nsec.sid);
if (ret != 0)
goto out;
new->nsec.ns = ns;
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index aaba6677ee2e..186e727b737b 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -22,8 +22,7 @@
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -60,7 +59,7 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
{
int rc;
- rc = security_netlbl_secattr_to_sid(secattr, sid);
+ rc = security_netlbl_secattr_to_sid(&selinux_state, secattr, sid);
if (rc == 0 &&
(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
(secattr->flags & NETLBL_SECATTR_CACHE))
@@ -91,7 +90,8 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
secattr = netlbl_secattr_alloc(GFP_ATOMIC);
if (secattr == NULL)
return NULL;
- rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
+ rc = security_netlbl_sid_to_secattr(&selinux_state, sksec->sid,
+ secattr);
if (rc != 0) {
netlbl_secattr_free(secattr);
return NULL;
@@ -250,6 +250,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
sk = skb_to_full_sk(skb);
if (sk != NULL) {
struct sk_security_struct *sksec = sk->sk_security;
+
if (sksec->nlbl_state != NLBL_REQSKB)
return 0;
secattr = selinux_netlbl_sock_getattr(sk, sid);
@@ -257,7 +258,8 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
if (secattr == NULL) {
secattr = &secattr_storage;
netlbl_secattr_init(secattr);
- rc = security_netlbl_sid_to_secattr(sid, secattr);
+ rc = security_netlbl_sid_to_secattr(&selinux_state, sid,
+ secattr);
if (rc != 0)
goto skbuff_setsid_return;
}
@@ -271,6 +273,62 @@ skbuff_setsid_return:
}
/**
+ * selinux_netlbl_sctp_assoc_request - Label an incoming sctp association.
+ * @ep: incoming association endpoint.
+ * @skb: the packet.
+ *
+ * Description:
+ * A new incoming connection is represented by @ep, ......
+ * Returns zero on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep,
+ struct sk_buff *skb)
+{
+ int rc;
+ struct netlbl_lsm_secattr secattr;
+ struct sk_security_struct *sksec = ep->base.sk->sk_security;
+ struct sockaddr *addr;
+ struct sockaddr_in addr4;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct sockaddr_in6 addr6;
+#endif
+
+ if (ep->base.sk->sk_family != PF_INET &&
+ ep->base.sk->sk_family != PF_INET6)
+ return 0;
+
+ netlbl_secattr_init(&secattr);
+ rc = security_netlbl_sid_to_secattr(&selinux_state,
+ ep->secid, &secattr);
+ if (rc != 0)
+ goto assoc_request_return;
+
+ /* Move skb hdr address info to a struct sockaddr and then call
+ * netlbl_conn_setattr().
+ */
+ if (ip_hdr(skb)->version == 4) {
+ addr4.sin_family = AF_INET;
+ addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;
+ addr = (struct sockaddr *)&addr4;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_addr = ipv6_hdr(skb)->saddr;
+ addr = (struct sockaddr *)&addr6;
+#endif
+ }
+
+ rc = netlbl_conn_setattr(ep->base.sk, addr, &secattr);
+ if (rc == 0)
+ sksec->nlbl_state = NLBL_LABELED;
+
+assoc_request_return:
+ netlbl_secattr_destroy(&secattr);
+ return rc;
+}
+
+/**
* selinux_netlbl_inet_conn_request - Label an incoming stream connection
* @req: incoming connection request socket
*
@@ -290,7 +348,8 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
return 0;
netlbl_secattr_init(&secattr);
- rc = security_netlbl_sid_to_secattr(req->secid, &secattr);
+ rc = security_netlbl_sid_to_secattr(&selinux_state, req->secid,
+ &secattr);
if (rc != 0)
goto inet_conn_request_return;
rc = netlbl_req_setattr(req, &secattr);
@@ -320,6 +379,22 @@ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
}
/**
+ * selinux_netlbl_sctp_sk_clone - Copy state to the newly created sock
+ * @sk: current sock
+ * @newsk: the new sock
+ *
+ * Description:
+ * Called whenever a new socket is created by accept(2) or sctp_peeloff(3).
+ */
+void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *newsksec = newsk->sk_security;
+
+ newsksec->nlbl_state = sksec->nlbl_state;
+}
+
+/**
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
* @sock: the socket to label
* @family: protocol family
@@ -403,7 +478,8 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
perm = RAWIP_SOCKET__RECVFROM;
}
- rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
+ rc = avc_has_perm(&selinux_state,
+ sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
if (rc == 0)
return 0;
@@ -470,7 +546,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
}
/**
- * selinux_netlbl_socket_connect - Label a client-side socket on connect
+ * selinux_netlbl_socket_connect_helper - Help label a client-side socket on
+ * connect
* @sk: the socket to label
* @addr: the destination address
*
@@ -479,18 +556,13 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
* Returns zero values on success, negative values on failure.
*
*/
-int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
+static int selinux_netlbl_socket_connect_helper(struct sock *sk,
+ struct sockaddr *addr)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr *secattr;
- if (sksec->nlbl_state != NLBL_REQSKB &&
- sksec->nlbl_state != NLBL_CONNLABELED)
- return 0;
-
- lock_sock(sk);
-
/* connected sockets are allowed to disconnect when the address family
* is set to AF_UNSPEC, if that is what is happening we want to reset
* the socket */
@@ -498,18 +570,61 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
netlbl_sock_delattr(sk);
sksec->nlbl_state = NLBL_REQSKB;
rc = 0;
- goto socket_connect_return;
+ return rc;
}
secattr = selinux_netlbl_sock_genattr(sk);
if (secattr == NULL) {
rc = -ENOMEM;
- goto socket_connect_return;
+ return rc;
}
rc = netlbl_conn_setattr(sk, addr, secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_CONNLABELED;
-socket_connect_return:
+ return rc;
+}
+
+/**
+ * selinux_netlbl_socket_connect_locked - Label a client-side socket on
+ * connect
+ * @sk: the socket to label
+ * @addr: the destination address
+ *
+ * Description:
+ * Attempt to label a connected socket that already has the socket locked
+ * with NetLabel using the given address.
+ * Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_connect_locked(struct sock *sk,
+ struct sockaddr *addr)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ if (sksec->nlbl_state != NLBL_REQSKB &&
+ sksec->nlbl_state != NLBL_CONNLABELED)
+ return 0;
+
+ return selinux_netlbl_socket_connect_helper(sk, addr);
+}
+
+/**
+ * selinux_netlbl_socket_connect - Label a client-side socket on connect
+ * @sk: the socket to label
+ * @addr: the destination address
+ *
+ * Description:
+ * Attempt to label a connected socket with NetLabel using the given address.
+ * Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
+{
+ int rc;
+
+ lock_sock(sk);
+ rc = selinux_netlbl_socket_connect_locked(sk, addr);
release_sock(sk);
+
return rc;
}
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index da923f89d2a9..6dd89b89bc1f 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -215,12 +215,12 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
goto out;
switch (family) {
case PF_INET:
- ret = security_node_sid(PF_INET,
+ ret = security_node_sid(&selinux_state, PF_INET,
addr, sizeof(struct in_addr), sid);
new->nsec.addr.ipv4 = *(__be32 *)addr;
break;
case PF_INET6:
- ret = security_node_sid(PF_INET6,
+ ret = security_node_sid(&selinux_state, PF_INET6,
addr, sizeof(struct in6_addr), sid);
new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
break;
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 3311cc393cb4..9ed4c5064a5e 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -161,7 +161,7 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (new == NULL)
goto out;
- ret = security_port_sid(protocol, pnum, sid);
+ ret = security_port_sid(&selinux_state, protocol, pnum, sid);
if (ret != 0)
goto out;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 00eed842c491..245160373dab 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <linux/mount.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/string.h>
@@ -41,34 +42,6 @@
#include "objsec.h"
#include "conditional.h"
-unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
-
-static int __init checkreqprot_setup(char *str)
-{
- unsigned long checkreqprot;
- if (!kstrtoul(str, 0, &checkreqprot))
- selinux_checkreqprot = checkreqprot ? 1 : 0;
- return 1;
-}
-__setup("checkreqprot=", checkreqprot_setup);
-
-static DEFINE_MUTEX(sel_mutex);
-
-/* global data for booleans */
-static struct dentry *bool_dir;
-static int bool_num;
-static char **bool_pending_names;
-static int *bool_pending_values;
-
-/* global data for classes */
-static struct dentry *class_dir;
-static unsigned long last_class_ino;
-
-static char policy_opened;
-
-/* global data for policy capabilities */
-static struct dentry *policycap_dir;
-
enum sel_inos {
SEL_ROOT_INO = 2,
SEL_LOAD, /* load policy */
@@ -93,7 +66,51 @@ enum sel_inos {
SEL_INO_NEXT, /* The next inode number to use */
};
-static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
+struct selinux_fs_info {
+ struct dentry *bool_dir;
+ unsigned int bool_num;
+ char **bool_pending_names;
+ unsigned int *bool_pending_values;
+ struct dentry *class_dir;
+ unsigned long last_class_ino;
+ bool policy_opened;
+ struct dentry *policycap_dir;
+ struct mutex mutex;
+ unsigned long last_ino;
+ struct selinux_state *state;
+ struct super_block *sb;
+};
+
+static int selinux_fs_info_create(struct super_block *sb)
+{
+ struct selinux_fs_info *fsi;
+
+ fsi = kzalloc(sizeof(*fsi), GFP_KERNEL);
+ if (!fsi)
+ return -ENOMEM;
+
+ mutex_init(&fsi->mutex);
+ fsi->last_ino = SEL_INO_NEXT - 1;
+ fsi->state = &selinux_state;
+ fsi->sb = sb;
+ sb->s_fs_info = fsi;
+ return 0;
+}
+
+static void selinux_fs_info_free(struct super_block *sb)
+{
+ struct selinux_fs_info *fsi = sb->s_fs_info;
+ int i;
+
+ if (fsi) {
+ for (i = 0; i < fsi->bool_num; i++)
+ kfree(fsi->bool_pending_names[i]);
+ kfree(fsi->bool_pending_names);
+ kfree(fsi->bool_pending_values);
+ }
+ kfree(sb->s_fs_info);
+ sb->s_fs_info = NULL;
+}
#define SEL_INITCON_INO_OFFSET 0x01000000
#define SEL_BOOL_INO_OFFSET 0x02000000
@@ -105,10 +122,12 @@ static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
char tmpbuf[TMPBUFLEN];
ssize_t length;
- length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing);
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d",
+ enforcing_enabled(fsi->state));
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
@@ -117,9 +136,11 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *page = NULL;
ssize_t length;
- int new_value;
+ int old_value, new_value;
if (count >= PAGE_SIZE)
return -ENOMEM;
@@ -138,23 +159,25 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
new_value = !!new_value;
- if (new_value != selinux_enforcing) {
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ old_value = enforcing_enabled(state);
+ if (new_value != old_value) {
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETENFORCE,
NULL);
if (length)
goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
"enforcing=%d old_enforcing=%d auid=%u ses=%u",
- new_value, selinux_enforcing,
+ new_value, old_value,
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
- selinux_enforcing = new_value;
- if (selinux_enforcing)
- avc_ss_reset(0);
- selnl_notify_setenforce(selinux_enforcing);
- selinux_status_update_setenforce(selinux_enforcing);
- if (!selinux_enforcing)
+ enforcing_set(state, new_value);
+ if (new_value)
+ avc_ss_reset(state->avc, 0);
+ selnl_notify_setenforce(new_value);
+ selinux_status_update_setenforce(state, new_value);
+ if (!new_value)
call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
}
length = count;
@@ -175,11 +198,14 @@ static const struct file_operations sel_enforce_ops = {
static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char tmpbuf[TMPBUFLEN];
ssize_t length;
ino_t ino = file_inode(filp)->i_ino;
int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ?
- security_get_reject_unknown() : !security_get_allow_unknown();
+ security_get_reject_unknown(state) :
+ !security_get_allow_unknown(state);
length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown);
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
@@ -192,7 +218,8 @@ static const struct file_operations sel_handle_unknown_ops = {
static int sel_open_handle_status(struct inode *inode, struct file *filp)
{
- struct page *status = selinux_kernel_status_page();
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
+ struct page *status = selinux_kernel_status_page(fsi->state);
if (!status)
return -ENOMEM;
@@ -248,6 +275,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
char *page;
ssize_t length;
int new_value;
@@ -268,7 +296,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
goto out;
if (new_value) {
- length = selinux_disable();
+ length = selinux_disable(fsi->state);
if (length)
goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
@@ -307,9 +335,9 @@ static const struct file_operations sel_policyvers_ops = {
};
/* declaration for sel_write_load */
-static int sel_make_bools(void);
-static int sel_make_classes(void);
-static int sel_make_policycap(void);
+static int sel_make_bools(struct selinux_fs_info *fsi);
+static int sel_make_classes(struct selinux_fs_info *fsi);
+static int sel_make_policycap(struct selinux_fs_info *fsi);
/* declaration for sel_make_class_dirs */
static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
@@ -318,11 +346,12 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
static ssize_t sel_read_mls(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
char tmpbuf[TMPBUFLEN];
ssize_t length;
length = scnprintf(tmpbuf, TMPBUFLEN, "%d",
- security_mls_enabled());
+ security_mls_enabled(fsi->state));
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
@@ -338,20 +367,23 @@ struct policy_load_memory {
static int sel_open_policy(struct inode *inode, struct file *filp)
{
+ struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
struct policy_load_memory *plm = NULL;
int rc;
BUG_ON(filp->private_data);
- mutex_lock(&sel_mutex);
+ mutex_lock(&fsi->mutex);
- rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ rc = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (rc)
goto err;
rc = -EBUSY;
- if (policy_opened)
+ if (fsi->policy_opened)
goto err;
rc = -ENOMEM;
@@ -359,25 +391,25 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
if (!plm)
goto err;
- if (i_size_read(inode) != security_policydb_len()) {
+ if (i_size_read(inode) != security_policydb_len(state)) {
inode_lock(inode);
- i_size_write(inode, security_policydb_len());
+ i_size_write(inode, security_policydb_len(state));
inode_unlock(inode);
}
- rc = security_read_policy(&plm->data, &plm->len);
+ rc = security_read_policy(state, &plm->data, &plm->len);
if (rc)
goto err;
- policy_opened = 1;
+ fsi->policy_opened = 1;
filp->private_data = plm;
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
return 0;
err:
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
if (plm)
vfree(plm->data);
@@ -387,11 +419,12 @@ err:
static int sel_release_policy(struct inode *inode, struct file *filp)
{
+ struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
struct policy_load_memory *plm = filp->private_data;
BUG_ON(!plm);
- policy_opened = 0;
+ fsi->policy_opened = 0;
vfree(plm->data);
kfree(plm);
@@ -402,19 +435,21 @@ static int sel_release_policy(struct inode *inode, struct file *filp)
static ssize_t sel_read_policy(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
struct policy_load_memory *plm = filp->private_data;
int ret;
- mutex_lock(&sel_mutex);
+ mutex_lock(&fsi->mutex);
- ret = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ ret = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
if (ret)
goto out;
ret = simple_read_from_buffer(buf, count, ppos, plm->data, plm->len);
out:
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
return ret;
}
@@ -468,16 +503,43 @@ static const struct file_operations sel_policy_ops = {
.llseek = generic_file_llseek,
};
+static int sel_make_policy_nodes(struct selinux_fs_info *fsi)
+{
+ int ret;
+
+ ret = sel_make_bools(fsi);
+ if (ret) {
+ pr_err("SELinux: failed to load policy booleans\n");
+ return ret;
+ }
+
+ ret = sel_make_classes(fsi);
+ if (ret) {
+ pr_err("SELinux: failed to load policy classes\n");
+ return ret;
+ }
+
+ ret = sel_make_policycap(fsi);
+ if (ret) {
+ pr_err("SELinux: failed to load policy capabilities\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static ssize_t sel_write_load(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
ssize_t length;
void *data = NULL;
- mutex_lock(&sel_mutex);
+ mutex_lock(&fsi->mutex);
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL);
if (length)
goto out;
@@ -500,29 +562,15 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
if (copy_from_user(data, buf, count) != 0)
goto out;
- length = security_load_policy(data, count);
+ length = security_load_policy(fsi->state, data, count);
if (length) {
pr_warn_ratelimited("SELinux: failed to load policy\n");
goto out;
}
- length = sel_make_bools();
- if (length) {
- pr_err("SELinux: failed to load policy booleans\n");
- goto out1;
- }
-
- length = sel_make_classes();
- if (length) {
- pr_err("SELinux: failed to load policy classes\n");
- goto out1;
- }
-
- length = sel_make_policycap();
- if (length) {
- pr_err("SELinux: failed to load policy capabilities\n");
+ length = sel_make_policy_nodes(fsi);
+ if (length)
goto out1;
- }
length = count;
@@ -532,7 +580,7 @@ out1:
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
out:
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
vfree(data);
return length;
}
@@ -544,20 +592,23 @@ static const struct file_operations sel_load_ops = {
static ssize_t sel_write_context(struct file *file, char *buf, size_t size)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *canon = NULL;
u32 sid, len;
ssize_t length;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL);
if (length)
goto out;
- length = security_context_to_sid(buf, size, &sid, GFP_KERNEL);
+ length = security_context_to_sid(state, buf, size, &sid, GFP_KERNEL);
if (length)
goto out;
- length = security_sid_to_context(sid, &canon, &len);
+ length = security_sid_to_context(state, sid, &canon, &len);
if (length)
goto out;
@@ -578,21 +629,24 @@ out:
static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
char tmpbuf[TMPBUFLEN];
ssize_t length;
- length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot);
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", fsi->state->checkreqprot);
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
char *page;
ssize_t length;
unsigned int new_value;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT,
NULL);
if (length)
@@ -613,7 +667,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
if (sscanf(page, "%u", &new_value) != 1)
goto out;
- selinux_checkreqprot = new_value ? 1 : 0;
+ fsi->state->checkreqprot = new_value ? 1 : 0;
length = count;
out:
kfree(page);
@@ -629,13 +683,16 @@ static ssize_t sel_write_validatetrans(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *oldcon = NULL, *newcon = NULL, *taskcon = NULL;
char *req = NULL;
u32 osid, nsid, tsid;
u16 tclass;
int rc;
- rc = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ rc = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL);
if (rc)
goto out;
@@ -673,19 +730,19 @@ static ssize_t sel_write_validatetrans(struct file *file,
if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4)
goto out;
- rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL);
+ rc = security_context_str_to_sid(state, oldcon, &osid, GFP_KERNEL);
if (rc)
goto out;
- rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL);
+ rc = security_context_str_to_sid(state, newcon, &nsid, GFP_KERNEL);
if (rc)
goto out;
- rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL);
+ rc = security_context_str_to_sid(state, taskcon, &tsid, GFP_KERNEL);
if (rc)
goto out;
- rc = security_validate_transition_user(osid, nsid, tsid, tclass);
+ rc = security_validate_transition_user(state, osid, nsid, tsid, tclass);
if (!rc)
rc = count;
out:
@@ -755,13 +812,16 @@ static const struct file_operations transaction_ops = {
static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid;
u16 tclass;
struct av_decision avd;
ssize_t length;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL);
if (length)
goto out;
@@ -780,15 +840,15 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out;
- length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
- security_compute_av_user(ssid, tsid, tclass, &avd);
+ security_compute_av_user(state, ssid, tsid, tclass, &avd);
length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
"%x %x %x %x %u %x",
@@ -803,6 +863,8 @@ out:
static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *scon = NULL, *tcon = NULL;
char *namebuf = NULL, *objname = NULL;
u32 ssid, tsid, newsid;
@@ -812,7 +874,8 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
u32 len;
int nargs;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE,
NULL);
if (length)
@@ -868,20 +931,20 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
objname = namebuf;
}
- length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
- length = security_transition_sid_user(ssid, tsid, tclass,
+ length = security_transition_sid_user(state, ssid, tsid, tclass,
objname, &newsid);
if (length)
goto out;
- length = security_sid_to_context(newsid, &newcon, &len);
+ length = security_sid_to_context(state, newsid, &newcon, &len);
if (length)
goto out;
@@ -904,6 +967,8 @@ out:
static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid, newsid;
u16 tclass;
@@ -911,7 +976,8 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
char *newcon = NULL;
u32 len;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL,
NULL);
if (length)
@@ -931,19 +997,19 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out;
- length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
- length = security_change_sid(ssid, tsid, tclass, &newsid);
+ length = security_change_sid(state, ssid, tsid, tclass, &newsid);
if (length)
goto out;
- length = security_sid_to_context(newsid, &newcon, &len);
+ length = security_sid_to_context(state, newsid, &newcon, &len);
if (length)
goto out;
@@ -962,6 +1028,8 @@ out:
static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *con = NULL, *user = NULL, *ptr;
u32 sid, *sids = NULL;
ssize_t length;
@@ -969,7 +1037,8 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
int i, rc;
u32 len, nsids;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
NULL);
if (length)
@@ -989,18 +1058,18 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s", con, user) != 2)
goto out;
- length = security_context_str_to_sid(con, &sid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, con, &sid, GFP_KERNEL);
if (length)
goto out;
- length = security_get_user_sids(sid, user, &sids, &nsids);
+ length = security_get_user_sids(state, sid, user, &sids, &nsids);
if (length)
goto out;
length = sprintf(buf, "%u", nsids) + 1;
ptr = buf + length;
for (i = 0; i < nsids; i++) {
- rc = security_sid_to_context(sids[i], &newcon, &len);
+ rc = security_sid_to_context(state, sids[i], &newcon, &len);
if (rc) {
length = rc;
goto out;
@@ -1024,6 +1093,8 @@ out:
static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid, newsid;
u16 tclass;
@@ -1031,7 +1102,8 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
char *newcon = NULL;
u32 len;
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER,
NULL);
if (length)
@@ -1051,19 +1123,19 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out;
- length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
+ length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
- length = security_member_sid(ssid, tsid, tclass, &newsid);
+ length = security_member_sid(state, ssid, tsid, tclass, &newsid);
if (length)
goto out;
- length = security_sid_to_context(newsid, &newcon, &len);
+ length = security_sid_to_context(state, newsid, &newcon, &len);
if (length)
goto out;
@@ -1097,6 +1169,7 @@ static struct inode *sel_make_inode(struct super_block *sb, int mode)
static ssize_t sel_read_bool(struct file *filep, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info;
char *page = NULL;
ssize_t length;
ssize_t ret;
@@ -1104,10 +1177,11 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK;
const char *name = filep->f_path.dentry->d_name.name;
- mutex_lock(&sel_mutex);
+ mutex_lock(&fsi->mutex);
ret = -EINVAL;
- if (index >= bool_num || strcmp(name, bool_pending_names[index]))
+ if (index >= fsi->bool_num || strcmp(name,
+ fsi->bool_pending_names[index]))
goto out;
ret = -ENOMEM;
@@ -1115,16 +1189,16 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
if (!page)
goto out;
- cur_enforcing = security_get_bool_value(index);
+ cur_enforcing = security_get_bool_value(fsi->state, index);
if (cur_enforcing < 0) {
ret = cur_enforcing;
goto out;
}
length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
- bool_pending_values[index]);
+ fsi->bool_pending_values[index]);
ret = simple_read_from_buffer(buf, count, ppos, page, length);
out:
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
free_page((unsigned long)page);
return ret;
}
@@ -1132,22 +1206,25 @@ out:
static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info;
char *page = NULL;
ssize_t length;
int new_value;
unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK;
const char *name = filep->f_path.dentry->d_name.name;
- mutex_lock(&sel_mutex);
+ mutex_lock(&fsi->mutex);
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETBOOL,
NULL);
if (length)
goto out;
length = -EINVAL;
- if (index >= bool_num || strcmp(name, bool_pending_names[index]))
+ if (index >= fsi->bool_num || strcmp(name,
+ fsi->bool_pending_names[index]))
goto out;
length = -ENOMEM;
@@ -1173,11 +1250,11 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (new_value)
new_value = 1;
- bool_pending_values[index] = new_value;
+ fsi->bool_pending_values[index] = new_value;
length = count;
out:
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
kfree(page);
return length;
}
@@ -1192,13 +1269,15 @@ static ssize_t sel_commit_bools_write(struct file *filep,
const char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info;
char *page = NULL;
ssize_t length;
int new_value;
- mutex_lock(&sel_mutex);
+ mutex_lock(&fsi->mutex);
- length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ length = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETBOOL,
NULL);
if (length)
@@ -1225,14 +1304,15 @@ static ssize_t sel_commit_bools_write(struct file *filep,
goto out;
length = 0;
- if (new_value && bool_pending_values)
- length = security_set_bools(bool_num, bool_pending_values);
+ if (new_value && fsi->bool_pending_values)
+ length = security_set_bools(fsi->state, fsi->bool_num,
+ fsi->bool_pending_values);
if (!length)
length = count;
out:
- mutex_unlock(&sel_mutex);
+ mutex_unlock(&fsi->mutex);
kfree(page);
return length;
}
@@ -1250,12 +1330,12 @@ static void sel_remove_entries(struct dentry *de)
#define BOOL_DIR_NAME "booleans"
-static int sel_make_bools(void)
+static int sel_make_bools(struct selinux_fs_info *fsi)
{
int i, ret;
ssize_t len;
struct dentry *dentry = NULL;
- struct dentry *dir = bool_dir;
+ struct dentry *dir = fsi->bool_dir;
struct inode *inode = NULL;
struct inode_security_struct *isec;
char **names = NULL, *page;
@@ -1264,13 +1344,13 @@ static int sel_make_bools(void)
u32 sid;
/* remove any existing files */
- for (i = 0; i < bool_num; i++)
- kfree(bool_pending_names[i]);
- kfree(bool_pending_names);
- kfree(bool_pending_values);
- bool_num = 0;
- bool_pending_names = NULL;
- bool_pending_values = NULL;
+ for (i = 0; i < fsi->bool_num; i++)
+ kfree(fsi->bool_pending_names[i]);
+ kfree(fsi->bool_pending_names);
+ kfree(fsi->bool_pending_values);
+ fsi->bool_num = 0;
+ fsi->bool_pending_names = NULL;
+ fsi->bool_pending_values = NULL;
sel_remove_entries(dir);
@@ -1279,7 +1359,7 @@ static int sel_make_bools(void)
if (!page)
goto out;
- ret = security_get_bools(&num, &names, &values);
+ ret = security_get_bools(fsi->state, &num, &names, &values);
if (ret)
goto out;
@@ -1300,7 +1380,8 @@ static int sel_make_bools(void)
goto out;
isec = (struct inode_security_struct *)inode->i_security;
- ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid);
+ ret = security_genfs_sid(fsi->state, "selinuxfs", page,
+ SECCLASS_FILE, &sid);
if (ret) {
pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n",
page);
@@ -1313,9 +1394,9 @@ static int sel_make_bools(void)
inode->i_ino = i|SEL_BOOL_INO_OFFSET;
d_add(dentry, inode);
}
- bool_num = num;
- bool_pending_names = names;
- bool_pending_values = values;
+ fsi->bool_num = num;
+ fsi->bool_pending_names = names;
+ fsi->bool_pending_values = values;
free_page((unsigned long)page);
return 0;
@@ -1333,17 +1414,16 @@ out:
return ret;
}
-#define NULL_FILE_NAME "null"
-
-struct path selinux_null;
-
static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char tmpbuf[TMPBUFLEN];
ssize_t length;
- length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold);
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
+ avc_get_cache_threshold(state->avc));
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
@@ -1352,11 +1432,14 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *page;
ssize_t ret;
unsigned int new_value;
- ret = avc_has_perm(current_sid(), SECINITSID_SECURITY,
+ ret = avc_has_perm(&selinux_state,
+ current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__SETSECPARAM,
NULL);
if (ret)
@@ -1377,7 +1460,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
if (sscanf(page, "%u", &new_value) != 1)
goto out;
- avc_cache_threshold = new_value;
+ avc_set_cache_threshold(state->avc, new_value);
ret = count;
out:
@@ -1388,6 +1471,8 @@ out:
static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
+ struct selinux_state *state = fsi->state;
char *page;
ssize_t length;
@@ -1395,7 +1480,7 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
if (!page)
return -ENOMEM;
- length = avc_get_hash_stats(page);
+ length = avc_get_hash_stats(state->avc, page);
if (length >= 0)
length = simple_read_from_buffer(buf, count, ppos, page, length);
free_page((unsigned long)page);
@@ -1486,6 +1571,8 @@ static const struct file_operations sel_avc_cache_stats_ops = {
static int sel_make_avc_files(struct dentry *dir)
{
+ struct super_block *sb = dir->d_sb;
+ struct selinux_fs_info *fsi = sb->s_fs_info;
int i;
static const struct tree_descr files[] = {
{ "cache_threshold",
@@ -1509,7 +1596,7 @@ static int sel_make_avc_files(struct dentry *dir)
return -ENOMEM;
inode->i_fop = files[i].ops;
- inode->i_ino = ++sel_last_ino;
+ inode->i_ino = ++fsi->last_ino;
d_add(dentry, inode);
}
@@ -1519,12 +1606,13 @@ static int sel_make_avc_files(struct dentry *dir)
static ssize_t sel_read_initcon(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
char *con;
u32 sid, len;
ssize_t ret;
sid = file_inode(file)->i_ino&SEL_INO_MASK;
- ret = security_sid_to_context(sid, &con, &len);
+ ret = security_sid_to_context(fsi->state, sid, &con, &len);
if (ret)
return ret;
@@ -1612,12 +1700,13 @@ static const struct file_operations sel_perm_ops = {
static ssize_t sel_read_policycap(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
+ struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
int value;
char tmpbuf[TMPBUFLEN];
ssize_t length;
unsigned long i_ino = file_inode(file)->i_ino;
- value = security_policycap_supported(i_ino & SEL_INO_MASK);
+ value = security_policycap_supported(fsi->state, i_ino & SEL_INO_MASK);
length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value);
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
@@ -1631,10 +1720,11 @@ static const struct file_operations sel_policycap_ops = {
static int sel_make_perm_files(char *objclass, int classvalue,
struct dentry *dir)
{
+ struct selinux_fs_info *fsi = dir->d_sb->s_fs_info;
int i, rc, nperms;
char **perms;
- rc = security_get_permissions(objclass, &perms, &nperms);
+ rc = security_get_permissions(fsi->state, objclass, &perms, &nperms);
if (rc)
return rc;
@@ -1668,6 +1758,8 @@ out:
static int sel_make_class_dir_entries(char *classname, int index,
struct dentry *dir)
{
+ struct super_block *sb = dir->d_sb;
+ struct selinux_fs_info *fsi = sb->s_fs_info;
struct dentry *dentry = NULL;
struct inode *inode = NULL;
int rc;
@@ -1684,7 +1776,7 @@ static int sel_make_class_dir_entries(char *classname, int index,
inode->i_ino = sel_class_to_ino(index);
d_add(dentry, inode);
- dentry = sel_make_dir(dir, "perms", &last_class_ino);
+ dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
@@ -1693,26 +1785,27 @@ static int sel_make_class_dir_entries(char *classname, int index,
return rc;
}
-static int sel_make_classes(void)
+static int sel_make_classes(struct selinux_fs_info *fsi)
{
+
int rc, nclasses, i;
char **classes;
/* delete any existing entries */
- sel_remove_entries(class_dir);
+ sel_remove_entries(fsi->class_dir);
- rc = security_get_classes(&classes, &nclasses);
+ rc = security_get_classes(fsi->state, &classes, &nclasses);
if (rc)
return rc;
/* +2 since classes are 1-indexed */
- last_class_ino = sel_class_to_ino(nclasses + 2);
+ fsi->last_class_ino = sel_class_to_ino(nclasses + 2);
for (i = 0; i < nclasses; i++) {
struct dentry *class_name_dir;
- class_name_dir = sel_make_dir(class_dir, classes[i],
- &last_class_ino);
+ class_name_dir = sel_make_dir(fsi->class_dir, classes[i],
+ &fsi->last_class_ino);
if (IS_ERR(class_name_dir)) {
rc = PTR_ERR(class_name_dir);
goto out;
@@ -1732,25 +1825,25 @@ out:
return rc;
}
-static int sel_make_policycap(void)
+static int sel_make_policycap(struct selinux_fs_info *fsi)
{
unsigned int iter;
struct dentry *dentry = NULL;
struct inode *inode = NULL;
- sel_remove_entries(policycap_dir);
+ sel_remove_entries(fsi->policycap_dir);
for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
if (iter < ARRAY_SIZE(selinux_policycap_names))
- dentry = d_alloc_name(policycap_dir,
+ dentry = d_alloc_name(fsi->policycap_dir,
selinux_policycap_names[iter]);
else
- dentry = d_alloc_name(policycap_dir, "unknown");
+ dentry = d_alloc_name(fsi->policycap_dir, "unknown");
if (dentry == NULL)
return -ENOMEM;
- inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO);
+ inode = sel_make_inode(fsi->sb, S_IFREG | 0444);
if (inode == NULL)
return -ENOMEM;
@@ -1789,8 +1882,11 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
return dentry;
}
+#define NULL_FILE_NAME "null"
+
static int sel_fill_super(struct super_block *sb, void *data, int silent)
{
+ struct selinux_fs_info *fsi;
int ret;
struct dentry *dentry;
struct inode *inode;
@@ -1818,14 +1914,20 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
S_IWUGO},
/* last one */ {""}
};
+
+ ret = selinux_fs_info_create(sb);
+ if (ret)
+ goto err;
+
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
if (ret)
goto err;
- bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &sel_last_ino);
- if (IS_ERR(bool_dir)) {
- ret = PTR_ERR(bool_dir);
- bool_dir = NULL;
+ fsi = sb->s_fs_info;
+ fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino);
+ if (IS_ERR(fsi->bool_dir)) {
+ ret = PTR_ERR(fsi->bool_dir);
+ fsi->bool_dir = NULL;
goto err;
}
@@ -1839,7 +1941,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
if (!inode)
goto err;
- inode->i_ino = ++sel_last_ino;
+ inode->i_ino = ++fsi->last_ino;
isec = (struct inode_security_struct *)inode->i_security;
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
@@ -1847,9 +1949,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
d_add(dentry, inode);
- selinux_null.dentry = dentry;
- dentry = sel_make_dir(sb->s_root, "avc", &sel_last_ino);
+ dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto err;
@@ -1859,7 +1960,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
if (ret)
goto err;
- dentry = sel_make_dir(sb->s_root, "initial_contexts", &sel_last_ino);
+ dentry = sel_make_dir(sb->s_root, "initial_contexts", &fsi->last_ino);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
goto err;
@@ -1869,23 +1970,31 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
if (ret)
goto err;
- class_dir = sel_make_dir(sb->s_root, "class", &sel_last_ino);
- if (IS_ERR(class_dir)) {
- ret = PTR_ERR(class_dir);
- class_dir = NULL;
+ fsi->class_dir = sel_make_dir(sb->s_root, "class", &fsi->last_ino);
+ if (IS_ERR(fsi->class_dir)) {
+ ret = PTR_ERR(fsi->class_dir);
+ fsi->class_dir = NULL;
goto err;
}
- policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities", &sel_last_ino);
- if (IS_ERR(policycap_dir)) {
- ret = PTR_ERR(policycap_dir);
- policycap_dir = NULL;
+ fsi->policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities",
+ &fsi->last_ino);
+ if (IS_ERR(fsi->policycap_dir)) {
+ ret = PTR_ERR(fsi->policycap_dir);
+ fsi->policycap_dir = NULL;
goto err;
}
+
+ ret = sel_make_policy_nodes(fsi);
+ if (ret)
+ goto err;
return 0;
err:
printk(KERN_ERR "SELinux: %s: failed while creating inodes\n",
__func__);
+
+ selinux_fs_info_free(sb);
+
return ret;
}
@@ -1895,16 +2004,25 @@ static struct dentry *sel_mount(struct file_system_type *fs_type,
return mount_single(fs_type, flags, data, sel_fill_super);
}
+static void sel_kill_sb(struct super_block *sb)
+{
+ selinux_fs_info_free(sb);
+ kill_litter_super(sb);
+}
+
static struct file_system_type sel_fs_type = {
.name = "selinuxfs",
.mount = sel_mount,
- .kill_sb = kill_litter_super,
+ .kill_sb = sel_kill_sb,
};
struct vfsmount *selinuxfs_mount;
+struct path selinux_null;
static int __init init_sel_fs(void)
{
+ struct qstr null_name = QSTR_INIT(NULL_FILE_NAME,
+ sizeof(NULL_FILE_NAME)-1);
int err;
if (!selinux_enabled)
@@ -1926,6 +2044,13 @@ static int __init init_sel_fs(void)
err = PTR_ERR(selinuxfs_mount);
selinuxfs_mount = NULL;
}
+ selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root,
+ &null_name);
+ if (IS_ERR(selinux_null.dentry)) {
+ pr_err("selinuxfs: could not lookup null!\n");
+ err = PTR_ERR(selinux_null.dentry);
+ selinux_null.dentry = NULL;
+ }
return err;
}
@@ -1936,6 +2061,7 @@ __initcall(init_sel_fs);
void exit_sel_fs(void)
{
sysfs_remove_mount_point(fs_kobj, "selinux");
+ dput(selinux_null.dentry);
kern_unmount(selinuxfs_mount);
unregister_filesystem(&sel_fs_type);
}
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 2c3c7d010d8a..a2c9148b0662 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -655,7 +655,8 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
return rc;
}
-void avtab_cache_init(void)
+
+void __init avtab_cache_init(void)
{
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
@@ -664,9 +665,3 @@ void avtab_cache_init(void)
sizeof(struct avtab_extended_perms),
0, SLAB_PANIC, NULL);
}
-
-void avtab_cache_destroy(void)
-{
- kmem_cache_destroy(avtab_node_cachep);
- kmem_cache_destroy(avtab_xperms_cachep);
-}
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 725853cadc42..0d652fad5319 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -114,9 +114,6 @@ struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);
struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
-void avtab_cache_init(void);
-void avtab_cache_destroy(void);
-
#define MAX_AVTAB_HASH_BITS 16
#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index b6a78b09235c..5ae8c61b75bf 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -523,14 +523,9 @@ int ebitmap_write(struct ebitmap *e, void *fp)
return 0;
}
-void ebitmap_cache_init(void)
+void __init ebitmap_cache_init(void)
{
ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
sizeof(struct ebitmap_node),
0, SLAB_PANIC, NULL);
}
-
-void ebitmap_cache_destroy(void)
-{
- kmem_cache_destroy(ebitmap_node_cachep);
-}
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
index edf4fa39c60a..6aa7cf6a2197 100644
--- a/security/selinux/ss/ebitmap.h
+++ b/security/selinux/ss/ebitmap.h
@@ -131,9 +131,6 @@ void ebitmap_destroy(struct ebitmap *e);
int ebitmap_read(struct ebitmap *e, void *fp);
int ebitmap_write(struct ebitmap *e, void *fp);
-void ebitmap_cache_init(void);
-void ebitmap_cache_destroy(void);
-
#ifdef CONFIG_NETLABEL
int ebitmap_netlbl_export(struct ebitmap *ebmap,
struct netlbl_lsm_catmap **catmap);
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
index fe25b3fb2154..ebfdaa31ee32 100644
--- a/security/selinux/ss/hashtab.c
+++ b/security/selinux/ss/hashtab.c
@@ -169,14 +169,10 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
info->slots_used = slots_used;
info->max_chain_len = max_chain_len;
}
-void hashtab_cache_init(void)
+
+void __init hashtab_cache_init(void)
{
hashtab_node_cachep = kmem_cache_create("hashtab_node",
sizeof(struct hashtab_node),
0, SLAB_PANIC, NULL);
}
-
-void hashtab_cache_destroy(void)
-{
- kmem_cache_destroy(hashtab_node_cachep);
-}
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
index 6183ee2a2e7a..3e3e42bfd150 100644
--- a/security/selinux/ss/hashtab.h
+++ b/security/selinux/ss/hashtab.h
@@ -85,8 +85,4 @@ int hashtab_map(struct hashtab *h,
/* Fill info with some hash table statistics */
void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
-/* Use kmem_cache for hashtab_node */
-void hashtab_cache_init(void);
-void hashtab_cache_destroy(void);
-
#endif /* _SS_HASHTAB_H */
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index ad982ce8bfa4..39475fb455bc 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -33,20 +33,20 @@
* Return the length in bytes for the MLS fields of the
* security context string representation of `context'.
*/
-int mls_compute_context_len(struct context *context)
+int mls_compute_context_len(struct policydb *p, struct context *context)
{
int i, l, len, head, prev;
char *nm;
struct ebitmap *e;
struct ebitmap_node *node;
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return 0;
len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) {
int index_sens = context->range.level[l].sens;
- len += strlen(sym_name(&policydb, SYM_LEVELS, index_sens - 1));
+ len += strlen(sym_name(p, SYM_LEVELS, index_sens - 1));
/* categories */
head = -2;
@@ -56,17 +56,17 @@ int mls_compute_context_len(struct context *context)
if (i - prev > 1) {
/* one or more negative bits are skipped */
if (head != prev) {
- nm = sym_name(&policydb, SYM_CATS, prev);
+ nm = sym_name(p, SYM_CATS, prev);
len += strlen(nm) + 1;
}
- nm = sym_name(&policydb, SYM_CATS, i);
+ nm = sym_name(p, SYM_CATS, i);
len += strlen(nm) + 1;
head = i;
}
prev = i;
}
if (prev != head) {
- nm = sym_name(&policydb, SYM_CATS, prev);
+ nm = sym_name(p, SYM_CATS, prev);
len += strlen(nm) + 1;
}
if (l == 0) {
@@ -86,7 +86,8 @@ int mls_compute_context_len(struct context *context)
* the MLS fields of `context' into the string `*scontext'.
* Update `*scontext' to point to the end of the MLS fields.
*/
-void mls_sid_to_context(struct context *context,
+void mls_sid_to_context(struct policydb *p,
+ struct context *context,
char **scontext)
{
char *scontextp, *nm;
@@ -94,7 +95,7 @@ void mls_sid_to_context(struct context *context,
struct ebitmap *e;
struct ebitmap_node *node;
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return;
scontextp = *scontext;
@@ -103,7 +104,7 @@ void mls_sid_to_context(struct context *context,
scontextp++;
for (l = 0; l < 2; l++) {
- strcpy(scontextp, sym_name(&policydb, SYM_LEVELS,
+ strcpy(scontextp, sym_name(p, SYM_LEVELS,
context->range.level[l].sens - 1));
scontextp += strlen(scontextp);
@@ -119,7 +120,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = '.';
else
*scontextp++ = ',';
- nm = sym_name(&policydb, SYM_CATS, prev);
+ nm = sym_name(p, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
@@ -127,7 +128,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = ':';
else
*scontextp++ = ',';
- nm = sym_name(&policydb, SYM_CATS, i);
+ nm = sym_name(p, SYM_CATS, i);
strcpy(scontextp, nm);
scontextp += strlen(nm);
head = i;
@@ -140,7 +141,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = '.';
else
*scontextp++ = ',';
- nm = sym_name(&policydb, SYM_CATS, prev);
+ nm = sym_name(p, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
@@ -375,12 +376,13 @@ out:
* the string `str'. This function will allocate temporary memory with the
* given constraints of gfp_mask.
*/
-int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
+int mls_from_string(struct policydb *p, char *str, struct context *context,
+ gfp_t gfp_mask)
{
char *tmpstr, *freestr;
int rc;
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return -EINVAL;
/* we need freestr because mls_context_to_sid will change
@@ -389,7 +391,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
if (!tmpstr) {
rc = -ENOMEM;
} else {
- rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
+ rc = mls_context_to_sid(p, ':', &tmpstr, context,
NULL, SECSID_NULL);
kfree(freestr);
}
@@ -417,10 +419,11 @@ int mls_range_set(struct context *context,
return rc;
}
-int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+int mls_setup_user_range(struct policydb *p,
+ struct context *fromcon, struct user_datum *user,
struct context *usercon)
{
- if (policydb.mls_enabled) {
+ if (p->mls_enabled) {
struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
struct mls_level *user_low = &(user->range.level[0]);
@@ -470,7 +473,7 @@ int mls_convert_context(struct policydb *oldp,
struct ebitmap_node *node;
int l, i;
- if (!policydb.mls_enabled)
+ if (!oldp->mls_enabled || !newp->mls_enabled)
return 0;
for (l = 0; l < 2; l++) {
@@ -503,7 +506,8 @@ int mls_convert_context(struct policydb *oldp,
return 0;
}
-int mls_compute_sid(struct context *scontext,
+int mls_compute_sid(struct policydb *p,
+ struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 specified,
@@ -515,7 +519,7 @@ int mls_compute_sid(struct context *scontext,
struct class_datum *cladatum;
int default_range = 0;
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return 0;
switch (specified) {
@@ -524,12 +528,12 @@ int mls_compute_sid(struct context *scontext,
rtr.source_type = scontext->type;
rtr.target_type = tcontext->type;
rtr.target_class = tclass;
- r = hashtab_search(policydb.range_tr, &rtr);
+ r = hashtab_search(p->range_tr, &rtr);
if (r)
return mls_range_set(newcontext, r);
- if (tclass && tclass <= policydb.p_classes.nprim) {
- cladatum = policydb.class_val_to_struct[tclass - 1];
+ if (tclass && tclass <= p->p_classes.nprim) {
+ cladatum = p->class_val_to_struct[tclass - 1];
if (cladatum)
default_range = cladatum->default_range;
}
@@ -551,7 +555,7 @@ int mls_compute_sid(struct context *scontext,
/* Fallthrough */
case AVTAB_CHANGE:
- if ((tclass == policydb.process_class) || (sock == true))
+ if ((tclass == p->process_class) || (sock == true))
/* Use the process MLS attributes. */
return mls_context_cpy(newcontext, scontext);
else
@@ -577,10 +581,11 @@ int mls_compute_sid(struct context *scontext,
* NetLabel MLS sensitivity level field.
*
*/
-void mls_export_netlbl_lvl(struct context *context,
+void mls_export_netlbl_lvl(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return;
secattr->attr.mls.lvl = context->range.level[0].sens - 1;
@@ -597,10 +602,11 @@ void mls_export_netlbl_lvl(struct context *context,
* NetLabel MLS sensitivity level into the context.
*
*/
-void mls_import_netlbl_lvl(struct context *context,
+void mls_import_netlbl_lvl(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return;
context->range.level[0].sens = secattr->attr.mls.lvl + 1;
@@ -617,12 +623,13 @@ void mls_import_netlbl_lvl(struct context *context,
* MLS category field. Returns zero on success, negative values on failure.
*
*/
-int mls_export_netlbl_cat(struct context *context,
+int mls_export_netlbl_cat(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return 0;
rc = ebitmap_netlbl_export(&context->range.level[0].cat,
@@ -645,12 +652,13 @@ int mls_export_netlbl_cat(struct context *context,
* negative values on failure.
*
*/
-int mls_import_netlbl_cat(struct context *context,
+int mls_import_netlbl_cat(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
- if (!policydb.mls_enabled)
+ if (!p->mls_enabled)
return 0;
rc = ebitmap_netlbl_import(&context->range.level[0].cat,
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 131d76266ea5..9a3ff7af70ad 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -25,8 +25,9 @@
#include "context.h"
#include "policydb.h"
-int mls_compute_context_len(struct context *context);
-void mls_sid_to_context(struct context *context, char **scontext);
+int mls_compute_context_len(struct policydb *p, struct context *context);
+void mls_sid_to_context(struct policydb *p, struct context *context,
+ char **scontext);
int mls_context_isvalid(struct policydb *p, struct context *c);
int mls_range_isvalid(struct policydb *p, struct mls_range *r);
int mls_level_isvalid(struct policydb *p, struct mls_level *l);
@@ -38,7 +39,8 @@ int mls_context_to_sid(struct policydb *p,
struct sidtab *s,
u32 def_sid);
-int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
+int mls_from_string(struct policydb *p, char *str, struct context *context,
+ gfp_t gfp_mask);
int mls_range_set(struct context *context, struct mls_range *range);
@@ -46,42 +48,52 @@ int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *context);
-int mls_compute_sid(struct context *scontext,
+int mls_compute_sid(struct policydb *p,
+ struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 specified,
struct context *newcontext,
bool sock);
-int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+int mls_setup_user_range(struct policydb *p,
+ struct context *fromcon, struct user_datum *user,
struct context *usercon);
#ifdef CONFIG_NETLABEL
-void mls_export_netlbl_lvl(struct context *context,
+void mls_export_netlbl_lvl(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr);
-void mls_import_netlbl_lvl(struct context *context,
+void mls_import_netlbl_lvl(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr);
-int mls_export_netlbl_cat(struct context *context,
+int mls_export_netlbl_cat(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr);
-int mls_import_netlbl_cat(struct context *context,
+int mls_import_netlbl_cat(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr);
#else
-static inline void mls_export_netlbl_lvl(struct context *context,
+static inline void mls_export_netlbl_lvl(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return;
}
-static inline void mls_import_netlbl_lvl(struct context *context,
+static inline void mls_import_netlbl_lvl(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return;
}
-static inline int mls_export_netlbl_cat(struct context *context,
+static inline int mls_export_netlbl_cat(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return -ENOMEM;
}
-static inline int mls_import_netlbl_cat(struct context *context,
+static inline int mls_import_netlbl_cat(struct policydb *p,
+ struct context *context,
struct netlbl_lsm_secattr *secattr)
{
return -ENOMEM;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 33cfe5d3d6cb..8057e19dc15f 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -80,53 +80,32 @@ char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
"nnp_nosuid_transition"
};
-int selinux_policycap_netpeer;
-int selinux_policycap_openperm;
-int selinux_policycap_extsockclass;
-int selinux_policycap_alwaysnetwork;
-int selinux_policycap_cgroupseclabel;
-int selinux_policycap_nnp_nosuid_transition;
+static struct selinux_ss selinux_ss;
-static DEFINE_RWLOCK(policy_rwlock);
-
-static struct sidtab sidtab;
-struct policydb policydb;
-int ss_initialized;
-
-/*
- * The largest sequence number that has been used when
- * providing an access decision to the access vector cache.
- * The sequence number only changes when a policy change
- * occurs.
- */
-static u32 latest_granting;
+void selinux_ss_init(struct selinux_ss **ss)
+{
+ rwlock_init(&selinux_ss.policy_rwlock);
+ mutex_init(&selinux_ss.status_lock);
+ *ss = &selinux_ss;
+}
/* Forward declaration. */
-static int context_struct_to_string(struct context *context, char **scontext,
+static int context_struct_to_string(struct policydb *policydb,
+ struct context *context,
+ char **scontext,
u32 *scontext_len);
-static void context_struct_compute_av(struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- struct av_decision *avd,
- struct extended_perms *xperms);
-
-struct selinux_mapping {
- u16 value; /* policy value */
- unsigned num_perms;
- u32 perms[sizeof(u32) * 8];
-};
-
-static struct selinux_mapping *current_mapping;
-static u16 current_mapping_size;
+static void context_struct_compute_av(struct policydb *policydb,
+ struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ struct av_decision *avd,
+ struct extended_perms *xperms);
static int selinux_set_mapping(struct policydb *pol,
struct security_class_mapping *map,
- struct selinux_mapping **out_map_p,
- u16 *out_map_size)
+ struct selinux_map *out_map)
{
- struct selinux_mapping *out_map = NULL;
- size_t size = sizeof(struct selinux_mapping);
u16 i, j;
unsigned k;
bool print_unknown_handle = false;
@@ -139,15 +118,15 @@ static int selinux_set_mapping(struct policydb *pol,
i++;
/* Allocate space for the class records, plus one for class zero */
- out_map = kcalloc(++i, size, GFP_ATOMIC);
- if (!out_map)
+ out_map->mapping = kcalloc(++i, sizeof(*out_map->mapping), GFP_ATOMIC);
+ if (!out_map->mapping)
return -ENOMEM;
/* Store the raw class and permission values */
j = 0;
while (map[j].name) {
struct security_class_mapping *p_in = map + (j++);
- struct selinux_mapping *p_out = out_map + j;
+ struct selinux_mapping *p_out = out_map->mapping + j;
/* An empty class string skips ahead */
if (!strcmp(p_in->name, "")) {
@@ -194,11 +173,11 @@ static int selinux_set_mapping(struct policydb *pol,
printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
pol->allow_unknown ? "allowed" : "denied");
- *out_map_p = out_map;
- *out_map_size = i;
+ out_map->size = i;
return 0;
err:
- kfree(out_map);
+ kfree(out_map->mapping);
+ out_map->mapping = NULL;
return -EINVAL;
}
@@ -206,10 +185,10 @@ err:
* Get real, policy values from mapped values
*/
-static u16 unmap_class(u16 tclass)
+static u16 unmap_class(struct selinux_map *map, u16 tclass)
{
- if (tclass < current_mapping_size)
- return current_mapping[tclass].value;
+ if (tclass < map->size)
+ return map->mapping[tclass].value;
return tclass;
}
@@ -217,42 +196,44 @@ static u16 unmap_class(u16 tclass)
/*
* Get kernel value for class from its policy value
*/
-static u16 map_class(u16 pol_value)
+static u16 map_class(struct selinux_map *map, u16 pol_value)
{
u16 i;
- for (i = 1; i < current_mapping_size; i++) {
- if (current_mapping[i].value == pol_value)
+ for (i = 1; i < map->size; i++) {
+ if (map->mapping[i].value == pol_value)
return i;
}
return SECCLASS_NULL;
}
-static void map_decision(u16 tclass, struct av_decision *avd,
+static void map_decision(struct selinux_map *map,
+ u16 tclass, struct av_decision *avd,
int allow_unknown)
{
- if (tclass < current_mapping_size) {
- unsigned i, n = current_mapping[tclass].num_perms;
+ if (tclass < map->size) {
+ struct selinux_mapping *mapping = &map->mapping[tclass];
+ unsigned int i, n = mapping->num_perms;
u32 result;
for (i = 0, result = 0; i < n; i++) {
- if (avd->allowed & current_mapping[tclass].perms[i])
+ if (avd->allowed & mapping->perms[i])
result |= 1<<i;
- if (allow_unknown && !current_mapping[tclass].perms[i])
+ if (allow_unknown && !mapping->perms[i])
result |= 1<<i;
}
avd->allowed = result;
for (i = 0, result = 0; i < n; i++)
- if (avd->auditallow & current_mapping[tclass].perms[i])
+ if (avd->auditallow & mapping->perms[i])
result |= 1<<i;
avd->auditallow = result;
for (i = 0, result = 0; i < n; i++) {
- if (avd->auditdeny & current_mapping[tclass].perms[i])
+ if (avd->auditdeny & mapping->perms[i])
result |= 1<<i;
- if (!allow_unknown && !current_mapping[tclass].perms[i])
+ if (!allow_unknown && !mapping->perms[i])
result |= 1<<i;
}
/*
@@ -266,9 +247,11 @@ static void map_decision(u16 tclass, struct av_decision *avd,
}
}
-int security_mls_enabled(void)
+int security_mls_enabled(struct selinux_state *state)
{
- return policydb.mls_enabled;
+ struct policydb *p = &state->ss->policydb;
+
+ return p->mls_enabled;
}
/*
@@ -282,7 +265,8 @@ int security_mls_enabled(void)
* of the process performing the transition. All other callers of
* constraint_expr_eval should pass in NULL for xcontext.
*/
-static int constraint_expr_eval(struct context *scontext,
+static int constraint_expr_eval(struct policydb *policydb,
+ struct context *scontext,
struct context *tcontext,
struct context *xcontext,
struct constraint_expr *cexpr)
@@ -326,8 +310,8 @@ static int constraint_expr_eval(struct context *scontext,
case CEXPR_ROLE:
val1 = scontext->role;
val2 = tcontext->role;
- r1 = policydb.role_val_to_struct[val1 - 1];
- r2 = policydb.role_val_to_struct[val2 - 1];
+ r1 = policydb->role_val_to_struct[val1 - 1];
+ r2 = policydb->role_val_to_struct[val2 - 1];
switch (e->op) {
case CEXPR_DOM:
s[++sp] = ebitmap_get_bit(&r1->dominates,
@@ -472,7 +456,8 @@ static int dump_masked_av_helper(void *k, void *d, void *args)
return 0;
}
-static void security_dump_masked_av(struct context *scontext,
+static void security_dump_masked_av(struct policydb *policydb,
+ struct context *scontext,
struct context *tcontext,
u16 tclass,
u32 permissions,
@@ -492,8 +477,8 @@ static void security_dump_masked_av(struct context *scontext,
if (!permissions)
return;
- tclass_name = sym_name(&policydb, SYM_CLASSES, tclass - 1);
- tclass_dat = policydb.class_val_to_struct[tclass - 1];
+ tclass_name = sym_name(policydb, SYM_CLASSES, tclass - 1);
+ tclass_dat = policydb->class_val_to_struct[tclass - 1];
common_dat = tclass_dat->comdatum;
/* init permission_names */
@@ -507,11 +492,11 @@ static void security_dump_masked_av(struct context *scontext,
goto out;
/* get scontext/tcontext in text form */
- if (context_struct_to_string(scontext,
+ if (context_struct_to_string(policydb, scontext,
&scontext_name, &length) < 0)
goto out;
- if (context_struct_to_string(tcontext,
+ if (context_struct_to_string(policydb, tcontext,
&tcontext_name, &length) < 0)
goto out;
@@ -550,7 +535,8 @@ out:
* security_boundary_permission - drops violated permissions
* on boundary constraint.
*/
-static void type_attribute_bounds_av(struct context *scontext,
+static void type_attribute_bounds_av(struct policydb *policydb,
+ struct context *scontext,
struct context *tcontext,
u16 tclass,
struct av_decision *avd)
@@ -562,14 +548,14 @@ static void type_attribute_bounds_av(struct context *scontext,
struct type_datum *target;
u32 masked = 0;
- source = flex_array_get_ptr(policydb.type_val_to_struct_array,
+ source = flex_array_get_ptr(policydb->type_val_to_struct_array,
scontext->type - 1);
BUG_ON(!source);
if (!source->bounds)
return;
- target = flex_array_get_ptr(policydb.type_val_to_struct_array,
+ target = flex_array_get_ptr(policydb->type_val_to_struct_array,
tcontext->type - 1);
BUG_ON(!target);
@@ -584,7 +570,7 @@ static void type_attribute_bounds_av(struct context *scontext,
tcontextp = &lo_tcontext;
}
- context_struct_compute_av(&lo_scontext,
+ context_struct_compute_av(policydb, &lo_scontext,
tcontextp,
tclass,
&lo_avd,
@@ -599,7 +585,7 @@ static void type_attribute_bounds_av(struct context *scontext,
avd->allowed &= ~masked;
/* audit masked permissions */
- security_dump_masked_av(scontext, tcontext,
+ security_dump_masked_av(policydb, scontext, tcontext,
tclass, masked, "bounds");
}
@@ -632,11 +618,12 @@ void services_compute_xperms_drivers(
* Compute access vectors and extended permissions based on a context
* structure pair for the permissions in a particular class.
*/
-static void context_struct_compute_av(struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- struct av_decision *avd,
- struct extended_perms *xperms)
+static void context_struct_compute_av(struct policydb *policydb,
+ struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ struct av_decision *avd,
+ struct extended_perms *xperms)
{
struct constraint_node *constraint;
struct role_allow *ra;
@@ -655,13 +642,13 @@ static void context_struct_compute_av(struct context *scontext,
xperms->len = 0;
}
- if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
+ if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) {
if (printk_ratelimit())
printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass);
return;
}
- tclass_datum = policydb.class_val_to_struct[tclass - 1];
+ tclass_datum = policydb->class_val_to_struct[tclass - 1];
/*
* If a specific type enforcement rule was defined for
@@ -669,15 +656,18 @@ static void context_struct_compute_av(struct context *scontext,
*/
avkey.target_class = tclass;
avkey.specified = AVTAB_AV | AVTAB_XPERMS;
- sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1);
+ sattr = flex_array_get(policydb->type_attr_map_array,
+ scontext->type - 1);
BUG_ON(!sattr);
- tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1);
+ tattr = flex_array_get(policydb->type_attr_map_array,
+ tcontext->type - 1);
BUG_ON(!tattr);
ebitmap_for_each_positive_bit(sattr, snode, i) {
ebitmap_for_each_positive_bit(tattr, tnode, j) {
avkey.source_type = i + 1;
avkey.target_type = j + 1;
- for (node = avtab_search_node(&policydb.te_avtab, &avkey);
+ for (node = avtab_search_node(&policydb->te_avtab,
+ &avkey);
node;
node = avtab_search_node_next(node, avkey.specified)) {
if (node->key.specified == AVTAB_ALLOWED)
@@ -691,7 +681,7 @@ static void context_struct_compute_av(struct context *scontext,
}
/* Check conditional av table for additional permissions */
- cond_compute_av(&policydb.te_cond_avtab, &avkey,
+ cond_compute_av(&policydb->te_cond_avtab, &avkey,
avd, xperms);
}
@@ -704,7 +694,7 @@ static void context_struct_compute_av(struct context *scontext,
constraint = tclass_datum->constraints;
while (constraint) {
if ((constraint->permissions & (avd->allowed)) &&
- !constraint_expr_eval(scontext, tcontext, NULL,
+ !constraint_expr_eval(policydb, scontext, tcontext, NULL,
constraint->expr)) {
avd->allowed &= ~(constraint->permissions);
}
@@ -716,16 +706,16 @@ static void context_struct_compute_av(struct context *scontext,
* role is changing, then check the (current_role, new_role)
* pair.
*/
- if (tclass == policydb.process_class &&
- (avd->allowed & policydb.process_trans_perms) &&
+ if (tclass == policydb->process_class &&
+ (avd->allowed & policydb->process_trans_perms) &&
scontext->role != tcontext->role) {
- for (ra = policydb.role_allow; ra; ra = ra->next) {
+ for (ra = policydb->role_allow; ra; ra = ra->next) {
if (scontext->role == ra->role &&
tcontext->role == ra->new_role)
break;
}
if (!ra)
- avd->allowed &= ~policydb.process_trans_perms;
+ avd->allowed &= ~policydb->process_trans_perms;
}
/*
@@ -733,41 +723,46 @@ static void context_struct_compute_av(struct context *scontext,
* constraint, lazy checks have to mask any violated
* permission and notice it to userspace via audit.
*/
- type_attribute_bounds_av(scontext, tcontext,
+ type_attribute_bounds_av(policydb, scontext, tcontext,
tclass, avd);
}
-static int security_validtrans_handle_fail(struct context *ocontext,
+static int security_validtrans_handle_fail(struct selinux_state *state,
+ struct context *ocontext,
struct context *ncontext,
struct context *tcontext,
u16 tclass)
{
+ struct policydb *p = &state->ss->policydb;
char *o = NULL, *n = NULL, *t = NULL;
u32 olen, nlen, tlen;
- if (context_struct_to_string(ocontext, &o, &olen))
+ if (context_struct_to_string(p, ocontext, &o, &olen))
goto out;
- if (context_struct_to_string(ncontext, &n, &nlen))
+ if (context_struct_to_string(p, ncontext, &n, &nlen))
goto out;
- if (context_struct_to_string(tcontext, &t, &tlen))
+ if (context_struct_to_string(p, tcontext, &t, &tlen))
goto out;
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"op=security_validate_transition seresult=denied"
" oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
- o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
+ o, n, t, sym_name(p, SYM_CLASSES, tclass-1));
out:
kfree(o);
kfree(n);
kfree(t);
- if (!selinux_enforcing)
+ if (!enforcing_enabled(state))
return 0;
return -EPERM;
}
-static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
+static int security_compute_validatetrans(struct selinux_state *state,
+ u32 oldsid, u32 newsid, u32 tasksid,
u16 orig_tclass, bool user)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct context *ocontext;
struct context *ncontext;
struct context *tcontext;
@@ -776,23 +771,27 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass;
int rc = 0;
- if (!ss_initialized)
+
+ if (!state->initialized)
return 0;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
if (!user)
- tclass = unmap_class(orig_tclass);
+ tclass = unmap_class(&state->ss->map, orig_tclass);
else
tclass = orig_tclass;
- if (!tclass || tclass > policydb.p_classes.nprim) {
+ if (!tclass || tclass > policydb->p_classes.nprim) {
rc = -EINVAL;
goto out;
}
- tclass_datum = policydb.class_val_to_struct[tclass - 1];
+ tclass_datum = policydb->class_val_to_struct[tclass - 1];
- ocontext = sidtab_search(&sidtab, oldsid);
+ ocontext = sidtab_search(sidtab, oldsid);
if (!ocontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, oldsid);
@@ -800,7 +799,7 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
goto out;
}
- ncontext = sidtab_search(&sidtab, newsid);
+ ncontext = sidtab_search(sidtab, newsid);
if (!ncontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, newsid);
@@ -808,7 +807,7 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
goto out;
}
- tcontext = sidtab_search(&sidtab, tasksid);
+ tcontext = sidtab_search(sidtab, tasksid);
if (!tcontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, tasksid);
@@ -818,12 +817,13 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
constraint = tclass_datum->validatetrans;
while (constraint) {
- if (!constraint_expr_eval(ocontext, ncontext, tcontext,
- constraint->expr)) {
+ if (!constraint_expr_eval(policydb, ocontext, ncontext,
+ tcontext, constraint->expr)) {
if (user)
rc = -EPERM;
else
- rc = security_validtrans_handle_fail(ocontext,
+ rc = security_validtrans_handle_fail(state,
+ ocontext,
ncontext,
tcontext,
tclass);
@@ -833,22 +833,24 @@ static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
-int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
- u16 tclass)
+int security_validate_transition_user(struct selinux_state *state,
+ u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass)
{
- return security_compute_validatetrans(oldsid, newsid, tasksid,
- tclass, true);
+ return security_compute_validatetrans(state, oldsid, newsid, tasksid,
+ tclass, true);
}
-int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+int security_validate_transition(struct selinux_state *state,
+ u32 oldsid, u32 newsid, u32 tasksid,
u16 orig_tclass)
{
- return security_compute_validatetrans(oldsid, newsid, tasksid,
- orig_tclass, false);
+ return security_compute_validatetrans(state, oldsid, newsid, tasksid,
+ orig_tclass, false);
}
/*
@@ -860,17 +862,26 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
* @oldsid : current security identifier
* @newsid : destinated security identifier
*/
-int security_bounded_transition(u32 old_sid, u32 new_sid)
+int security_bounded_transition(struct selinux_state *state,
+ u32 old_sid, u32 new_sid)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct context *old_context, *new_context;
struct type_datum *type;
int index;
int rc;
- read_lock(&policy_rwlock);
+ if (!state->initialized)
+ return 0;
+
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
rc = -EINVAL;
- old_context = sidtab_search(&sidtab, old_sid);
+ old_context = sidtab_search(sidtab, old_sid);
if (!old_context) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
__func__, old_sid);
@@ -878,7 +889,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
}
rc = -EINVAL;
- new_context = sidtab_search(&sidtab, new_sid);
+ new_context = sidtab_search(sidtab, new_sid);
if (!new_context) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
__func__, new_sid);
@@ -892,7 +903,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
index = new_context->type;
while (true) {
- type = flex_array_get_ptr(policydb.type_val_to_struct_array,
+ type = flex_array_get_ptr(policydb->type_val_to_struct_array,
index - 1);
BUG_ON(!type);
@@ -914,9 +925,9 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
char *new_name = NULL;
u32 length;
- if (!context_struct_to_string(old_context,
+ if (!context_struct_to_string(policydb, old_context,
&old_name, &length) &&
- !context_struct_to_string(new_context,
+ !context_struct_to_string(policydb, new_context,
&new_name, &length)) {
audit_log(current->audit_context,
GFP_ATOMIC, AUDIT_SELINUX_ERR,
@@ -929,17 +940,17 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
kfree(old_name);
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
-static void avd_init(struct av_decision *avd)
+static void avd_init(struct selinux_state *state, struct av_decision *avd)
{
avd->allowed = 0;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
- avd->seqno = latest_granting;
+ avd->seqno = state->ss->latest_granting;
avd->flags = 0;
}
@@ -997,12 +1008,15 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
}
}
-void security_compute_xperms_decision(u32 ssid,
- u32 tsid,
- u16 orig_tclass,
- u8 driver,
- struct extended_perms_decision *xpermd)
+void security_compute_xperms_decision(struct selinux_state *state,
+ u32 ssid,
+ u32 tsid,
+ u16 orig_tclass,
+ u8 driver,
+ struct extended_perms_decision *xpermd)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
u16 tclass;
struct context *scontext, *tcontext;
struct avtab_key avkey;
@@ -1017,60 +1031,64 @@ void security_compute_xperms_decision(u32 ssid,
memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p));
memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p));
- read_lock(&policy_rwlock);
- if (!ss_initialized)
+ read_lock(&state->ss->policy_rwlock);
+ if (!state->initialized)
goto allow;
- scontext = sidtab_search(&sidtab, ssid);
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
goto out;
}
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, tsid);
goto out;
}
- tclass = unmap_class(orig_tclass);
+ tclass = unmap_class(&state->ss->map, orig_tclass);
if (unlikely(orig_tclass && !tclass)) {
- if (policydb.allow_unknown)
+ if (policydb->allow_unknown)
goto allow;
goto out;
}
- if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
+ if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) {
pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass);
goto out;
}
avkey.target_class = tclass;
avkey.specified = AVTAB_XPERMS;
- sattr = flex_array_get(policydb.type_attr_map_array,
+ sattr = flex_array_get(policydb->type_attr_map_array,
scontext->type - 1);
BUG_ON(!sattr);
- tattr = flex_array_get(policydb.type_attr_map_array,
+ tattr = flex_array_get(policydb->type_attr_map_array,
tcontext->type - 1);
BUG_ON(!tattr);
ebitmap_for_each_positive_bit(sattr, snode, i) {
ebitmap_for_each_positive_bit(tattr, tnode, j) {
avkey.source_type = i + 1;
avkey.target_type = j + 1;
- for (node = avtab_search_node(&policydb.te_avtab, &avkey);
+ for (node = avtab_search_node(&policydb->te_avtab,
+ &avkey);
node;
node = avtab_search_node_next(node, avkey.specified))
services_compute_xperms_decision(xpermd, node);
- cond_compute_xperms(&policydb.te_cond_avtab,
+ cond_compute_xperms(&policydb->te_cond_avtab,
&avkey, xpermd);
}
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return;
allow:
memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p));
@@ -1088,22 +1106,28 @@ allow:
* Compute a set of access vector decisions based on the
* SID pair (@ssid, @tsid) for the permissions in @tclass.
*/
-void security_compute_av(u32 ssid,
+void security_compute_av(struct selinux_state *state,
+ u32 ssid,
u32 tsid,
u16 orig_tclass,
struct av_decision *avd,
struct extended_perms *xperms)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
u16 tclass;
struct context *scontext = NULL, *tcontext = NULL;
- read_lock(&policy_rwlock);
- avd_init(avd);
+ read_lock(&state->ss->policy_rwlock);
+ avd_init(state, avd);
xperms->len = 0;
- if (!ss_initialized)
+ if (!state->initialized)
goto allow;
- scontext = sidtab_search(&sidtab, ssid);
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
@@ -1111,45 +1135,53 @@ void security_compute_av(u32 ssid,
}
/* permissive domain? */
- if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+ if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
avd->flags |= AVD_FLAGS_PERMISSIVE;
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, tsid);
goto out;
}
- tclass = unmap_class(orig_tclass);
+ tclass = unmap_class(&state->ss->map, orig_tclass);
if (unlikely(orig_tclass && !tclass)) {
- if (policydb.allow_unknown)
+ if (policydb->allow_unknown)
goto allow;
goto out;
}
- context_struct_compute_av(scontext, tcontext, tclass, avd, xperms);
- map_decision(orig_tclass, avd, policydb.allow_unknown);
+ context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
+ xperms);
+ map_decision(&state->ss->map, orig_tclass, avd,
+ policydb->allow_unknown);
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return;
allow:
avd->allowed = 0xffffffff;
goto out;
}
-void security_compute_av_user(u32 ssid,
+void security_compute_av_user(struct selinux_state *state,
+ u32 ssid,
u32 tsid,
u16 tclass,
struct av_decision *avd)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct context *scontext = NULL, *tcontext = NULL;
- read_lock(&policy_rwlock);
- avd_init(avd);
- if (!ss_initialized)
+ read_lock(&state->ss->policy_rwlock);
+ avd_init(state, avd);
+ if (!state->initialized)
goto allow;
- scontext = sidtab_search(&sidtab, ssid);
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
@@ -1157,10 +1189,10 @@ void security_compute_av_user(u32 ssid,
}
/* permissive domain? */
- if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+ if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
avd->flags |= AVD_FLAGS_PERMISSIVE;
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, tsid);
@@ -1168,14 +1200,15 @@ void security_compute_av_user(u32 ssid,
}
if (unlikely(!tclass)) {
- if (policydb.allow_unknown)
+ if (policydb->allow_unknown)
goto allow;
goto out;
}
- context_struct_compute_av(scontext, tcontext, tclass, avd, NULL);
+ context_struct_compute_av(policydb, scontext, tcontext, tclass, avd,
+ NULL);
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return;
allow:
avd->allowed = 0xffffffff;
@@ -1189,7 +1222,9 @@ allow:
* to point to this string and set `*scontext_len' to
* the length of the string.
*/
-static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
+static int context_struct_to_string(struct policydb *p,
+ struct context *context,
+ char **scontext, u32 *scontext_len)
{
char *scontextp;
@@ -1208,10 +1243,10 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
}
/* Compute the size of the context. */
- *scontext_len += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + 1;
- *scontext_len += strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + 1;
- *scontext_len += strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)) + 1;
- *scontext_len += mls_compute_context_len(context);
+ *scontext_len += strlen(sym_name(p, SYM_USERS, context->user - 1)) + 1;
+ *scontext_len += strlen(sym_name(p, SYM_ROLES, context->role - 1)) + 1;
+ *scontext_len += strlen(sym_name(p, SYM_TYPES, context->type - 1)) + 1;
+ *scontext_len += mls_compute_context_len(p, context);
if (!scontext)
return 0;
@@ -1226,11 +1261,11 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
* Copy the user name, role name and type name into the context.
*/
scontextp += sprintf(scontextp, "%s:%s:%s",
- sym_name(&policydb, SYM_USERS, context->user - 1),
- sym_name(&policydb, SYM_ROLES, context->role - 1),
- sym_name(&policydb, SYM_TYPES, context->type - 1));
+ sym_name(p, SYM_USERS, context->user - 1),
+ sym_name(p, SYM_ROLES, context->role - 1),
+ sym_name(p, SYM_TYPES, context->type - 1));
- mls_sid_to_context(context, &scontextp);
+ mls_sid_to_context(p, context, &scontextp);
*scontextp = 0;
@@ -1246,9 +1281,12 @@ const char *security_get_initial_sid_context(u32 sid)
return initial_sid_to_string[sid];
}
-static int security_sid_to_context_core(u32 sid, char **scontext,
+static int security_sid_to_context_core(struct selinux_state *state,
+ u32 sid, char **scontext,
u32 *scontext_len, int force)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct context *context;
int rc = 0;
@@ -1256,7 +1294,7 @@ static int security_sid_to_context_core(u32 sid, char **scontext,
*scontext = NULL;
*scontext_len = 0;
- if (!ss_initialized) {
+ if (!state->initialized) {
if (sid <= SECINITSID_NUM) {
char *scontextp;
@@ -1277,20 +1315,23 @@ static int security_sid_to_context_core(u32 sid, char **scontext,
rc = -EINVAL;
goto out;
}
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
if (force)
- context = sidtab_search_force(&sidtab, sid);
+ context = sidtab_search_force(sidtab, sid);
else
- context = sidtab_search(&sidtab, sid);
+ context = sidtab_search(sidtab, sid);
if (!context) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, sid);
rc = -EINVAL;
goto out_unlock;
}
- rc = context_struct_to_string(context, scontext, scontext_len);
+ rc = context_struct_to_string(policydb, context, scontext,
+ scontext_len);
out_unlock:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
out:
return rc;
@@ -1306,14 +1347,18 @@ out:
* into a dynamically allocated string of the correct size. Set @scontext
* to point to this string and set @scontext_len to the length of the string.
*/
-int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
+int security_sid_to_context(struct selinux_state *state,
+ u32 sid, char **scontext, u32 *scontext_len)
{
- return security_sid_to_context_core(sid, scontext, scontext_len, 0);
+ return security_sid_to_context_core(state, sid, scontext,
+ scontext_len, 0);
}
-int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
+int security_sid_to_context_force(struct selinux_state *state, u32 sid,
+ char **scontext, u32 *scontext_len)
{
- return security_sid_to_context_core(sid, scontext, scontext_len, 1);
+ return security_sid_to_context_core(state, sid, scontext,
+ scontext_len, 1);
}
/*
@@ -1401,10 +1446,13 @@ out:
return rc;
}
-static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
+static int security_context_to_sid_core(struct selinux_state *state,
+ const char *scontext, u32 scontext_len,
u32 *sid, u32 def_sid, gfp_t gfp_flags,
int force)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
char *scontext2, *str = NULL;
struct context context;
int rc = 0;
@@ -1413,27 +1461,25 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
if (!scontext_len)
return -EINVAL;
- if (!ss_initialized) {
+ /* Copy the string to allow changes and ensure a NUL terminator */
+ scontext2 = kmemdup_nul(scontext, scontext_len, gfp_flags);
+ if (!scontext2)
+ return -ENOMEM;
+
+ if (!state->initialized) {
int i;
for (i = 1; i < SECINITSID_NUM; i++) {
- if (!strcmp(initial_sid_to_string[i], scontext)) {
+ if (!strcmp(initial_sid_to_string[i], scontext2)) {
*sid = i;
- return 0;
+ goto out;
}
}
*sid = SECINITSID_KERNEL;
- return 0;
+ goto out;
}
*sid = SECSID_NULL;
- /* Copy the string so that we can modify the copy as we parse it. */
- scontext2 = kmalloc(scontext_len + 1, gfp_flags);
- if (!scontext2)
- return -ENOMEM;
- memcpy(scontext2, scontext, scontext_len);
- scontext2[scontext_len] = 0;
-
if (force) {
/* Save another copy for storing in uninterpreted form */
rc = -ENOMEM;
@@ -1441,9 +1487,10 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
if (!str)
goto out;
}
-
- read_lock(&policy_rwlock);
- rc = string_to_context_struct(&policydb, &sidtab, scontext2,
+ read_lock(&state->ss->policy_rwlock);
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+ rc = string_to_context_struct(policydb, sidtab, scontext2,
scontext_len, &context, def_sid);
if (rc == -EINVAL && force) {
context.str = str;
@@ -1451,10 +1498,10 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
str = NULL;
} else if (rc)
goto out_unlock;
- rc = sidtab_context_to_sid(&sidtab, &context, sid);
+ rc = sidtab_context_to_sid(sidtab, &context, sid);
context_destroy(&context);
out_unlock:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
out:
kfree(scontext2);
kfree(str);
@@ -1473,16 +1520,19 @@ out:
* Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
* memory is available, or 0 on success.
*/
-int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid,
+int security_context_to_sid(struct selinux_state *state,
+ const char *scontext, u32 scontext_len, u32 *sid,
gfp_t gfp)
{
- return security_context_to_sid_core(scontext, scontext_len,
+ return security_context_to_sid_core(state, scontext, scontext_len,
sid, SECSID_NULL, gfp, 0);
}
-int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp)
+int security_context_str_to_sid(struct selinux_state *state,
+ const char *scontext, u32 *sid, gfp_t gfp)
{
- return security_context_to_sid(scontext, strlen(scontext), sid, gfp);
+ return security_context_to_sid(state, scontext, strlen(scontext),
+ sid, gfp);
}
/**
@@ -1503,51 +1553,56 @@ int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp)
* Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
* memory is available, or 0 on success.
*/
-int security_context_to_sid_default(const char *scontext, u32 scontext_len,
+int security_context_to_sid_default(struct selinux_state *state,
+ const char *scontext, u32 scontext_len,
u32 *sid, u32 def_sid, gfp_t gfp_flags)
{
- return security_context_to_sid_core(scontext, scontext_len,
+ return security_context_to_sid_core(state, scontext, scontext_len,
sid, def_sid, gfp_flags, 1);
}
-int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+int security_context_to_sid_force(struct selinux_state *state,
+ const char *scontext, u32 scontext_len,
u32 *sid)
{
- return security_context_to_sid_core(scontext, scontext_len,
+ return security_context_to_sid_core(state, scontext, scontext_len,
sid, SECSID_NULL, GFP_KERNEL, 1);
}
static int compute_sid_handle_invalid_context(
+ struct selinux_state *state,
struct context *scontext,
struct context *tcontext,
u16 tclass,
struct context *newcontext)
{
+ struct policydb *policydb = &state->ss->policydb;
char *s = NULL, *t = NULL, *n = NULL;
u32 slen, tlen, nlen;
- if (context_struct_to_string(scontext, &s, &slen))
+ if (context_struct_to_string(policydb, scontext, &s, &slen))
goto out;
- if (context_struct_to_string(tcontext, &t, &tlen))
+ if (context_struct_to_string(policydb, tcontext, &t, &tlen))
goto out;
- if (context_struct_to_string(newcontext, &n, &nlen))
+ if (context_struct_to_string(policydb, newcontext, &n, &nlen))
goto out;
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"op=security_compute_sid invalid_context=%s"
" scontext=%s"
" tcontext=%s"
" tclass=%s",
- n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
+ n, s, t, sym_name(policydb, SYM_CLASSES, tclass-1));
out:
kfree(s);
kfree(t);
kfree(n);
- if (!selinux_enforcing)
+ if (!enforcing_enabled(state))
return 0;
return -EACCES;
}
-static void filename_compute_type(struct policydb *p, struct context *newcontext,
+static void filename_compute_type(struct policydb *policydb,
+ struct context *newcontext,
u32 stype, u32 ttype, u16 tclass,
const char *objname)
{
@@ -1559,7 +1614,7 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext
* like /dev or /var/run. This bitmap will quickly skip rule searches
* if the ttype does not contain any rules.
*/
- if (!ebitmap_get_bit(&p->filename_trans_ttypes, ttype))
+ if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype))
return;
ft.stype = stype;
@@ -1567,12 +1622,13 @@ static void filename_compute_type(struct policydb *p, struct context *newcontext
ft.tclass = tclass;
ft.name = objname;
- otype = hashtab_search(p->filename_trans, &ft);
+ otype = hashtab_search(policydb->filename_trans, &ft);
if (otype)
newcontext->type = otype->otype;
}
-static int security_compute_sid(u32 ssid,
+static int security_compute_sid(struct selinux_state *state,
+ u32 ssid,
u32 tsid,
u16 orig_tclass,
u32 specified,
@@ -1580,6 +1636,8 @@ static int security_compute_sid(u32 ssid,
u32 *out_sid,
bool kern)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct class_datum *cladatum = NULL;
struct context *scontext = NULL, *tcontext = NULL, newcontext;
struct role_trans *roletr = NULL;
@@ -1590,7 +1648,7 @@ static int security_compute_sid(u32 ssid,
int rc = 0;
bool sock;
- if (!ss_initialized) {
+ if (!state->initialized) {
switch (orig_tclass) {
case SECCLASS_PROCESS: /* kernel value */
*out_sid = ssid;
@@ -1604,24 +1662,28 @@ static int security_compute_sid(u32 ssid,
context_init(&newcontext);
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
if (kern) {
- tclass = unmap_class(orig_tclass);
+ tclass = unmap_class(&state->ss->map, orig_tclass);
sock = security_is_socket_class(orig_tclass);
} else {
tclass = orig_tclass;
- sock = security_is_socket_class(map_class(tclass));
+ sock = security_is_socket_class(map_class(&state->ss->map,
+ tclass));
}
- scontext = sidtab_search(&sidtab, ssid);
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
rc = -EINVAL;
goto out_unlock;
}
- tcontext = sidtab_search(&sidtab, tsid);
+ tcontext = sidtab_search(sidtab, tsid);
if (!tcontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, tsid);
@@ -1629,8 +1691,8 @@ static int security_compute_sid(u32 ssid,
goto out_unlock;
}
- if (tclass && tclass <= policydb.p_classes.nprim)
- cladatum = policydb.class_val_to_struct[tclass - 1];
+ if (tclass && tclass <= policydb->p_classes.nprim)
+ cladatum = policydb->class_val_to_struct[tclass - 1];
/* Set the user identity. */
switch (specified) {
@@ -1656,7 +1718,7 @@ static int security_compute_sid(u32 ssid,
} else if (cladatum && cladatum->default_role == DEFAULT_TARGET) {
newcontext.role = tcontext->role;
} else {
- if ((tclass == policydb.process_class) || (sock == true))
+ if ((tclass == policydb->process_class) || (sock == true))
newcontext.role = scontext->role;
else
newcontext.role = OBJECT_R_VAL;
@@ -1668,7 +1730,7 @@ static int security_compute_sid(u32 ssid,
} else if (cladatum && cladatum->default_type == DEFAULT_TARGET) {
newcontext.type = tcontext->type;
} else {
- if ((tclass == policydb.process_class) || (sock == true)) {
+ if ((tclass == policydb->process_class) || (sock == true)) {
/* Use the type of process. */
newcontext.type = scontext->type;
} else {
@@ -1682,11 +1744,11 @@ static int security_compute_sid(u32 ssid,
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
avkey.specified = specified;
- avdatum = avtab_search(&policydb.te_avtab, &avkey);
+ avdatum = avtab_search(&policydb->te_avtab, &avkey);
/* If no permanent rule, also check for enabled conditional rules */
if (!avdatum) {
- node = avtab_search_node(&policydb.te_cond_avtab, &avkey);
+ node = avtab_search_node(&policydb->te_cond_avtab, &avkey);
for (; node; node = avtab_search_node_next(node, specified)) {
if (node->key.specified & AVTAB_ENABLED) {
avdatum = &node->datum;
@@ -1702,13 +1764,14 @@ static int security_compute_sid(u32 ssid,
/* if we have a objname this is a file trans check so check those rules */
if (objname)
- filename_compute_type(&policydb, &newcontext, scontext->type,
+ filename_compute_type(policydb, &newcontext, scontext->type,
tcontext->type, tclass, objname);
/* Check for class-specific changes. */
if (specified & AVTAB_TRANSITION) {
/* Look for a role transition rule. */
- for (roletr = policydb.role_tr; roletr; roletr = roletr->next) {
+ for (roletr = policydb->role_tr; roletr;
+ roletr = roletr->next) {
if ((roletr->role == scontext->role) &&
(roletr->type == tcontext->type) &&
(roletr->tclass == tclass)) {
@@ -1721,14 +1784,14 @@ static int security_compute_sid(u32 ssid,
/* Set the MLS attributes.
This is done last because it may allocate memory. */
- rc = mls_compute_sid(scontext, tcontext, tclass, specified,
+ rc = mls_compute_sid(policydb, scontext, tcontext, tclass, specified,
&newcontext, sock);
if (rc)
goto out_unlock;
/* Check the validity of the context. */
- if (!policydb_context_isvalid(&policydb, &newcontext)) {
- rc = compute_sid_handle_invalid_context(scontext,
+ if (!policydb_context_isvalid(policydb, &newcontext)) {
+ rc = compute_sid_handle_invalid_context(state, scontext,
tcontext,
tclass,
&newcontext);
@@ -1736,9 +1799,9 @@ static int security_compute_sid(u32 ssid,
goto out_unlock;
}
/* Obtain the sid for the context. */
- rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
+ rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
out_unlock:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
context_destroy(&newcontext);
out:
return rc;
@@ -1757,17 +1820,21 @@ out:
* if insufficient memory is available, or %0 if the new SID was
* computed successfully.
*/
-int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
+int security_transition_sid(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
const struct qstr *qstr, u32 *out_sid)
{
- return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
+ return security_compute_sid(state, ssid, tsid, tclass,
+ AVTAB_TRANSITION,
qstr ? qstr->name : NULL, out_sid, true);
}
-int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass,
+int security_transition_sid_user(struct selinux_state *state,
+ u32 ssid, u32 tsid, u16 tclass,
const char *objname, u32 *out_sid)
{
- return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
+ return security_compute_sid(state, ssid, tsid, tclass,
+ AVTAB_TRANSITION,
objname, out_sid, false);
}
@@ -1784,12 +1851,14 @@ int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass,
* if insufficient memory is available, or %0 if the SID was
* computed successfully.
*/
-int security_member_sid(u32 ssid,
+int security_member_sid(struct selinux_state *state,
+ u32 ssid,
u32 tsid,
u16 tclass,
u32 *out_sid)
{
- return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, NULL,
+ return security_compute_sid(state, ssid, tsid, tclass,
+ AVTAB_MEMBER, NULL,
out_sid, false);
}
@@ -1806,12 +1875,14 @@ int security_member_sid(u32 ssid,
* if insufficient memory is available, or %0 if the SID was
* computed successfully.
*/
-int security_change_sid(u32 ssid,
+int security_change_sid(struct selinux_state *state,
+ u32 ssid,
u32 tsid,
u16 tclass,
u32 *out_sid)
{
- return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, NULL,
+ return security_compute_sid(state,
+ ssid, tsid, tclass, AVTAB_CHANGE, NULL,
out_sid, false);
}
@@ -1828,15 +1899,18 @@ static int clone_sid(u32 sid,
return 0;
}
-static inline int convert_context_handle_invalid_context(struct context *context)
+static inline int convert_context_handle_invalid_context(
+ struct selinux_state *state,
+ struct context *context)
{
+ struct policydb *policydb = &state->ss->policydb;
char *s;
u32 len;
- if (selinux_enforcing)
+ if (enforcing_enabled(state))
return -EINVAL;
- if (!context_struct_to_string(context, &s, &len)) {
+ if (!context_struct_to_string(policydb, context, &s, &len)) {
printk(KERN_WARNING "SELinux: Context %s would be invalid if enforcing\n", s);
kfree(s);
}
@@ -1844,6 +1918,7 @@ static inline int convert_context_handle_invalid_context(struct context *context
}
struct convert_context_args {
+ struct selinux_state *state;
struct policydb *oldp;
struct policydb *newp;
};
@@ -1970,7 +2045,8 @@ static int convert_context(u32 key,
/* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, c)) {
- rc = convert_context_handle_invalid_context(&oldc);
+ rc = convert_context_handle_invalid_context(args->state,
+ &oldc);
if (rc)
goto bad;
}
@@ -1982,7 +2058,7 @@ out:
return rc;
bad:
/* Map old representation to string and save it. */
- rc = context_struct_to_string(&oldc, &s, &len);
+ rc = context_struct_to_string(args->oldp, &oldc, &s, &len);
if (rc)
return rc;
context_destroy(&oldc);
@@ -1995,39 +2071,29 @@ bad:
goto out;
}
-static void security_load_policycaps(void)
+static void security_load_policycaps(struct selinux_state *state)
{
+ struct policydb *p = &state->ss->policydb;
unsigned int i;
struct ebitmap_node *node;
- selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
- POLICYDB_CAPABILITY_NETPEER);
- selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
- POLICYDB_CAPABILITY_OPENPERM);
- selinux_policycap_extsockclass = ebitmap_get_bit(&policydb.policycaps,
- POLICYDB_CAPABILITY_EXTSOCKCLASS);
- selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
- POLICYDB_CAPABILITY_ALWAYSNETWORK);
- selinux_policycap_cgroupseclabel =
- ebitmap_get_bit(&policydb.policycaps,
- POLICYDB_CAPABILITY_CGROUPSECLABEL);
- selinux_policycap_nnp_nosuid_transition =
- ebitmap_get_bit(&policydb.policycaps,
- POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION);
+ for (i = 0; i < ARRAY_SIZE(state->policycap); i++)
+ state->policycap[i] = ebitmap_get_bit(&p->policycaps, i);
for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++)
pr_info("SELinux: policy capability %s=%d\n",
selinux_policycap_names[i],
- ebitmap_get_bit(&policydb.policycaps, i));
+ ebitmap_get_bit(&p->policycaps, i));
- ebitmap_for_each_positive_bit(&policydb.policycaps, node, i) {
+ ebitmap_for_each_positive_bit(&p->policycaps, node, i) {
if (i >= ARRAY_SIZE(selinux_policycap_names))
pr_info("SELinux: unknown policy capability %u\n",
i);
}
}
-static int security_preserve_bools(struct policydb *p);
+static int security_preserve_bools(struct selinux_state *state,
+ struct policydb *newpolicydb);
/**
* security_load_policy - Load a security policy configuration.
@@ -2039,14 +2105,16 @@ static int security_preserve_bools(struct policydb *p);
* This function will flush the access vector cache after
* loading the new policy.
*/
-int security_load_policy(void *data, size_t len)
+int security_load_policy(struct selinux_state *state, void *data, size_t len)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct policydb *oldpolicydb, *newpolicydb;
struct sidtab oldsidtab, newsidtab;
- struct selinux_mapping *oldmap, *map = NULL;
+ struct selinux_mapping *oldmapping;
+ struct selinux_map newmap;
struct convert_context_args args;
u32 seqno;
- u16 map_size;
int rc = 0;
struct policy_file file = { data, len }, *fp = &file;
@@ -2057,53 +2125,42 @@ int security_load_policy(void *data, size_t len)
}
newpolicydb = oldpolicydb + 1;
- if (!ss_initialized) {
- avtab_cache_init();
- ebitmap_cache_init();
- hashtab_cache_init();
- rc = policydb_read(&policydb, fp);
- if (rc) {
- avtab_cache_destroy();
- ebitmap_cache_destroy();
- hashtab_cache_destroy();
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ if (!state->initialized) {
+ rc = policydb_read(policydb, fp);
+ if (rc)
goto out;
- }
- policydb.len = len;
- rc = selinux_set_mapping(&policydb, secclass_map,
- &current_mapping,
- &current_mapping_size);
+ policydb->len = len;
+ rc = selinux_set_mapping(policydb, secclass_map,
+ &state->ss->map);
if (rc) {
- policydb_destroy(&policydb);
- avtab_cache_destroy();
- ebitmap_cache_destroy();
- hashtab_cache_destroy();
+ policydb_destroy(policydb);
goto out;
}
- rc = policydb_load_isids(&policydb, &sidtab);
+ rc = policydb_load_isids(policydb, sidtab);
if (rc) {
- policydb_destroy(&policydb);
- avtab_cache_destroy();
- ebitmap_cache_destroy();
- hashtab_cache_destroy();
+ policydb_destroy(policydb);
goto out;
}
- security_load_policycaps();
- ss_initialized = 1;
- seqno = ++latest_granting;
+ security_load_policycaps(state);
+ state->initialized = 1;
+ seqno = ++state->ss->latest_granting;
selinux_complete_init();
- avc_ss_reset(seqno);
+ avc_ss_reset(state->avc, seqno);
selnl_notify_policyload(seqno);
- selinux_status_update_policyload(seqno);
+ selinux_status_update_policyload(state, seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
goto out;
}
#if 0
- sidtab_hash_eval(&sidtab, "sids");
+ sidtab_hash_eval(sidtab, "sids");
#endif
rc = policydb_read(newpolicydb, fp);
@@ -2112,9 +2169,9 @@ int security_load_policy(void *data, size_t len)
newpolicydb->len = len;
/* If switching between different policy types, log MLS status */
- if (policydb.mls_enabled && !newpolicydb->mls_enabled)
+ if (policydb->mls_enabled && !newpolicydb->mls_enabled)
printk(KERN_INFO "SELinux: Disabling MLS support...\n");
- else if (!policydb.mls_enabled && newpolicydb->mls_enabled)
+ else if (!policydb->mls_enabled && newpolicydb->mls_enabled)
printk(KERN_INFO "SELinux: Enabling MLS support...\n");
rc = policydb_load_isids(newpolicydb, &newsidtab);
@@ -2124,20 +2181,20 @@ int security_load_policy(void *data, size_t len)
goto out;
}
- rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size);
+ rc = selinux_set_mapping(newpolicydb, secclass_map, &newmap);
if (rc)
goto err;
- rc = security_preserve_bools(newpolicydb);
+ rc = security_preserve_bools(state, newpolicydb);
if (rc) {
printk(KERN_ERR "SELinux: unable to preserve booleans\n");
goto err;
}
/* Clone the SID table. */
- sidtab_shutdown(&sidtab);
+ sidtab_shutdown(sidtab);
- rc = sidtab_map(&sidtab, clone_sid, &newsidtab);
+ rc = sidtab_map(sidtab, clone_sid, &newsidtab);
if (rc)
goto err;
@@ -2145,7 +2202,8 @@ int security_load_policy(void *data, size_t len)
* Convert the internal representations of contexts
* in the new SID table.
*/
- args.oldp = &policydb;
+ args.state = state;
+ args.oldp = policydb;
args.newp = newpolicydb;
rc = sidtab_map(&newsidtab, convert_context, &args);
if (rc) {
@@ -2156,28 +2214,28 @@ int security_load_policy(void *data, size_t len)
}
/* Save the old policydb and SID table to free later. */
- memcpy(oldpolicydb, &policydb, sizeof(policydb));
- sidtab_set(&oldsidtab, &sidtab);
+ memcpy(oldpolicydb, policydb, sizeof(*policydb));
+ sidtab_set(&oldsidtab, sidtab);
/* Install the new policydb and SID table. */
- write_lock_irq(&policy_rwlock);
- memcpy(&policydb, newpolicydb, sizeof(policydb));
- sidtab_set(&sidtab, &newsidtab);
- security_load_policycaps();
- oldmap = current_mapping;
- current_mapping = map;
- current_mapping_size = map_size;
- seqno = ++latest_granting;
- write_unlock_irq(&policy_rwlock);
+ write_lock_irq(&state->ss->policy_rwlock);
+ memcpy(policydb, newpolicydb, sizeof(*policydb));
+ sidtab_set(sidtab, &newsidtab);
+ security_load_policycaps(state);
+ oldmapping = state->ss->map.mapping;
+ state->ss->map.mapping = newmap.mapping;
+ state->ss->map.size = newmap.size;
+ seqno = ++state->ss->latest_granting;
+ write_unlock_irq(&state->ss->policy_rwlock);
/* Free the old policydb and SID table. */
policydb_destroy(oldpolicydb);
sidtab_destroy(&oldsidtab);
- kfree(oldmap);
+ kfree(oldmapping);
- avc_ss_reset(seqno);
+ avc_ss_reset(state->avc, seqno);
selnl_notify_policyload(seqno);
- selinux_status_update_policyload(seqno);
+ selinux_status_update_policyload(state, seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
@@ -2185,7 +2243,7 @@ int security_load_policy(void *data, size_t len)
goto out;
err:
- kfree(map);
+ kfree(newmap.mapping);
sidtab_destroy(&newsidtab);
policydb_destroy(newpolicydb);
@@ -2194,13 +2252,14 @@ out:
return rc;
}
-size_t security_policydb_len(void)
+size_t security_policydb_len(struct selinux_state *state)
{
+ struct policydb *p = &state->ss->policydb;
size_t len;
- read_lock(&policy_rwlock);
- len = policydb.len;
- read_unlock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+ len = p->len;
+ read_unlock(&state->ss->policy_rwlock);
return len;
}
@@ -2211,14 +2270,20 @@ size_t security_policydb_len(void)
* @port: port number
* @out_sid: security identifier
*/
-int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
+int security_port_sid(struct selinux_state *state,
+ u8 protocol, u16 port, u32 *out_sid)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct ocontext *c;
int rc = 0;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
- c = policydb.ocontexts[OCON_PORT];
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ c = policydb->ocontexts[OCON_PORT];
while (c) {
if (c->u.port.protocol == protocol &&
c->u.port.low_port <= port &&
@@ -2229,7 +2294,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
@@ -2241,7 +2306,7 @@ int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -2251,14 +2316,20 @@ out:
* @pkey_num: pkey number
* @out_sid: security identifier
*/
-int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
+int security_ib_pkey_sid(struct selinux_state *state,
+ u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct ocontext *c;
int rc = 0;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
- c = policydb.ocontexts[OCON_IBPKEY];
+ c = policydb->ocontexts[OCON_IBPKEY];
while (c) {
if (c->u.ibpkey.low_pkey <= pkey_num &&
c->u.ibpkey.high_pkey >= pkey_num &&
@@ -2270,7 +2341,7 @@ int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
@@ -2281,7 +2352,7 @@ int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid)
*out_sid = SECINITSID_UNLABELED;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -2291,14 +2362,20 @@ out:
* @port: port number
* @out_sid: security identifier
*/
-int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid)
+int security_ib_endport_sid(struct selinux_state *state,
+ const char *dev_name, u8 port_num, u32 *out_sid)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct ocontext *c;
int rc = 0;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
- c = policydb.ocontexts[OCON_IBENDPORT];
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
+
+ c = policydb->ocontexts[OCON_IBENDPORT];
while (c) {
if (c->u.ibendport.port == port_num &&
!strncmp(c->u.ibendport.dev_name,
@@ -2311,7 +2388,7 @@ int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid)
if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
@@ -2322,7 +2399,7 @@ int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid)
*out_sid = SECINITSID_UNLABELED;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -2331,14 +2408,20 @@ out:
* @name: interface name
* @if_sid: interface SID
*/
-int security_netif_sid(char *name, u32 *if_sid)
+int security_netif_sid(struct selinux_state *state,
+ char *name, u32 *if_sid)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
int rc = 0;
struct ocontext *c;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
- c = policydb.ocontexts[OCON_NETIF];
+ c = policydb->ocontexts[OCON_NETIF];
while (c) {
if (strcmp(name, c->u.name) == 0)
break;
@@ -2347,12 +2430,12 @@ int security_netif_sid(char *name, u32 *if_sid)
if (c) {
if (!c->sid[0] || !c->sid[1]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
goto out;
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(sidtab,
&c->context[1],
&c->sid[1]);
if (rc)
@@ -2363,7 +2446,7 @@ int security_netif_sid(char *name, u32 *if_sid)
*if_sid = SECINITSID_NETIF;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -2387,15 +2470,21 @@ static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
* @addrlen: address length in bytes
* @out_sid: security identifier
*/
-int security_node_sid(u16 domain,
+int security_node_sid(struct selinux_state *state,
+ u16 domain,
void *addrp,
u32 addrlen,
u32 *out_sid)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
int rc;
struct ocontext *c;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
switch (domain) {
case AF_INET: {
@@ -2407,7 +2496,7 @@ int security_node_sid(u16 domain,
addr = *((u32 *)addrp);
- c = policydb.ocontexts[OCON_NODE];
+ c = policydb->ocontexts[OCON_NODE];
while (c) {
if (c->u.node.addr == (addr & c->u.node.mask))
break;
@@ -2420,7 +2509,7 @@ int security_node_sid(u16 domain,
rc = -EINVAL;
if (addrlen != sizeof(u64) * 2)
goto out;
- c = policydb.ocontexts[OCON_NODE6];
+ c = policydb->ocontexts[OCON_NODE6];
while (c) {
if (match_ipv6_addrmask(addrp, c->u.node6.addr,
c->u.node6.mask))
@@ -2437,7 +2526,7 @@ int security_node_sid(u16 domain,
if (c) {
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab,
+ rc = sidtab_context_to_sid(sidtab,
&c->context[0],
&c->sid[0]);
if (rc)
@@ -2450,7 +2539,7 @@ int security_node_sid(u16 domain,
rc = 0;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -2470,11 +2559,14 @@ out:
* number of elements in the array.
*/
-int security_get_user_sids(u32 fromsid,
+int security_get_user_sids(struct selinux_state *state,
+ u32 fromsid,
char *username,
u32 **sids,
u32 *nel)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
struct context *fromcon, usercon;
u32 *mysids = NULL, *mysids2, sid;
u32 mynel = 0, maxnel = SIDS_NEL;
@@ -2486,20 +2578,23 @@ int security_get_user_sids(u32 fromsid,
*sids = NULL;
*nel = 0;
- if (!ss_initialized)
+ if (!state->initialized)
goto out;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
context_init(&usercon);
rc = -EINVAL;
- fromcon = sidtab_search(&sidtab, fromsid);
+ fromcon = sidtab_search(sidtab, fromsid);
if (!fromcon)
goto out_unlock;
rc = -EINVAL;
- user = hashtab_search(policydb.p_users.table, username);
+ user = hashtab_search(policydb->p_users.table, username);
if (!user)
goto out_unlock;
@@ -2511,15 +2606,16 @@ int security_get_user_sids(u32 fromsid,
goto out_unlock;
ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
- role = policydb.role_val_to_struct[i];
+ role = policydb->role_val_to_struct[i];
usercon.role = i + 1;
ebitmap_for_each_positive_bit(&role->types, tnode, j) {
usercon.type = j + 1;
- if (mls_setup_user_range(fromcon, user, &usercon))
+ if (mls_setup_user_range(policydb, fromcon, user,
+ &usercon))
continue;
- rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
+ rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
if (rc)
goto out_unlock;
if (mynel < maxnel) {
@@ -2539,7 +2635,7 @@ int security_get_user_sids(u32 fromsid,
}
rc = 0;
out_unlock:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
if (rc || !mynel) {
kfree(mysids);
goto out;
@@ -2553,7 +2649,8 @@ out_unlock:
}
for (i = 0, j = 0; i < mynel; i++) {
struct av_decision dummy_avd;
- rc = avc_has_perm_noaudit(fromsid, mysids[i],
+ rc = avc_has_perm_noaudit(state,
+ fromsid, mysids[i],
SECCLASS_PROCESS, /* kernel value */
PROCESS__TRANSITION, AVC_STRICT,
&dummy_avd);
@@ -2582,11 +2679,14 @@ out:
*
* The caller must acquire the policy_rwlock before calling this function.
*/
-static inline int __security_genfs_sid(const char *fstype,
+static inline int __security_genfs_sid(struct selinux_state *state,
+ const char *fstype,
char *path,
u16 orig_sclass,
u32 *sid)
{
+ struct policydb *policydb = &state->ss->policydb;
+ struct sidtab *sidtab = &state->ss->sidtab;
int len;
u16 sclass;
struct genfs *genfs;
@@ -2596,10 +2696,10 @@ static inline int __security_genfs_sid(const char *fstype,
while (path[0] == '/' && path[1] == '/')
path++;
- sclass = unmap_class(orig_sclass);
+ sclass = unmap_class(&state->ss->map, orig_sclass);
*sid = SECINITSID_UNLABELED;
- for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
+ for (genfs = policydb->genfs; genfs; genfs = genfs->next) {
cmp = strcmp(fstype, genfs->fstype);
if (cmp <= 0)
break;
@@ -2621,7 +2721,7 @@ static inline int __security_genfs_sid(const char *fstype,
goto out;
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]);
+ rc = sidtab_context_to_sid(sidtab, &c->context[0], &c->sid[0]);
if (rc)
goto out;
}
@@ -2642,16 +2742,17 @@ out:
* Acquire policy_rwlock before calling __security_genfs_sid() and release
* it afterward.
*/
-int security_genfs_sid(const char *fstype,
+int security_genfs_sid(struct selinux_state *state,
+ const char *fstype,
char *path,
u16 orig_sclass,
u32 *sid)
{
int retval;
- read_lock(&policy_rwlock);
- retval = __security_genfs_sid(fstype, path, orig_sclass, sid);
- read_unlock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+ retval = __security_genfs_sid(state, fstype, path, orig_sclass, sid);
+ read_unlock(&state->ss->policy_rwlock);
return retval;
}
@@ -2659,16 +2760,21 @@ int security_genfs_sid(const char *fstype,
* security_fs_use - Determine how to handle labeling for a filesystem.
* @sb: superblock in question
*/
-int security_fs_use(struct super_block *sb)
+int security_fs_use(struct selinux_state *state, struct super_block *sb)
{
+ struct policydb *policydb;
+ struct sidtab *sidtab;
int rc = 0;
struct ocontext *c;
struct superblock_security_struct *sbsec = sb->s_security;
const char *fstype = sb->s_type->name;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+ sidtab = &state->ss->sidtab;
- c = policydb.ocontexts[OCON_FSUSE];
+ c = policydb->ocontexts[OCON_FSUSE];
while (c) {
if (strcmp(fstype, c->u.name) == 0)
break;
@@ -2678,14 +2784,14 @@ int security_fs_use(struct super_block *sb)
if (c) {
sbsec->behavior = c->v.behavior;
if (!c->sid[0]) {
- rc = sidtab_context_to_sid(&sidtab, &c->context[0],
+ rc = sidtab_context_to_sid(sidtab, &c->context[0],
&c->sid[0]);
if (rc)
goto out;
}
sbsec->sid = c->sid[0];
} else {
- rc = __security_genfs_sid(fstype, "/", SECCLASS_DIR,
+ rc = __security_genfs_sid(state, fstype, "/", SECCLASS_DIR,
&sbsec->sid);
if (rc) {
sbsec->behavior = SECURITY_FS_USE_NONE;
@@ -2696,20 +2802,32 @@ int security_fs_use(struct super_block *sb)
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
-int security_get_bools(int *len, char ***names, int **values)
+int security_get_bools(struct selinux_state *state,
+ int *len, char ***names, int **values)
{
+ struct policydb *policydb;
int i, rc;
- read_lock(&policy_rwlock);
+ if (!state->initialized) {
+ *len = 0;
+ *names = NULL;
+ *values = NULL;
+ return 0;
+ }
+
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
+
*names = NULL;
*values = NULL;
rc = 0;
- *len = policydb.p_bools.nprim;
+ *len = policydb->p_bools.nprim;
if (!*len)
goto out;
@@ -2724,16 +2842,17 @@ int security_get_bools(int *len, char ***names, int **values)
goto err;
for (i = 0; i < *len; i++) {
- (*values)[i] = policydb.bool_val_to_struct[i]->state;
+ (*values)[i] = policydb->bool_val_to_struct[i]->state;
rc = -ENOMEM;
- (*names)[i] = kstrdup(sym_name(&policydb, SYM_BOOLS, i), GFP_ATOMIC);
+ (*names)[i] = kstrdup(sym_name(policydb, SYM_BOOLS, i),
+ GFP_ATOMIC);
if (!(*names)[i])
goto err;
}
rc = 0;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
err:
if (*names) {
@@ -2745,90 +2864,98 @@ err:
}
-int security_set_bools(int len, int *values)
+int security_set_bools(struct selinux_state *state, int len, int *values)
{
+ struct policydb *policydb;
int i, rc;
int lenp, seqno = 0;
struct cond_node *cur;
- write_lock_irq(&policy_rwlock);
+ write_lock_irq(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
rc = -EFAULT;
- lenp = policydb.p_bools.nprim;
+ lenp = policydb->p_bools.nprim;
if (len != lenp)
goto out;
for (i = 0; i < len; i++) {
- if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
+ if (!!values[i] != policydb->bool_val_to_struct[i]->state) {
audit_log(current->audit_context, GFP_ATOMIC,
AUDIT_MAC_CONFIG_CHANGE,
"bool=%s val=%d old_val=%d auid=%u ses=%u",
- sym_name(&policydb, SYM_BOOLS, i),
+ sym_name(policydb, SYM_BOOLS, i),
!!values[i],
- policydb.bool_val_to_struct[i]->state,
+ policydb->bool_val_to_struct[i]->state,
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
}
if (values[i])
- policydb.bool_val_to_struct[i]->state = 1;
+ policydb->bool_val_to_struct[i]->state = 1;
else
- policydb.bool_val_to_struct[i]->state = 0;
+ policydb->bool_val_to_struct[i]->state = 0;
}
- for (cur = policydb.cond_list; cur; cur = cur->next) {
- rc = evaluate_cond_node(&policydb, cur);
+ for (cur = policydb->cond_list; cur; cur = cur->next) {
+ rc = evaluate_cond_node(policydb, cur);
if (rc)
goto out;
}
- seqno = ++latest_granting;
+ seqno = ++state->ss->latest_granting;
rc = 0;
out:
- write_unlock_irq(&policy_rwlock);
+ write_unlock_irq(&state->ss->policy_rwlock);
if (!rc) {
- avc_ss_reset(seqno);
+ avc_ss_reset(state->avc, seqno);
selnl_notify_policyload(seqno);
- selinux_status_update_policyload(seqno);
+ selinux_status_update_policyload(state, seqno);
selinux_xfrm_notify_policyload();
}
return rc;
}
-int security_get_bool_value(int index)
+int security_get_bool_value(struct selinux_state *state,
+ int index)
{
+ struct policydb *policydb;
int rc;
int len;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+
+ policydb = &state->ss->policydb;
rc = -EFAULT;
- len = policydb.p_bools.nprim;
+ len = policydb->p_bools.nprim;
if (index >= len)
goto out;
- rc = policydb.bool_val_to_struct[index]->state;
+ rc = policydb->bool_val_to_struct[index]->state;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
-static int security_preserve_bools(struct policydb *p)
+static int security_preserve_bools(struct selinux_state *state,
+ struct policydb *policydb)
{
int rc, nbools = 0, *bvalues = NULL, i;
char **bnames = NULL;
struct cond_bool_datum *booldatum;
struct cond_node *cur;
- rc = security_get_bools(&nbools, &bnames, &bvalues);
+ rc = security_get_bools(state, &nbools, &bnames, &bvalues);
if (rc)
goto out;
for (i = 0; i < nbools; i++) {
- booldatum = hashtab_search(p->p_bools.table, bnames[i]);
+ booldatum = hashtab_search(policydb->p_bools.table, bnames[i]);
if (booldatum)
booldatum->state = bvalues[i];
}
- for (cur = p->cond_list; cur; cur = cur->next) {
- rc = evaluate_cond_node(p, cur);
+ for (cur = policydb->cond_list; cur; cur = cur->next) {
+ rc = evaluate_cond_node(policydb, cur);
if (rc)
goto out;
}
@@ -2847,8 +2974,11 @@ out:
* security_sid_mls_copy() - computes a new sid based on the given
* sid and the mls portion of mls_sid.
*/
-int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
+int security_sid_mls_copy(struct selinux_state *state,
+ u32 sid, u32 mls_sid, u32 *new_sid)
{
+ struct policydb *policydb = &state->ss->policydb;
+ struct sidtab *sidtab = &state->ss->sidtab;
struct context *context1;
struct context *context2;
struct context newcon;
@@ -2857,17 +2987,17 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
int rc;
rc = 0;
- if (!ss_initialized || !policydb.mls_enabled) {
+ if (!state->initialized || !policydb->mls_enabled) {
*new_sid = sid;
goto out;
}
context_init(&newcon);
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
rc = -EINVAL;
- context1 = sidtab_search(&sidtab, sid);
+ context1 = sidtab_search(sidtab, sid);
if (!context1) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, sid);
@@ -2875,7 +3005,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
}
rc = -EINVAL;
- context2 = sidtab_search(&sidtab, mls_sid);
+ context2 = sidtab_search(sidtab, mls_sid);
if (!context2) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, mls_sid);
@@ -2890,10 +3020,11 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
goto out_unlock;
/* Check the validity of the new context. */
- if (!policydb_context_isvalid(&policydb, &newcon)) {
- rc = convert_context_handle_invalid_context(&newcon);
+ if (!policydb_context_isvalid(policydb, &newcon)) {
+ rc = convert_context_handle_invalid_context(state, &newcon);
if (rc) {
- if (!context_struct_to_string(&newcon, &s, &len)) {
+ if (!context_struct_to_string(policydb, &newcon, &s,
+ &len)) {
audit_log(current->audit_context,
GFP_ATOMIC, AUDIT_SELINUX_ERR,
"op=security_sid_mls_copy "
@@ -2904,9 +3035,9 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
}
}
- rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
+ rc = sidtab_context_to_sid(sidtab, &newcon, new_sid);
out_unlock:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
context_destroy(&newcon);
out:
return rc;
@@ -2932,10 +3063,13 @@ out:
* multiple, inconsistent labels | -<errno> | SECSID_NULL
*
*/
-int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+int security_net_peersid_resolve(struct selinux_state *state,
+ u32 nlbl_sid, u32 nlbl_type,
u32 xfrm_sid,
u32 *peer_sid)
{
+ struct policydb *policydb = &state->ss->policydb;
+ struct sidtab *sidtab = &state->ss->sidtab;
int rc;
struct context *nlbl_ctx;
struct context *xfrm_ctx;
@@ -2957,23 +3091,25 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
return 0;
}
- /* we don't need to check ss_initialized here since the only way both
+ /*
+ * We don't need to check initialized here since the only way both
* nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
- * security server was initialized and ss_initialized was true */
- if (!policydb.mls_enabled)
+ * security server was initialized and state->initialized was true.
+ */
+ if (!policydb->mls_enabled)
return 0;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
rc = -EINVAL;
- nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
+ nlbl_ctx = sidtab_search(sidtab, nlbl_sid);
if (!nlbl_ctx) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, nlbl_sid);
goto out;
}
rc = -EINVAL;
- xfrm_ctx = sidtab_search(&sidtab, xfrm_sid);
+ xfrm_ctx = sidtab_search(sidtab, xfrm_sid);
if (!xfrm_ctx) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, xfrm_sid);
@@ -2990,7 +3126,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
* expressive */
*peer_sid = xfrm_sid;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -3007,19 +3143,27 @@ static int get_classes_callback(void *k, void *d, void *args)
return 0;
}
-int security_get_classes(char ***classes, int *nclasses)
+int security_get_classes(struct selinux_state *state,
+ char ***classes, int *nclasses)
{
+ struct policydb *policydb = &state->ss->policydb;
int rc;
- read_lock(&policy_rwlock);
+ if (!state->initialized) {
+ *nclasses = 0;
+ *classes = NULL;
+ return 0;
+ }
+
+ read_lock(&state->ss->policy_rwlock);
rc = -ENOMEM;
- *nclasses = policydb.p_classes.nprim;
+ *nclasses = policydb->p_classes.nprim;
*classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC);
if (!*classes)
goto out;
- rc = hashtab_map(policydb.p_classes.table, get_classes_callback,
+ rc = hashtab_map(policydb->p_classes.table, get_classes_callback,
*classes);
if (rc) {
int i;
@@ -3029,7 +3173,7 @@ int security_get_classes(char ***classes, int *nclasses)
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -3046,15 +3190,17 @@ static int get_permissions_callback(void *k, void *d, void *args)
return 0;
}
-int security_get_permissions(char *class, char ***perms, int *nperms)
+int security_get_permissions(struct selinux_state *state,
+ char *class, char ***perms, int *nperms)
{
+ struct policydb *policydb = &state->ss->policydb;
int rc, i;
struct class_datum *match;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
rc = -EINVAL;
- match = hashtab_search(policydb.p_classes.table, class);
+ match = hashtab_search(policydb->p_classes.table, class);
if (!match) {
printk(KERN_ERR "SELinux: %s: unrecognized class %s\n",
__func__, class);
@@ -3080,25 +3226,25 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
goto err;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
err:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
for (i = 0; i < *nperms; i++)
kfree((*perms)[i]);
kfree(*perms);
return rc;
}
-int security_get_reject_unknown(void)
+int security_get_reject_unknown(struct selinux_state *state)
{
- return policydb.reject_unknown;
+ return state->ss->policydb.reject_unknown;
}
-int security_get_allow_unknown(void)
+int security_get_allow_unknown(struct selinux_state *state)
{
- return policydb.allow_unknown;
+ return state->ss->policydb.allow_unknown;
}
/**
@@ -3111,13 +3257,15 @@ int security_get_allow_unknown(void)
* supported, false (0) if it isn't supported.
*
*/
-int security_policycap_supported(unsigned int req_cap)
+int security_policycap_supported(struct selinux_state *state,
+ unsigned int req_cap)
{
+ struct policydb *policydb = &state->ss->policydb;
int rc;
- read_lock(&policy_rwlock);
- rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
- read_unlock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+ rc = ebitmap_get_bit(&policydb->policycaps, req_cap);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -3139,6 +3287,8 @@ void selinux_audit_rule_free(void *vrule)
int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
{
+ struct selinux_state *state = &selinux_state;
+ struct policydb *policydb = &state->ss->policydb;
struct selinux_audit_rule *tmprule;
struct role_datum *roledatum;
struct type_datum *typedatum;
@@ -3148,7 +3298,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
*rule = NULL;
- if (!ss_initialized)
+ if (!state->initialized)
return -EOPNOTSUPP;
switch (field) {
@@ -3181,15 +3331,15 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
context_init(&tmprule->au_ctxt);
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
- tmprule->au_seqno = latest_granting;
+ tmprule->au_seqno = state->ss->latest_granting;
switch (field) {
case AUDIT_SUBJ_USER:
case AUDIT_OBJ_USER:
rc = -EINVAL;
- userdatum = hashtab_search(policydb.p_users.table, rulestr);
+ userdatum = hashtab_search(policydb->p_users.table, rulestr);
if (!userdatum)
goto out;
tmprule->au_ctxt.user = userdatum->value;
@@ -3197,7 +3347,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
case AUDIT_SUBJ_ROLE:
case AUDIT_OBJ_ROLE:
rc = -EINVAL;
- roledatum = hashtab_search(policydb.p_roles.table, rulestr);
+ roledatum = hashtab_search(policydb->p_roles.table, rulestr);
if (!roledatum)
goto out;
tmprule->au_ctxt.role = roledatum->value;
@@ -3205,7 +3355,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
case AUDIT_SUBJ_TYPE:
case AUDIT_OBJ_TYPE:
rc = -EINVAL;
- typedatum = hashtab_search(policydb.p_types.table, rulestr);
+ typedatum = hashtab_search(policydb->p_types.table, rulestr);
if (!typedatum)
goto out;
tmprule->au_ctxt.type = typedatum->value;
@@ -3214,14 +3364,15 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
case AUDIT_SUBJ_CLR:
case AUDIT_OBJ_LEV_LOW:
case AUDIT_OBJ_LEV_HIGH:
- rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
+ rc = mls_from_string(policydb, rulestr, &tmprule->au_ctxt,
+ GFP_ATOMIC);
if (rc)
goto out;
break;
}
rc = 0;
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
if (rc) {
selinux_audit_rule_free(tmprule);
@@ -3261,6 +3412,7 @@ int selinux_audit_rule_known(struct audit_krule *rule)
int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
struct audit_context *actx)
{
+ struct selinux_state *state = &selinux_state;
struct context *ctxt;
struct mls_level *level;
struct selinux_audit_rule *rule = vrule;
@@ -3271,14 +3423,14 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
return -ENOENT;
}
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
- if (rule->au_seqno < latest_granting) {
+ if (rule->au_seqno < state->ss->latest_granting) {
match = -ESTALE;
goto out;
}
- ctxt = sidtab_search(&sidtab, sid);
+ ctxt = sidtab_search(&state->ss->sidtab, sid);
if (unlikely(!ctxt)) {
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
sid);
@@ -3362,7 +3514,7 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
}
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return match;
}
@@ -3436,19 +3588,22 @@ static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
* failure.
*
*/
-int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+int security_netlbl_secattr_to_sid(struct selinux_state *state,
+ struct netlbl_lsm_secattr *secattr,
u32 *sid)
{
+ struct policydb *policydb = &state->ss->policydb;
+ struct sidtab *sidtab = &state->ss->sidtab;
int rc;
struct context *ctx;
struct context ctx_new;
- if (!ss_initialized) {
+ if (!state->initialized) {
*sid = SECSID_NULL;
return 0;
}
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
if (secattr->flags & NETLBL_SECATTR_CACHE)
*sid = *(u32 *)secattr->cache->data;
@@ -3456,7 +3611,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
*sid = secattr->attr.secid;
else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
rc = -EIDRM;
- ctx = sidtab_search(&sidtab, SECINITSID_NETMSG);
+ ctx = sidtab_search(sidtab, SECINITSID_NETMSG);
if (ctx == NULL)
goto out;
@@ -3464,17 +3619,17 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
ctx_new.user = ctx->user;
ctx_new.role = ctx->role;
ctx_new.type = ctx->type;
- mls_import_netlbl_lvl(&ctx_new, secattr);
+ mls_import_netlbl_lvl(policydb, &ctx_new, secattr);
if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
- rc = mls_import_netlbl_cat(&ctx_new, secattr);
+ rc = mls_import_netlbl_cat(policydb, &ctx_new, secattr);
if (rc)
goto out;
}
rc = -EIDRM;
- if (!mls_context_isvalid(&policydb, &ctx_new))
+ if (!mls_context_isvalid(policydb, &ctx_new))
goto out_free;
- rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+ rc = sidtab_context_to_sid(sidtab, &ctx_new, sid);
if (rc)
goto out_free;
@@ -3484,12 +3639,12 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
} else
*sid = SECSID_NULL;
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return 0;
out_free:
ebitmap_destroy(&ctx_new.range.level[0].cat);
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
@@ -3503,33 +3658,35 @@ out:
* Returns zero on success, negative values on failure.
*
*/
-int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
+int security_netlbl_sid_to_secattr(struct selinux_state *state,
+ u32 sid, struct netlbl_lsm_secattr *secattr)
{
+ struct policydb *policydb = &state->ss->policydb;
int rc;
struct context *ctx;
- if (!ss_initialized)
+ if (!state->initialized)
return 0;
- read_lock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
rc = -ENOENT;
- ctx = sidtab_search(&sidtab, sid);
+ ctx = sidtab_search(&state->ss->sidtab, sid);
if (ctx == NULL)
goto out;
rc = -ENOMEM;
- secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1),
+ secattr->domain = kstrdup(sym_name(policydb, SYM_TYPES, ctx->type - 1),
GFP_ATOMIC);
if (secattr->domain == NULL)
goto out;
secattr->attr.secid = sid;
secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
- mls_export_netlbl_lvl(ctx, secattr);
- rc = mls_export_netlbl_cat(ctx, secattr);
+ mls_export_netlbl_lvl(policydb, ctx, secattr);
+ rc = mls_export_netlbl_cat(policydb, ctx, secattr);
out:
- read_unlock(&policy_rwlock);
+ read_unlock(&state->ss->policy_rwlock);
return rc;
}
#endif /* CONFIG_NETLABEL */
@@ -3540,15 +3697,17 @@ out:
* @len: length of data in bytes
*
*/
-int security_read_policy(void **data, size_t *len)
+int security_read_policy(struct selinux_state *state,
+ void **data, size_t *len)
{
+ struct policydb *policydb = &state->ss->policydb;
int rc;
struct policy_file fp;
- if (!ss_initialized)
+ if (!state->initialized)
return -EINVAL;
- *len = security_policydb_len();
+ *len = security_policydb_len(state);
*data = vmalloc_user(*len);
if (!*data)
@@ -3557,9 +3716,9 @@ int security_read_policy(void **data, size_t *len)
fp.data = *data;
fp.len = *len;
- read_lock(&policy_rwlock);
- rc = policydb_write(&policydb, &fp);
- read_unlock(&policy_rwlock);
+ read_lock(&state->ss->policy_rwlock);
+ rc = policydb_write(policydb, &fp);
+ read_unlock(&state->ss->policy_rwlock);
if (rc)
return rc;
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h
index 356bdd36cf6d..24c7bdcc8075 100644
--- a/security/selinux/ss/services.h
+++ b/security/selinux/ss/services.h
@@ -10,7 +10,28 @@
#include "policydb.h"
#include "sidtab.h"
-extern struct policydb policydb;
+/* Mapping for a single class */
+struct selinux_mapping {
+ u16 value; /* policy value for class */
+ unsigned int num_perms; /* number of permissions in class */
+ u32 perms[sizeof(u32) * 8]; /* policy values for permissions */
+};
+
+/* Map for all of the classes, with array size */
+struct selinux_map {
+ struct selinux_mapping *mapping; /* indexed by class */
+ u16 size; /* array size of mapping */
+};
+
+struct selinux_ss {
+ struct sidtab sidtab;
+ struct policydb policydb;
+ rwlock_t policy_rwlock;
+ u32 latest_granting;
+ struct selinux_map map;
+ struct page *status_page;
+ struct mutex status_lock;
+};
void services_compute_xperms_drivers(struct extended_perms *xperms,
struct avtab_node *node);
@@ -19,4 +40,3 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
struct avtab_node *node);
#endif /* _SS_SERVICES_H_ */
-
diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c
index d982365f9d1a..a121de45ac0e 100644
--- a/security/selinux/ss/status.c
+++ b/security/selinux/ss/status.c
@@ -35,8 +35,6 @@
* In most cases, application shall confirm the kernel status is not
* changed without any system call invocations.
*/
-static struct page *selinux_status_page;
-static DEFINE_MUTEX(selinux_status_lock);
/*
* selinux_kernel_status_page
@@ -44,21 +42,21 @@ static DEFINE_MUTEX(selinux_status_lock);
* It returns a reference to selinux_status_page. If the status page is
* not allocated yet, it also tries to allocate it at the first time.
*/
-struct page *selinux_kernel_status_page(void)
+struct page *selinux_kernel_status_page(struct selinux_state *state)
{
struct selinux_kernel_status *status;
struct page *result = NULL;
- mutex_lock(&selinux_status_lock);
- if (!selinux_status_page) {
- selinux_status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
+ mutex_lock(&state->ss->status_lock);
+ if (!state->ss->status_page) {
+ state->ss->status_page = alloc_page(GFP_KERNEL|__GFP_ZERO);
- if (selinux_status_page) {
- status = page_address(selinux_status_page);
+ if (state->ss->status_page) {
+ status = page_address(state->ss->status_page);
status->version = SELINUX_KERNEL_STATUS_VERSION;
status->sequence = 0;
- status->enforcing = selinux_enforcing;
+ status->enforcing = enforcing_enabled(state);
/*
* NOTE: the next policyload event shall set
* a positive value on the status->policyload,
@@ -66,11 +64,12 @@ struct page *selinux_kernel_status_page(void)
* So, application can know it was updated.
*/
status->policyload = 0;
- status->deny_unknown = !security_get_allow_unknown();
+ status->deny_unknown =
+ !security_get_allow_unknown(state);
}
}
- result = selinux_status_page;
- mutex_unlock(&selinux_status_lock);
+ result = state->ss->status_page;
+ mutex_unlock(&state->ss->status_lock);
return result;
}
@@ -80,13 +79,14 @@ struct page *selinux_kernel_status_page(void)
*
* It updates status of the current enforcing/permissive mode.
*/
-void selinux_status_update_setenforce(int enforcing)
+void selinux_status_update_setenforce(struct selinux_state *state,
+ int enforcing)
{
struct selinux_kernel_status *status;
- mutex_lock(&selinux_status_lock);
- if (selinux_status_page) {
- status = page_address(selinux_status_page);
+ mutex_lock(&state->ss->status_lock);
+ if (state->ss->status_page) {
+ status = page_address(state->ss->status_page);
status->sequence++;
smp_wmb();
@@ -96,7 +96,7 @@ void selinux_status_update_setenforce(int enforcing)
smp_wmb();
status->sequence++;
}
- mutex_unlock(&selinux_status_lock);
+ mutex_unlock(&state->ss->status_lock);
}
/*
@@ -105,22 +105,23 @@ void selinux_status_update_setenforce(int enforcing)
* It updates status of the times of policy reloaded, and current
* setting of deny_unknown.
*/
-void selinux_status_update_policyload(int seqno)
+void selinux_status_update_policyload(struct selinux_state *state,
+ int seqno)
{
struct selinux_kernel_status *status;
- mutex_lock(&selinux_status_lock);
- if (selinux_status_page) {
- status = page_address(selinux_status_page);
+ mutex_lock(&state->ss->status_lock);
+ if (state->ss->status_page) {
+ status = page_address(state->ss->status_page);
status->sequence++;
smp_wmb();
status->policyload = seqno;
- status->deny_unknown = !security_get_allow_unknown();
+ status->deny_unknown = !security_get_allow_unknown(state);
smp_wmb();
status->sequence++;
}
- mutex_unlock(&selinux_status_lock);
+ mutex_unlock(&state->ss->status_lock);
}
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 56e354fcdfc6..91dc3783ed94 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -101,11 +101,13 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
ctx->ctx_len = str_len;
memcpy(ctx->ctx_str, &uctx[1], str_len);
ctx->ctx_str[str_len] = '\0';
- rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid, gfp);
+ rc = security_context_to_sid(&selinux_state, ctx->ctx_str, str_len,
+ &ctx->ctx_sid, gfp);
if (rc)
goto err;
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(&selinux_state,
+ tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
if (rc)
goto err;
@@ -141,7 +143,8 @@ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
if (!ctx)
return 0;
- return avc_has_perm(tsec->sid, ctx->ctx_sid,
+ return avc_has_perm(&selinux_state,
+ tsec->sid, ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
NULL);
}
@@ -163,7 +166,8 @@ int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
if (!selinux_authorizable_ctx(ctx))
return -EINVAL;
- rc = avc_has_perm(fl_secid, ctx->ctx_sid,
+ rc = avc_has_perm(&selinux_state,
+ fl_secid, ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL);
return (rc == -EACCES ? -ESRCH : rc);
}
@@ -202,7 +206,8 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
/* We don't need a separate SA Vs. policy polmatch check since the SA
* is now of the same label as the flow and a flow Vs. policy polmatch
* check had already happened in selinux_xfrm_policy_lookup() above. */
- return (avc_has_perm(fl->flowi_secid, state_sid,
+ return (avc_has_perm(&selinux_state,
+ fl->flowi_secid, state_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
NULL) ? 0 : 1);
}
@@ -352,7 +357,8 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x,
if (secid == 0)
return -EINVAL;
- rc = security_sid_to_context(secid, &ctx_str, &str_len);
+ rc = security_sid_to_context(&selinux_state, secid, &ctx_str,
+ &str_len);
if (rc)
return rc;
@@ -420,7 +426,8 @@ int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb,
/* This check even when there's no association involved is intended,
* according to Trent Jaeger, to make sure a process can't engage in
* non-IPsec communication unless explicitly allowed by policy. */
- return avc_has_perm(sk_sid, peer_sid,
+ return avc_has_perm(&selinux_state,
+ sk_sid, peer_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);
}
@@ -452,7 +459,7 @@ int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
if (dst) {
struct dst_entry *iter;
- for (iter = dst; iter != NULL; iter = iter->child) {
+ for (iter = dst; iter != NULL; iter = xfrm_dst_child(iter)) {
struct xfrm_state *x = iter->xfrm;
if (x && selinux_authorizable_xfrm(x))
@@ -463,6 +470,6 @@ int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb,
/* This check even when there's no association involved is intended,
* according to Trent Jaeger, to make sure a process can't engage in
* non-IPsec communication unless explicitly allowed by policy. */
- return avc_has_perm(sk_sid, SECINITSID_UNLABELED,
+ return avc_has_perm(&selinux_state, sk_sid, SECINITSID_UNLABELED,
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);
}
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 6a71fc7831ab..f7db791fb566 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -321,6 +321,7 @@ struct smack_known *smk_import_entry(const char *, int);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
bool smack_privileged(int cap);
+bool smack_privileged_cred(int cap, const struct cred *cred);
void smk_destroy_label_list(struct list_head *list);
/*
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 1a3004189447..9a4c0ad46518 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -623,26 +623,24 @@ struct smack_known *smack_from_secid(const u32 secid)
LIST_HEAD(smack_onlycap_list);
DEFINE_MUTEX(smack_onlycap_lock);
-/*
+/**
+ * smack_privileged_cred - are all privilege requirements met by cred
+ * @cap: The requested capability
+ * @cred: the credential to use
+ *
* Is the task privileged and allowed to be privileged
* by the onlycap rule.
*
* Returns true if the task is allowed to be privileged, false if it's not.
*/
-bool smack_privileged(int cap)
+bool smack_privileged_cred(int cap, const struct cred *cred)
{
- struct smack_known *skp = smk_of_current();
+ struct task_smack *tsp = cred->security;
+ struct smack_known *skp = tsp->smk_task;
struct smack_known_list_elem *sklep;
int rc;
- /*
- * All kernel tasks are privileged
- */
- if (unlikely(current->flags & PF_KTHREAD))
- return true;
-
- rc = cap_capable(current_cred(), &init_user_ns, cap,
- SECURITY_CAP_AUDIT);
+ rc = cap_capable(cred, &init_user_ns, cap, SECURITY_CAP_AUDIT);
if (rc)
return false;
@@ -662,3 +660,23 @@ bool smack_privileged(int cap)
return false;
}
+
+/**
+ * smack_privileged - are all privilege requirements met
+ * @cap: The requested capability
+ *
+ * Is the task privileged and allowed to be privileged
+ * by the onlycap rule.
+ *
+ * Returns true if the task is allowed to be privileged, false if it's not.
+ */
+bool smack_privileged(int cap)
+{
+ /*
+ * All kernel tasks are privileged
+ */
+ if (unlikely(current->flags & PF_KTHREAD))
+ return true;
+
+ return smack_privileged_cred(cap, current_cred());
+}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 14cc7940b36d..0b414836bebd 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2050,6 +2050,23 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
}
/**
+ * smack_cred_getsecid - get the secid corresponding to a creds structure
+ * @c: the object creds
+ * @secid: where to put the result
+ *
+ * Sets the secid to contain a u32 version of the smack label.
+ */
+static void smack_cred_getsecid(const struct cred *c, u32 *secid)
+{
+ struct smack_known *skp;
+
+ rcu_read_lock();
+ skp = smk_of_task(c->security);
+ *secid = skp->smk_secid;
+ rcu_read_unlock();
+}
+
+/**
* smack_kernel_act_as - Set the subjective context in a set of credentials
* @new: points to the set of credentials to be modified.
* @secid: specifies the security ID to be set
@@ -2228,15 +2245,13 @@ static int smack_task_movememory(struct task_struct *p)
* @p: the task object
* @info: unused
* @sig: unused
- * @secid: identifies the smack to use in lieu of current's
+ * @cred: identifies the cred to use in lieu of current's
*
* Return 0 if write access is permitted
*
- * The secid behavior is an artifact of an SELinux hack
- * in the USB code. Someday it may go away.
*/
static int smack_task_kill(struct task_struct *p, struct siginfo *info,
- int sig, u32 secid)
+ int sig, const struct cred *cred)
{
struct smk_audit_info ad;
struct smack_known *skp;
@@ -2252,17 +2267,17 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
* Sending a signal requires that the sender
* can write the receiver.
*/
- if (secid == 0) {
+ if (cred == NULL) {
rc = smk_curacc(tkp, MAY_DELIVER, &ad);
rc = smk_bu_task(p, MAY_DELIVER, rc);
return rc;
}
/*
- * If the secid isn't 0 we're dealing with some USB IO
+ * If the cred isn't NULL we're dealing with some USB IO
* specific behavior. This is not clean. For one thing
* we can't take privilege into account.
*/
- skp = smack_from_secid(secid);
+ skp = smk_of_task(cred->security);
rc = smk_access(skp, tkp, MAY_DELIVER, &ad);
rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc);
return rc;
@@ -2866,12 +2881,16 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
#endif
#ifdef SMACK_IPV6_SECMARK_LABELING
struct smack_known *rsp;
- struct socket_smack *ssp = sock->sk->sk_security;
+ struct socket_smack *ssp;
#endif
if (sock->sk == NULL)
return 0;
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ ssp = sock->sk->sk_security;
+#endif
+
switch (sock->sk->sk_family) {
case PF_INET:
if (addrlen < sizeof(struct sockaddr_in))
@@ -2941,25 +2960,24 @@ static void smack_msg_msg_free_security(struct msg_msg *msg)
}
/**
- * smack_of_shm - the smack pointer for the shm
- * @shp: the object
+ * smack_of_ipc - the smack pointer for the ipc
+ * @isp: the object
*
* Returns a pointer to the smack value
*/
-static struct smack_known *smack_of_shm(struct shmid_kernel *shp)
+static struct smack_known *smack_of_ipc(struct kern_ipc_perm *isp)
{
- return (struct smack_known *)shp->shm_perm.security;
+ return (struct smack_known *)isp->security;
}
/**
- * smack_shm_alloc_security - Set the security blob for shm
- * @shp: the object
+ * smack_ipc_alloc_security - Set the security blob for ipc
+ * @isp: the object
*
* Returns 0
*/
-static int smack_shm_alloc_security(struct shmid_kernel *shp)
+static int smack_ipc_alloc_security(struct kern_ipc_perm *isp)
{
- struct kern_ipc_perm *isp = &shp->shm_perm;
struct smack_known *skp = smk_of_current();
isp->security = skp;
@@ -2967,34 +2985,32 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp)
}
/**
- * smack_shm_free_security - Clear the security blob for shm
- * @shp: the object
+ * smack_ipc_free_security - Clear the security blob for ipc
+ * @isp: the object
*
* Clears the blob pointer
*/
-static void smack_shm_free_security(struct shmid_kernel *shp)
+static void smack_ipc_free_security(struct kern_ipc_perm *isp)
{
- struct kern_ipc_perm *isp = &shp->shm_perm;
-
isp->security = NULL;
}
/**
* smk_curacc_shm : check if current has access on shm
- * @shp : the object
+ * @isp : the object
* @access : access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smk_curacc_shm(struct shmid_kernel *shp, int access)
+static int smk_curacc_shm(struct kern_ipc_perm *isp, int access)
{
- struct smack_known *ssp = smack_of_shm(shp);
+ struct smack_known *ssp = smack_of_ipc(isp);
struct smk_audit_info ad;
int rc;
#ifdef CONFIG_AUDIT
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
- ad.a.u.ipc_id = shp->shm_perm.id;
+ ad.a.u.ipc_id = isp->id;
#endif
rc = smk_curacc(ssp, access, &ad);
rc = smk_bu_current("shm", ssp, access, rc);
@@ -3003,33 +3019,34 @@ static int smk_curacc_shm(struct shmid_kernel *shp, int access)
/**
* smack_shm_associate - Smack access check for shm
- * @shp: the object
+ * @isp: the object
* @shmflg: access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_shm_associate(struct shmid_kernel *shp, int shmflg)
+static int smack_shm_associate(struct kern_ipc_perm *isp, int shmflg)
{
int may;
may = smack_flags_to_may(shmflg);
- return smk_curacc_shm(shp, may);
+ return smk_curacc_shm(isp, may);
}
/**
* smack_shm_shmctl - Smack access check for shm
- * @shp: the object
+ * @isp: the object
* @cmd: what it wants to do
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
+static int smack_shm_shmctl(struct kern_ipc_perm *isp, int cmd)
{
int may;
switch (cmd) {
case IPC_STAT:
case SHM_STAT:
+ case SHM_STAT_ANY:
may = MAY_READ;
break;
case IPC_SET:
@@ -3047,81 +3064,42 @@ static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
default:
return -EINVAL;
}
- return smk_curacc_shm(shp, may);
+ return smk_curacc_shm(isp, may);
}
/**
* smack_shm_shmat - Smack access for shmat
- * @shp: the object
+ * @isp: the object
* @shmaddr: unused
* @shmflg: access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
+static int smack_shm_shmat(struct kern_ipc_perm *ipc, char __user *shmaddr,
int shmflg)
{
int may;
may = smack_flags_to_may(shmflg);
- return smk_curacc_shm(shp, may);
-}
-
-/**
- * smack_of_sem - the smack pointer for the sem
- * @sma: the object
- *
- * Returns a pointer to the smack value
- */
-static struct smack_known *smack_of_sem(struct sem_array *sma)
-{
- return (struct smack_known *)sma->sem_perm.security;
-}
-
-/**
- * smack_sem_alloc_security - Set the security blob for sem
- * @sma: the object
- *
- * Returns 0
- */
-static int smack_sem_alloc_security(struct sem_array *sma)
-{
- struct kern_ipc_perm *isp = &sma->sem_perm;
- struct smack_known *skp = smk_of_current();
-
- isp->security = skp;
- return 0;
-}
-
-/**
- * smack_sem_free_security - Clear the security blob for sem
- * @sma: the object
- *
- * Clears the blob pointer
- */
-static void smack_sem_free_security(struct sem_array *sma)
-{
- struct kern_ipc_perm *isp = &sma->sem_perm;
-
- isp->security = NULL;
+ return smk_curacc_shm(ipc, may);
}
/**
* smk_curacc_sem : check if current has access on sem
- * @sma : the object
+ * @isp : the object
* @access : access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smk_curacc_sem(struct sem_array *sma, int access)
+static int smk_curacc_sem(struct kern_ipc_perm *isp, int access)
{
- struct smack_known *ssp = smack_of_sem(sma);
+ struct smack_known *ssp = smack_of_ipc(isp);
struct smk_audit_info ad;
int rc;
#ifdef CONFIG_AUDIT
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
- ad.a.u.ipc_id = sma->sem_perm.id;
+ ad.a.u.ipc_id = isp->id;
#endif
rc = smk_curacc(ssp, access, &ad);
rc = smk_bu_current("sem", ssp, access, rc);
@@ -3130,27 +3108,27 @@ static int smk_curacc_sem(struct sem_array *sma, int access)
/**
* smack_sem_associate - Smack access check for sem
- * @sma: the object
+ * @isp: the object
* @semflg: access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_sem_associate(struct sem_array *sma, int semflg)
+static int smack_sem_associate(struct kern_ipc_perm *isp, int semflg)
{
int may;
may = smack_flags_to_may(semflg);
- return smk_curacc_sem(sma, may);
+ return smk_curacc_sem(isp, may);
}
/**
* smack_sem_shmctl - Smack access check for sem
- * @sma: the object
+ * @isp: the object
* @cmd: what it wants to do
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_sem_semctl(struct sem_array *sma, int cmd)
+static int smack_sem_semctl(struct kern_ipc_perm *isp, int cmd)
{
int may;
@@ -3162,6 +3140,7 @@ static int smack_sem_semctl(struct sem_array *sma, int cmd)
case GETALL:
case IPC_STAT:
case SEM_STAT:
+ case SEM_STAT_ANY:
may = MAY_READ;
break;
case SETVAL:
@@ -3180,12 +3159,12 @@ static int smack_sem_semctl(struct sem_array *sma, int cmd)
return -EINVAL;
}
- return smk_curacc_sem(sma, may);
+ return smk_curacc_sem(isp, may);
}
/**
* smack_sem_semop - Smack checks of semaphore operations
- * @sma: the object
+ * @isp: the object
* @sops: unused
* @nsops: unused
* @alter: unused
@@ -3194,67 +3173,28 @@ static int smack_sem_semctl(struct sem_array *sma, int cmd)
*
* Returns 0 if access is allowed, error code otherwise
*/
-static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops,
+static int smack_sem_semop(struct kern_ipc_perm *isp, struct sembuf *sops,
unsigned nsops, int alter)
{
- return smk_curacc_sem(sma, MAY_READWRITE);
-}
-
-/**
- * smack_msg_alloc_security - Set the security blob for msg
- * @msq: the object
- *
- * Returns 0
- */
-static int smack_msg_queue_alloc_security(struct msg_queue *msq)
-{
- struct kern_ipc_perm *kisp = &msq->q_perm;
- struct smack_known *skp = smk_of_current();
-
- kisp->security = skp;
- return 0;
-}
-
-/**
- * smack_msg_free_security - Clear the security blob for msg
- * @msq: the object
- *
- * Clears the blob pointer
- */
-static void smack_msg_queue_free_security(struct msg_queue *msq)
-{
- struct kern_ipc_perm *kisp = &msq->q_perm;
-
- kisp->security = NULL;
-}
-
-/**
- * smack_of_msq - the smack pointer for the msq
- * @msq: the object
- *
- * Returns a pointer to the smack label entry
- */
-static struct smack_known *smack_of_msq(struct msg_queue *msq)
-{
- return (struct smack_known *)msq->q_perm.security;
+ return smk_curacc_sem(isp, MAY_READWRITE);
}
/**
* smk_curacc_msq : helper to check if current has access on msq
- * @msq : the msq
+ * @isp : the msq
* @access : access requested
*
* return 0 if current has access, error otherwise
*/
-static int smk_curacc_msq(struct msg_queue *msq, int access)
+static int smk_curacc_msq(struct kern_ipc_perm *isp, int access)
{
- struct smack_known *msp = smack_of_msq(msq);
+ struct smack_known *msp = smack_of_ipc(isp);
struct smk_audit_info ad;
int rc;
#ifdef CONFIG_AUDIT
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC);
- ad.a.u.ipc_id = msq->q_perm.id;
+ ad.a.u.ipc_id = isp->id;
#endif
rc = smk_curacc(msp, access, &ad);
rc = smk_bu_current("msq", msp, access, rc);
@@ -3263,33 +3203,34 @@ static int smk_curacc_msq(struct msg_queue *msq, int access)
/**
* smack_msg_queue_associate - Smack access check for msg_queue
- * @msq: the object
+ * @isp: the object
* @msqflg: access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_msg_queue_associate(struct msg_queue *msq, int msqflg)
+static int smack_msg_queue_associate(struct kern_ipc_perm *isp, int msqflg)
{
int may;
may = smack_flags_to_may(msqflg);
- return smk_curacc_msq(msq, may);
+ return smk_curacc_msq(isp, may);
}
/**
* smack_msg_queue_msgctl - Smack access check for msg_queue
- * @msq: the object
+ * @isp: the object
* @cmd: what it wants to do
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+static int smack_msg_queue_msgctl(struct kern_ipc_perm *isp, int cmd)
{
int may;
switch (cmd) {
case IPC_STAT:
case MSG_STAT:
+ case MSG_STAT_ANY:
may = MAY_READ;
break;
case IPC_SET:
@@ -3306,29 +3247,29 @@ static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
return -EINVAL;
}
- return smk_curacc_msq(msq, may);
+ return smk_curacc_msq(isp, may);
}
/**
* smack_msg_queue_msgsnd - Smack access check for msg_queue
- * @msq: the object
+ * @isp: the object
* @msg: unused
* @msqflg: access requested
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
+static int smack_msg_queue_msgsnd(struct kern_ipc_perm *isp, struct msg_msg *msg,
int msqflg)
{
int may;
may = smack_flags_to_may(msqflg);
- return smk_curacc_msq(msq, may);
+ return smk_curacc_msq(isp, may);
}
/**
* smack_msg_queue_msgsnd - Smack access check for msg_queue
- * @msq: the object
+ * @isp: the object
* @msg: unused
* @target: unused
* @type: unused
@@ -3336,10 +3277,10 @@ static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
*
* Returns 0 if current has read and write access, error code otherwise
*/
-static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, struct msg_msg *msg,
struct task_struct *target, long type, int mode)
{
- return smk_curacc_msq(msq, MAY_READWRITE);
+ return smk_curacc_msq(isp, MAY_READWRITE);
}
/**
@@ -3427,6 +3368,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
if (opt_dentry->d_parent == opt_dentry) {
switch (sbp->s_magic) {
case CGROUP_SUPER_MAGIC:
+ case CGROUP2_SUPER_MAGIC:
/*
* The cgroup filesystem is never mounted,
* so there's no opportunity to set the mount
@@ -3470,6 +3412,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
switch (sbp->s_magic) {
case SMACK_MAGIC:
case CGROUP_SUPER_MAGIC:
+ case CGROUP2_SUPER_MAGIC:
/*
* Casey says that it's a little embarrassing
* that the smack file system doesn't do
@@ -4365,6 +4308,10 @@ static int smack_key_permission(key_ref_t key_ref,
*/
if (tkp == NULL)
return -EACCES;
+
+ if (smack_privileged_cred(CAP_MAC_OVERRIDE, cred))
+ return 0;
+
#ifdef CONFIG_AUDIT
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY);
ad.a.u.key_struct.key = keyp->serial;
@@ -4727,6 +4674,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(cred_free, smack_cred_free),
LSM_HOOK_INIT(cred_prepare, smack_cred_prepare),
LSM_HOOK_INIT(cred_transfer, smack_cred_transfer),
+ LSM_HOOK_INIT(cred_getsecid, smack_cred_getsecid),
LSM_HOOK_INIT(kernel_act_as, smack_kernel_act_as),
LSM_HOOK_INIT(kernel_create_files_as, smack_kernel_create_files_as),
LSM_HOOK_INIT(task_setpgid, smack_task_setpgid),
@@ -4748,21 +4696,21 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security),
LSM_HOOK_INIT(msg_msg_free_security, smack_msg_msg_free_security),
- LSM_HOOK_INIT(msg_queue_alloc_security, smack_msg_queue_alloc_security),
- LSM_HOOK_INIT(msg_queue_free_security, smack_msg_queue_free_security),
+ LSM_HOOK_INIT(msg_queue_alloc_security, smack_ipc_alloc_security),
+ LSM_HOOK_INIT(msg_queue_free_security, smack_ipc_free_security),
LSM_HOOK_INIT(msg_queue_associate, smack_msg_queue_associate),
LSM_HOOK_INIT(msg_queue_msgctl, smack_msg_queue_msgctl),
LSM_HOOK_INIT(msg_queue_msgsnd, smack_msg_queue_msgsnd),
LSM_HOOK_INIT(msg_queue_msgrcv, smack_msg_queue_msgrcv),
- LSM_HOOK_INIT(shm_alloc_security, smack_shm_alloc_security),
- LSM_HOOK_INIT(shm_free_security, smack_shm_free_security),
+ LSM_HOOK_INIT(shm_alloc_security, smack_ipc_alloc_security),
+ LSM_HOOK_INIT(shm_free_security, smack_ipc_free_security),
LSM_HOOK_INIT(shm_associate, smack_shm_associate),
LSM_HOOK_INIT(shm_shmctl, smack_shm_shmctl),
LSM_HOOK_INIT(shm_shmat, smack_shm_shmat),
- LSM_HOOK_INIT(sem_alloc_security, smack_sem_alloc_security),
- LSM_HOOK_INIT(sem_free_security, smack_sem_free_security),
+ LSM_HOOK_INIT(sem_alloc_security, smack_ipc_alloc_security),
+ LSM_HOOK_INIT(sem_free_security, smack_ipc_free_security),
LSM_HOOK_INIT(sem_associate, smack_sem_associate),
LSM_HOOK_INIT(sem_semctl, smack_sem_semctl),
LSM_HOOK_INIT(sem_semop, smack_sem_semop),
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
index 0f73fe30e37a..479b03a7a17e 100644
--- a/security/tomoyo/audit.c
+++ b/security/tomoyo/audit.c
@@ -456,14 +456,14 @@ void tomoyo_read_log(struct tomoyo_io_buffer *head)
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table". Maybe NULL.
*
- * Returns POLLIN | POLLRDNORM when ready to read an audit log.
+ * Returns EPOLLIN | EPOLLRDNORM when ready to read an audit log.
*/
-unsigned int tomoyo_poll_log(struct file *file, poll_table *wait)
+__poll_t tomoyo_poll_log(struct file *file, poll_table *wait)
{
if (tomoyo_log_count)
- return POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
poll_wait(file, &tomoyo_log_wait, wait);
if (tomoyo_log_count)
- return POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
return 0;
}
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 25eed4b0b0e8..03923a138ef5 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -2116,17 +2116,17 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table".
*
- * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise.
+ * Returns EPOLLIN | EPOLLRDNORM when ready to read, 0 otherwise.
*
* Waits for access requests which violated policy in enforcing mode.
*/
-static unsigned int tomoyo_poll_query(struct file *file, poll_table *wait)
+static __poll_t tomoyo_poll_query(struct file *file, poll_table *wait)
{
if (!list_empty(&tomoyo_query_list))
- return POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
poll_wait(file, &tomoyo_query_wait, wait);
if (!list_empty(&tomoyo_query_list))
- return POLLIN | POLLRDNORM;
+ return EPOLLIN | EPOLLRDNORM;
return 0;
}
@@ -2450,15 +2450,15 @@ int tomoyo_open_control(const u8 type, struct file *file)
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table". Maybe NULL.
*
- * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write,
- * POLLOUT | POLLWRNORM otherwise.
+ * Returns EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM if ready to read/write,
+ * EPOLLOUT | EPOLLWRNORM otherwise.
*/
-unsigned int tomoyo_poll_control(struct file *file, poll_table *wait)
+__poll_t tomoyo_poll_control(struct file *file, poll_table *wait)
{
struct tomoyo_io_buffer *head = file->private_data;
if (head->poll)
- return head->poll(file, wait) | POLLOUT | POLLWRNORM;
- return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
+ return head->poll(file, wait) | EPOLLOUT | EPOLLWRNORM;
+ return EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
}
/**
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 7adccdd8e36d..539bcdd30bb8 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -789,7 +789,7 @@ struct tomoyo_acl_param {
struct tomoyo_io_buffer {
void (*read) (struct tomoyo_io_buffer *);
int (*write) (struct tomoyo_io_buffer *);
- unsigned int (*poll) (struct file *file, poll_table *wait);
+ __poll_t (*poll) (struct file *file, poll_table *wait);
/* Exclusive lock for this structure. */
struct mutex io_sem;
char __user *read_user_buf;
@@ -981,8 +981,8 @@ int tomoyo_path_number_perm(const u8 operation, const struct path *path,
unsigned long number);
int tomoyo_path_perm(const u8 operation, const struct path *path,
const char *target);
-unsigned int tomoyo_poll_control(struct file *file, poll_table *wait);
-unsigned int tomoyo_poll_log(struct file *file, poll_table *wait);
+__poll_t tomoyo_poll_control(struct file *file, poll_table *wait);
+__poll_t tomoyo_poll_log(struct file *file, poll_table *wait);
int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
int addr_len);
int tomoyo_socket_connect_permission(struct socket *sock,
diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c
index cd6932e5225c..9094f4b3b367 100644
--- a/security/tomoyo/network.c
+++ b/security/tomoyo/network.c
@@ -655,10 +655,11 @@ int tomoyo_socket_listen_permission(struct socket *sock)
return 0;
{
const int error = sock->ops->getname(sock, (struct sockaddr *)
- &addr, &addr_len, 0);
+ &addr, 0);
- if (error)
+ if (error < 0)
return error;
+ addr_len = error;
}
address.protocol = type;
address.operation = TOMOYO_NETWORK_LISTEN;
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index 49393c2a3f8b..1d3d7e7a1f05 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -154,10 +154,10 @@ static int tomoyo_release(struct inode *inode, struct file *file)
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table". Maybe NULL.
*
- * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write,
- * POLLOUT | POLLWRNORM otherwise.
+ * Returns EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM if ready to read/write,
+ * EPOLLOUT | EPOLLWRNORM otherwise.
*/
-static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
+static __poll_t tomoyo_poll(struct file *file, poll_table *wait)
{
return tomoyo_poll_control(file, wait);
}
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 8298e094f4f7..ffda91a4a1aa 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -250,15 +250,10 @@ int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
} else {
struct task_struct *tracer;
- rcu_read_lock();
- tracer = find_task_by_vpid(arg2);
- if (tracer)
- get_task_struct(tracer);
- else
+ tracer = find_get_task_by_vpid(arg2);
+ if (!tracer) {
rc = -EINVAL;
- rcu_read_unlock();
-
- if (tracer) {
+ } else {
rc = yama_ptracer_add(tracer, myself);
put_task_struct(tracer);
}