summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-05-19 18:21:36 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2016-05-19 18:21:36 +0200
commitf4f27d0028aabce57e44c16c2fdefccd6310d2f3 (patch)
tree09f25601316d22b64165c19042da51c101bde3c4 /security
parentMerge tag 'trace-v4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/roste... (diff)
parentLSM: LoadPin: provide enablement CONFIG (diff)
downloadlinux-f4f27d0028aabce57e44c16c2fdefccd6310d2f3.tar.xz
linux-f4f27d0028aabce57e44c16c2fdefccd6310d2f3.zip
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: - A new LSM, "LoadPin", from Kees Cook is added, which allows forcing of modules and firmware to be loaded from a specific device (this is from ChromeOS, where the device as a whole is verified cryptographically via dm-verity). This is disabled by default but can be configured to be enabled by default (don't do this if you don't know what you're doing). - Keys: allow authentication data to be stored in an asymmetric key. Lots of general fixes and updates. - SELinux: add restrictions for loading of kernel modules via finit_module(). Distinguish non-init user namespace capability checks. Apply execstack check on thread stacks" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (48 commits) LSM: LoadPin: provide enablement CONFIG Yama: use atomic allocations when reporting seccomp: Fix comment typo ima: add support for creating files using the mknodat syscall ima: fix ima_inode_post_setattr vfs: forbid write access when reading a file into memory fs: fix over-zealous use of "const" selinux: apply execstack check on thread stacks selinux: distinguish non-init user namespace capability checks LSM: LoadPin for kernel file loading restrictions fs: define a string representation of the kernel_read_file_id enumeration Yama: consolidate error reporting string_helpers: add kstrdup_quotable_file string_helpers: add kstrdup_quotable_cmdline string_helpers: add kstrdup_quotable selinux: check ss_initialized before revalidating an inode label selinux: delay inode label lookup as long as possible selinux: don't revalidate an inode's label when explicitly setting it selinux: Change bool variable name to index. KEYS: Add KEYCTL_DH_COMPUTE command ...
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig1
-rw-r--r--security/Makefile2
-rw-r--r--security/integrity/Kconfig1
-rw-r--r--security/integrity/digsig.c15
-rw-r--r--security/integrity/ima/Kconfig36
-rw-r--r--security/integrity/ima/Makefile2
-rw-r--r--security/integrity/ima/ima_appraise.c7
-rw-r--r--security/integrity/ima/ima_main.c25
-rw-r--r--security/integrity/ima/ima_mok.c23
-rw-r--r--security/integrity/integrity.h1
-rw-r--r--security/keys/Kconfig15
-rw-r--r--security/keys/Makefile1
-rw-r--r--security/keys/big_key.c198
-rw-r--r--security/keys/compat.c4
-rw-r--r--security/keys/dh.c160
-rw-r--r--security/keys/internal.h12
-rw-r--r--security/keys/key.c42
-rw-r--r--security/keys/keyctl.c5
-rw-r--r--security/keys/keyring.c46
-rw-r--r--security/keys/persistent.c4
-rw-r--r--security/keys/process_keys.c16
-rw-r--r--security/keys/request_key.c4
-rw-r--r--security/keys/request_key_auth.c2
-rw-r--r--security/keys/user_defined.c42
-rw-r--r--security/loadpin/Kconfig19
-rw-r--r--security/loadpin/Makefile1
-rw-r--r--security/loadpin/loadpin.c190
-rw-r--r--security/security.c2
-rw-r--r--security/selinux/hooks.c144
-rw-r--r--security/selinux/include/classmap.h30
-rw-r--r--security/selinux/include/conditional.h2
-rw-r--r--security/selinux/include/objsec.h5
-rw-r--r--security/selinux/ss/services.c6
-rw-r--r--security/yama/yama_lsm.c31
34 files changed, 909 insertions, 185 deletions
diff --git a/security/Kconfig b/security/Kconfig
index e45237897b43..176758cdfa57 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -122,6 +122,7 @@ source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
source security/apparmor/Kconfig
+source security/loadpin/Kconfig
source security/yama/Kconfig
source security/integrity/Kconfig
diff --git a/security/Makefile b/security/Makefile
index c9bfbc84ff50..f2d71cdb8e19 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
+subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
# always enable default capabilities
obj-y += commoncap.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
+obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index 979be65d22c4..da9565891738 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS
default n
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
- select PUBLIC_KEY_ALGO_RSA
select CRYPTO_RSA
select X509_CERTIFICATE_PARSER
help
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 8ef15118cc78..4304372b323f 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -18,6 +18,8 @@
#include <linux/cred.h>
#include <linux/key-type.h>
#include <linux/digsig.h>
+#include <crypto/public_key.h>
+#include <keys/system_keyring.h>
#include "integrity.h"
@@ -40,6 +42,12 @@ static bool init_keyring __initdata = true;
static bool init_keyring __initdata;
#endif
+#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
+#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
+#else
+#define restrict_link_to_ima restrict_link_by_builtin_trusted
+#endif
+
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen)
{
@@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id)
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH),
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
- if (!IS_ERR(keyring[id]))
- set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
- else {
+ KEY_ALLOC_NOT_IN_QUOTA,
+ restrict_link_to_ima, NULL);
+ if (IS_ERR(keyring[id])) {
err = PTR_ERR(keyring[id]);
pr_info("Can't allocate %s keyring (%d)\n",
keyring_name[id], err);
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index e54a8a8dae94..5487827fa86c 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING
This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING
-config IMA_MOK_KEYRING
- bool "Create IMA machine owner keys (MOK) and blacklist keyrings"
+config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
+ bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)"
+ depends on SYSTEM_TRUSTED_KEYRING
+ depends on SECONDARY_TRUSTED_KEYRING
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ select INTEGRITY_TRUSTED_KEYRING
+ default n
+ help
+ Keys may be added to the IMA or IMA blacklist keyrings, if the
+ key is validly signed by a CA cert in the system built-in or
+ secondary trusted keyrings.
+
+ Intermediate keys between those the kernel has compiled in and the
+ IMA keys to be added may be added to the system secondary keyring,
+ provided they are validly signed by a key already resident in the
+ built-in or secondary trusted keyrings.
+
+config IMA_BLACKLIST_KEYRING
+ bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)"
depends on SYSTEM_TRUSTED_KEYRING
depends on IMA_TRUSTED_KEYRING
default n
help
- This option creates IMA MOK and blacklist keyrings. IMA MOK is an
- intermediate keyring that sits between .system and .ima keyrings,
- effectively forming a simple CA hierarchy. To successfully import a
- key into .ima_mok it must be signed by a key which CA is in .system
- keyring. On turn any key that needs to go in .ima keyring must be
- signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty
- at kernel boot.
-
- IMA blacklist keyring contains all revoked IMA keys. It is consulted
- before any other keyring. If the search is successful the requested
- operation is rejected and error is returned to the caller.
+ This option creates an IMA blacklist keyring, which contains all
+ revoked IMA keys. It is consulted before any other keyring. If
+ the search is successful the requested operation is rejected and
+ an error is returned to the caller.
config IMA_LOAD_X509
bool "Load X509 certificate onto the '.ima' trusted keyring"
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index a8539f9e060f..9aeaedad1e2b 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
-obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o
+obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 6b4694aedae8..1bcbc12e03d9 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -275,6 +275,11 @@ out:
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)) {
+ status = INTEGRITY_PASS;
}
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
op, cause, rc, 0);
@@ -328,7 +333,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
if (iint) {
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
- IMA_ACTION_FLAGS);
+ IMA_ACTION_RULE_FLAGS);
if (must_appraise)
iint->flags |= IMA_APPRAISE;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 391f41751021..68b26c340acd 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -246,7 +246,8 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
ima_audit_measurement(iint, pathname);
out_digsig:
- if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
+ if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
+ !(iint->flags & IMA_NEW_FILE))
rc = -EACCES;
kfree(xattr_value);
out_free:
@@ -316,6 +317,28 @@ int ima_file_check(struct file *file, int mask, int opened)
EXPORT_SYMBOL_GPL(ima_file_check);
/**
+ * ima_post_path_mknod - mark as a new inode
+ * @dentry: newly created dentry
+ *
+ * Mark files created via the mknodat syscall as new, so that the
+ * file data can be written later.
+ */
+void ima_post_path_mknod(struct dentry *dentry)
+{
+ struct integrity_iint_cache *iint;
+ struct inode *inode = dentry->d_inode;
+ int must_appraise;
+
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+ if (!must_appraise)
+ return;
+
+ iint = integrity_inode_get(inode);
+ if (iint)
+ iint->flags |= IMA_NEW_FILE;
+}
+
+/**
* ima_read_file - pre-measure/appraise hook decision based on policy
* @file: pointer to the file to be measured/appraised/audit
* @read_id: caller identifier
diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c
index 676885e4320e..74a279957464 100644
--- a/security/integrity/ima/ima_mok.c
+++ b/security/integrity/ima/ima_mok.c
@@ -17,38 +17,29 @@
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/init.h>
-#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
-struct key *ima_mok_keyring;
struct key *ima_blacklist_keyring;
/*
- * Allocate the IMA MOK and blacklist keyrings
+ * Allocate the IMA blacklist keyring
*/
__init int ima_mok_init(void)
{
- pr_notice("Allocating IMA MOK and blacklist keyrings.\n");
-
- ima_mok_keyring = keyring_alloc(".ima_mok",
- KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
- (KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ |
- KEY_USR_WRITE | KEY_USR_SEARCH,
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ pr_notice("Allocating IMA blacklist keyring.\n");
ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ |
KEY_USR_WRITE | KEY_USR_SEARCH,
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ KEY_ALLOC_NOT_IN_QUOTA,
+ restrict_link_by_builtin_trusted, NULL);
- if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring))
- panic("Can't allocate IMA MOK or blacklist keyrings.");
- set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags);
+ if (IS_ERR(ima_blacklist_keyring))
+ panic("Can't allocate IMA blacklist keyring.");
- set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags);
set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags);
return 0;
}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index e08935cf343f..90bc57d796ec 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -28,6 +28,7 @@
/* 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
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index fe4d74e126a7..f826e8739023 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -41,6 +41,10 @@ config BIG_KEYS
bool "Large payload keys"
depends on KEYS
depends on TMPFS
+ select CRYPTO
+ select CRYPTO_AES
+ select CRYPTO_ECB
+ select CRYPTO_RNG
help
This option provides support for holding large keys within the kernel
(for example Kerberos ticket caches). The data may be stored out to
@@ -81,3 +85,14 @@ config ENCRYPTED_KEYS
Userspace only ever sees/stores encrypted blobs.
If you are unsure as to whether this is required, answer N.
+
+config KEY_DH_OPERATIONS
+ bool "Diffie-Hellman operations on retained keys"
+ depends on KEYS
+ select MPILIB
+ help
+ This option provides support for calculating Diffie-Hellman
+ public keys and shared secrets using values stored as keys
+ in the kernel.
+
+ If you are unsure as to whether this is required, answer N.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index dfb3a7bededf..1fd4a16e6daf 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
+obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
#
# Key types
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index c721e398893a..9e443fccad4c 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -14,8 +14,10 @@
#include <linux/file.h>
#include <linux/shmem_fs.h>
#include <linux/err.h>
+#include <linux/scatterlist.h>
#include <keys/user-type.h>
#include <keys/big_key-type.h>
+#include <crypto/rng.h>
/*
* Layout of key payload words.
@@ -28,6 +30,14 @@ enum {
};
/*
+ * Crypto operation with big_key data
+ */
+enum big_key_op {
+ BIG_KEY_ENC,
+ BIG_KEY_DEC,
+};
+
+/*
* If the data is under this limit, there's no point creating a shm file to
* hold it as the permanently resident metadata for the shmem fs will be at
* least as large as the data.
@@ -35,6 +45,11 @@ enum {
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
/*
+ * Key size for big_key data encryption
+ */
+#define ENC_KEY_SIZE 16
+
+/*
* big_key defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload
*/
@@ -50,12 +65,62 @@ struct key_type key_type_big_key = {
};
/*
+ * Crypto names for big_key data encryption
+ */
+static const char big_key_rng_name[] = "stdrng";
+static const char big_key_alg_name[] = "ecb(aes)";
+
+/*
+ * Crypto algorithms for big_key data encryption
+ */
+static struct crypto_rng *big_key_rng;
+static struct crypto_blkcipher *big_key_blkcipher;
+
+/*
+ * Generate random key to encrypt big_key data
+ */
+static inline int big_key_gen_enckey(u8 *key)
+{
+ return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
+}
+
+/*
+ * Encrypt/decrypt big_key data
+ */
+static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
+{
+ int ret = -EINVAL;
+ struct scatterlist sgio;
+ struct blkcipher_desc desc;
+
+ if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ desc.flags = 0;
+ desc.tfm = big_key_blkcipher;
+
+ sg_init_one(&sgio, data, datalen);
+
+ if (op == BIG_KEY_ENC)
+ ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen);
+ else
+ ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen);
+
+error:
+ return ret;
+}
+
+/*
* Preparse a big key
*/
int big_key_preparse(struct key_preparsed_payload *prep)
{
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;
int ret;
@@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Create a shmem file to store the data in. This will permit the data
* to be swapped out if needed.
*
- * TODO: Encrypt the stored data with a temporary key.
+ * File content is stored encrypted with randomly generated key.
*/
- file = shmem_kernel_file_setup("", datalen, 0);
+ size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
+
+ /* prepare aligned data to encrypt */
+ data = kmalloc(enclen, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(data, prep->data, datalen);
+ memset(data + datalen, 0x00, enclen - datalen);
+
+ /* generate random key */
+ enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
+ if (!enckey) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ret = big_key_gen_enckey(enckey);
+ if (ret)
+ goto err_enckey;
+
+ /* encrypt aligned data */
+ ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
+ if (ret)
+ goto err_enckey;
+
+ /* save aligned data to file */
+ file = shmem_kernel_file_setup("", enclen, 0);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
- goto error;
+ goto err_enckey;
}
- written = kernel_write(file, prep->data, prep->datalen, 0);
- if (written != datalen) {
+ written = kernel_write(file, data, enclen, 0);
+ if (written != enclen) {
ret = written;
if (written >= 0)
ret = -ENOMEM;
@@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Pin the mount and dentry to the key so that we can open it again
* later
*/
+ prep->payload.data[big_key_data] = enckey;
*path = file->f_path;
path_get(path);
fput(file);
+ kfree(data);
} else {
/* Just store the data in a buffer */
void *data = kmalloc(datalen, GFP_KERNEL);
+
if (!data)
return -ENOMEM;
@@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep)
err_fput:
fput(file);
+err_enckey:
+ kfree(enckey);
error:
+ kfree(data);
return ret;
}
@@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
{
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&prep->payload.data[big_key_path];
+
path_put(path);
- } else {
- kfree(prep->payload.data[big_key_data]);
}
+ kfree(prep->payload.data[big_key_data]);
}
/*
@@ -147,15 +245,15 @@ void big_key_destroy(struct key *key)
{
size_t datalen = (size_t)key->payload.data[big_key_len];
- if (datalen) {
+ if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path];
+
path_put(path);
path->mnt = NULL;
path->dentry = NULL;
- } else {
- kfree(key->payload.data[big_key_data]);
- key->payload.data[big_key_data] = NULL;
}
+ kfree(key->payload.data[big_key_data]);
+ key->payload.data[big_key_data] = NULL;
}
/*
@@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
if (datalen > BIG_KEY_FILE_THRESHOLD) {
struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file;
- loff_t pos;
+ u8 *data;
+ u8 *enckey = (u8 *)key->payload.data[big_key_data];
+ size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher));
+
+ data = kmalloc(enclen, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
file = dentry_open(path, O_RDONLY, current_cred());
- if (IS_ERR(file))
- return PTR_ERR(file);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto error;
+ }
- pos = 0;
- ret = vfs_read(file, buffer, datalen, &pos);
- fput(file);
- if (ret >= 0 && ret != datalen)
+ /* read file to kernel and decrypt */
+ ret = kernel_read(file, 0, data, enclen);
+ if (ret >= 0 && ret != enclen) {
ret = -EIO;
+ goto err_fput;
+ }
+
+ ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey);
+ if (ret)
+ goto err_fput;
+
+ ret = datalen;
+
+ /* copy decrypted data to user */
+ if (copy_to_user(buffer, data, datalen) != 0)
+ ret = -EFAULT;
+
+err_fput:
+ fput(file);
+error:
+ kfree(data);
} else {
ret = datalen;
if (copy_to_user(buffer, key->payload.data[big_key_data],
@@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
return ret;
}
+/*
+ * Register key type
+ */
static int __init big_key_init(void)
{
return register_key_type(&key_type_big_key);
}
+
+/*
+ * Initialize big_key crypto and RNG algorithms
+ */
+static int __init big_key_crypto_init(void)
+{
+ int ret = -EINVAL;
+
+ /* init RNG */
+ big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
+ if (IS_ERR(big_key_rng)) {
+ big_key_rng = NULL;
+ return -EFAULT;
+ }
+
+ /* seed RNG */
+ ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng));
+ if (ret)
+ goto error;
+
+ /* init block cipher */
+ big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0);
+ if (IS_ERR(big_key_blkcipher)) {
+ big_key_blkcipher = NULL;
+ ret = -EFAULT;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ crypto_free_rng(big_key_rng);
+ big_key_rng = NULL;
+ return ret;
+}
+
device_initcall(big_key_init);
+late_initcall(big_key_crypto_init);
diff --git a/security/keys/compat.c b/security/keys/compat.c
index 25430a3aa7f7..c8783b3b628c 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
case KEYCTL_GET_PERSISTENT:
return keyctl_get_persistent(arg2, arg3);
+ case KEYCTL_DH_COMPUTE:
+ return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
+ arg4);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/dh.c b/security/keys/dh.c
new file mode 100644
index 000000000000..880505a4b9f1
--- /dev/null
+++ b/security/keys/dh.c
@@ -0,0 +1,160 @@
+/* Crypto operations using stored keys
+ *
+ * Copyright (c) 2016, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mpi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+/*
+ * Public key or shared secret generation function [RFC2631 sec 2.1.1]
+ *
+ * ya = g^xa mod p;
+ * or
+ * ZZ = yb^xa mod p;
+ *
+ * where xa is the local private key, ya is the local public key, g is
+ * the generator, p is the prime, yb is the remote public key, and ZZ
+ * is the shared secret.
+ *
+ * Both are the same calculation, so g or yb are the "base" and ya or
+ * ZZ are the "result".
+ */
+static int do_dh(MPI result, MPI base, MPI xa, MPI p)
+{
+ return mpi_powm(result, base, xa, p);
+}
+
+static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
+{
+ struct key *key;
+ key_ref_t key_ref;
+ long status;
+ ssize_t ret;
+
+ key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
+ if (IS_ERR(key_ref)) {
+ ret = -ENOKEY;
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ ret = -EOPNOTSUPP;
+ if (key->type == &key_type_user) {
+ down_read(&key->sem);
+ status = key_validate(key);
+ if (status == 0) {
+ const struct user_key_payload *payload;
+
+ payload = user_key_payload(key);
+
+ if (maxlen == 0) {
+ *mpi = NULL;
+ ret = payload->datalen;
+ } else if (payload->datalen <= maxlen) {
+ *mpi = mpi_read_raw_data(payload->data,
+ payload->datalen);
+ if (*mpi)
+ ret = payload->datalen;
+ } else {
+ ret = -EINVAL;
+ }
+ }
+ up_read(&key->sem);
+ }
+
+ key_put(key);
+error:
+ return ret;
+}
+
+long keyctl_dh_compute(struct keyctl_dh_params __user *params,
+ char __user *buffer, size_t buflen)
+{
+ long ret;
+ MPI base, private, prime, result;
+ unsigned nbytes;
+ struct keyctl_dh_params pcopy;
+ uint8_t *kbuf;
+ ssize_t keylen;
+ size_t resultlen;
+
+ if (!params || (!buffer && buflen)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ keylen = mpi_from_key(pcopy.prime, buflen, &prime);
+ if (keylen < 0 || !prime) {
+ /* buflen == 0 may be used to query the required buffer size,
+ * which is the prime key length.
+ */
+ ret = keylen;
+ goto out;
+ }
+
+ /* The result is never longer than the prime */
+ resultlen = keylen;
+
+ keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base);
+ if (keylen < 0 || !base) {
+ ret = keylen;
+ goto error1;
+ }
+
+ keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private);
+ if (keylen < 0 || !private) {
+ ret = keylen;
+ goto error2;
+ }
+
+ result = mpi_alloc(0);
+ if (!result) {
+ ret = -ENOMEM;
+ goto error3;
+ }
+
+ kbuf = kmalloc(resultlen, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto error4;
+ }
+
+ ret = do_dh(result, base, private, prime);
+ if (ret)
+ goto error5;
+
+ ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL);
+ if (ret != 0)
+ goto error5;
+
+ ret = nbytes;
+ if (copy_to_user(buffer, kbuf, nbytes) != 0)
+ ret = -EFAULT;
+
+error5:
+ kfree(kbuf);
+error4:
+ mpi_free(result);
+error3:
+ mpi_free(private);
+error2:
+ mpi_free(base);
+error1:
+ mpi_free(prime);
+out:
+ return ret;
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 5105c2c2da75..8ec7a528365d 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -15,6 +15,7 @@
#include <linux/sched.h>
#include <linux/key-type.h>
#include <linux/task_work.h>
+#include <linux/keyctl.h>
struct iovec;
@@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
}
#endif
+#ifdef CONFIG_KEY_DH_OPERATIONS
+extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *,
+ size_t);
+#else
+static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
+ char __user *buffer, size_t buflen)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/*
* Debugging key validation
*/
diff --git a/security/keys/key.c b/security/keys/key.c
index b28755131687..bd5a272f28a6 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -201,6 +201,7 @@ serial_exists:
* @cred: The credentials specifying UID namespace.
* @perm: The permissions mask of the new key.
* @flags: Flags specifying quota properties.
+ * @restrict_link: Optional link restriction method for new keyrings.
*
* Allocate a key of the specified type with the attributes given. The key is
* returned in an uninstantiated state and the caller needs to instantiate the
@@ -223,7 +224,10 @@ serial_exists:
*/
struct key *key_alloc(struct key_type *type, const char *desc,
kuid_t uid, kgid_t gid, const struct cred *cred,
- key_perm_t perm, unsigned long flags)
+ key_perm_t perm, unsigned long flags,
+ int (*restrict_link)(struct key *,
+ const struct key_type *,
+ const union key_payload *))
{
struct key_user *user = NULL;
struct key *key;
@@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->uid = uid;
key->gid = gid;
key->perm = perm;
+ key->restrict_link = restrict_link;
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
- if (flags & KEY_ALLOC_TRUSTED)
- key->flags |= 1 << KEY_FLAG_TRUSTED;
if (flags & KEY_ALLOC_BUILT_IN)
key->flags |= 1 << KEY_FLAG_BUILTIN;
@@ -496,6 +499,12 @@ int key_instantiate_and_link(struct key *key,
}
if (keyring) {
+ if (keyring->restrict_link) {
+ ret = keyring->restrict_link(keyring, key->type,
+ &prep.payload);
+ if (ret < 0)
+ goto error;
+ }
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret < 0)
goto error;
@@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key,
awaken = 0;
ret = -EBUSY;
- if (keyring)
+ if (keyring) {
+ if (keyring->restrict_link)
+ return -EPERM;
+
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+ }
mutex_lock(&key_construction_mutex);
@@ -793,6 +806,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
struct key *keyring, *key = NULL;
key_ref_t key_ref;
int ret;
+ int (*restrict_link)(struct key *,
+ const struct key_type *,
+ const union key_payload *) = NULL;
/* look up the key type to see if it's one of the registered kernel
* types */
@@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_check(keyring);
+ key_ref = ERR_PTR(-EPERM);
+ if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION))
+ restrict_link = keyring->restrict_link;
+
key_ref = ERR_PTR(-ENOTDIR);
if (keyring->type != &key_type_keyring)
goto error_put_type;
@@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
prep.data = payload;
prep.datalen = plen;
prep.quotalen = index_key.type->def_datalen;
- prep.trusted = flags & KEY_ALLOC_TRUSTED;
prep.expiry = TIME_T_MAX;
if (index_key.type->preparse) {
ret = index_key.type->preparse(&prep);
@@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
index_key.desc_len = strlen(index_key.description);
- key_ref = ERR_PTR(-EPERM);
- if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
- goto error_free_prep;
- flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
+ if (restrict_link) {
+ ret = restrict_link(keyring, index_key.type, &prep.payload);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_free_prep;
+ }
+ }
ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) {
@@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
/* allocate a new key */
key = key_alloc(index_key.type, index_key.description,
- cred->fsuid, cred->fsgid, cred, perm, flags);
+ cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_link_end;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index ed73c6c1c326..3b135a0af344 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
case KEYCTL_GET_PERSISTENT:
return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
+ case KEYCTL_DH_COMPUTE:
+ return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
+ (char __user *) arg3,
+ (size_t) arg4);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index f931ccfeefb0..c91e4e0cea08 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -491,13 +491,17 @@ static long keyring_read(const struct key *keyring,
*/
struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
const struct cred *cred, key_perm_t perm,
- unsigned long flags, struct key *dest)
+ unsigned long flags,
+ int (*restrict_link)(struct key *,
+ const struct key_type *,
+ const union key_payload *),
+ struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid, cred, perm, flags);
+ uid, gid, cred, perm, flags, restrict_link);
if (!IS_ERR(keyring)) {
ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
if (ret < 0) {
@@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
}
EXPORT_SYMBOL(keyring_alloc);
+/**
+ * restrict_link_reject - Give -EPERM to restrict link
+ * @keyring: The keyring being added to.
+ * @type: The type of key being added.
+ * @payload: The payload of the key intended to be added.
+ *
+ * Reject the addition of any links to a keyring. It can be overridden by
+ * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
+ * adding a key to a keyring.
+ *
+ * This is meant to be passed as the restrict_link parameter to
+ * keyring_alloc().
+ */
+int restrict_link_reject(struct key *keyring,
+ const struct key_type *type,
+ const union key_payload *payload)
+{
+ return -EPERM;
+}
+
/*
* By default, we keys found by getting an exact match on their descriptions.
*/
@@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring,
up_write(&keyring->sem);
}
+/*
+ * Check addition of keys to restricted keyrings.
+ */
+static int __key_link_check_restriction(struct key *keyring, struct key *key)
+{
+ if (!keyring->restrict_link)
+ return 0;
+ return keyring->restrict_link(keyring, key->type, &key->payload);
+}
+
/**
* key_link - Link a key to a keyring
* @keyring: The keyring to make the link in.
@@ -1221,14 +1255,12 @@ int key_link(struct key *keyring, struct key *key)
key_check(keyring);
key_check(key);
- if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
- !test_bit(KEY_FLAG_TRUSTED, &key->flags))
- return -EPERM;
-
ret = __key_link_begin(keyring, &key->index_key, &edit);
if (ret == 0) {
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
- ret = __key_link_check_live_key(keyring, key);
+ ret = __key_link_check_restriction(keyring, key);
+ if (ret == 0)
+ ret = __key_link_check_live_key(keyring, key);
if (ret == 0)
__key_link(key, &edit);
__key_link_end(keyring, &key->index_key, edit);
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
index c9fae5ea89fe..2ef45b319dd9 100644
--- a/security/keys/persistent.c
+++ b/security/keys/persistent.c
@@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns)
current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(reg))
return PTR_ERR(reg);
@@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
uid, INVALID_GID, current_cred(),
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL,
ns->persistent_keyring_register);
if (IS_ERR(persistent))
return ERR_CAST(persistent);
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index e6d50172872f..40a885239782 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -76,7 +76,8 @@ int install_user_keyrings(void)
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm,
- KEY_ALLOC_IN_QUOTA, NULL);
+ KEY_ALLOC_IN_QUOTA,
+ NULL, NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
goto error;
@@ -92,7 +93,8 @@ int install_user_keyrings(void)
session_keyring =
keyring_alloc(buf, user->uid, INVALID_GID,
cred, user_keyring_perm,
- KEY_ALLOC_IN_QUOTA, NULL);
+ KEY_ALLOC_IN_QUOTA,
+ NULL, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error_release;
@@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new)
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ KEY_ALLOC_QUOTA_OVERRUN,
+ NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
@@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new)
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ KEY_ALLOC_QUOTA_OVERRUN,
+ NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
@@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
- flags, NULL);
+ flags, NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
} else {
@@ -785,7 +789,7 @@ long join_session_keyring(const char *name)
keyring = keyring_alloc(
name, old->uid, old->gid, old,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
- KEY_ALLOC_IN_QUOTA, NULL);
+ KEY_ALLOC_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index c7a117c9a8f3..a29e3554751e 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons,
cred = get_current_cred();
keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
put_cred(cred);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
@@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
key = key_alloc(ctx->index_key.type, ctx->index_key.description,
ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
- perm, flags);
+ perm, flags, NULL);
if (IS_ERR(key))
goto alloc_failed;
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 4f0f112fe276..9db8b4a82787 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
authkey = key_alloc(&key_type_request_key_auth, desc,
cred->fsuid, cred->fsgid, cred,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
- KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
+ KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
goto error_alloc;
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 8705d79b2c6f..66b1840b4110 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse);
*/
int user_update(struct key *key, struct key_preparsed_payload *prep)
{
- struct user_key_payload *upayload, *zap;
- size_t datalen = prep->datalen;
+ struct user_key_payload *zap = NULL;
int ret;
- ret = -EINVAL;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
- goto error;
-
- /* construct a replacement payload */
- ret = -ENOMEM;
- upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
- if (!upayload)
- goto error;
-
- upayload->datalen = datalen;
- memcpy(upayload->data, prep->data, datalen);
-
/* check the quota and attach the new data */
- zap = upayload;
-
- ret = key_payload_reserve(key, datalen);
-
- if (ret == 0) {
- /* attach the new data, displacing the old */
- if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
- zap = key->payload.data[0];
- else
- zap = NULL;
- rcu_assign_keypointer(key, upayload);
- key->expiry = 0;
- }
+ ret = key_payload_reserve(key, prep->datalen);
+ if (ret < 0)
+ return ret;
+
+ /* attach the new data, displacing the old */
+ key->expiry = prep->expiry;
+ if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ zap = rcu_dereference_key(key);
+ rcu_assign_keypointer(key, prep->payload.data[0]);
+ prep->payload.data[0] = NULL;
if (zap)
kfree_rcu(zap, rcu);
-
-error:
return ret;
}
-
EXPORT_SYMBOL_GPL(user_update);
/*
diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig
new file mode 100644
index 000000000000..dd01aa91e521
--- /dev/null
+++ b/security/loadpin/Kconfig
@@ -0,0 +1,19 @@
+config SECURITY_LOADPIN
+ bool "Pin load of kernel files (modules, fw, etc) to one filesystem"
+ depends on SECURITY && BLOCK
+ help
+ Any files read through the kernel file reading interface
+ (kernel modules, firmware, kexec images, security policy)
+ can be pinned to the first filesystem used for loading. When
+ enabled, any files that come from other filesystems will be
+ rejected. This is best used on systems without an initrd that
+ have a root filesystem backed by a read-only device such as
+ dm-verity or a CDROM.
+
+config SECURITY_LOADPIN_ENABLED
+ bool "Enforce LoadPin at boot"
+ depends on SECURITY_LOADPIN
+ help
+ If selected, LoadPin will enforce pinning at boot. If not
+ selected, it can be enabled at boot with the kernel parameter
+ "loadpin.enabled=1".
diff --git a/security/loadpin/Makefile b/security/loadpin/Makefile
new file mode 100644
index 000000000000..c2d77f83037b
--- /dev/null
+++ b/security/loadpin/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
new file mode 100644
index 000000000000..89a46f10b8a7
--- /dev/null
+++ b/security/loadpin/loadpin.c
@@ -0,0 +1,190 @@
+/*
+ * Module and Firmware Pinning Security Module
+ *
+ * Copyright 2011-2016 Google Inc.
+ *
+ * Author: Kees Cook <keescook@chromium.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "LoadPin: " fmt
+
+#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>
+#include <linux/sched.h> /* current */
+#include <linux/string_helpers.h>
+
+static void report_load(const char *origin, struct file *file, char *operation)
+{
+ char *cmdline, *pathname;
+
+ pathname = kstrdup_quotable_file(file, GFP_KERNEL);
+ cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL);
+
+ pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n",
+ origin, operation,
+ (pathname && pathname[0] != '<') ? "\"" : "",
+ pathname,
+ (pathname && pathname[0] != '<') ? "\"" : "",
+ task_pid_nr(current),
+ cmdline ? "\"" : "", cmdline, cmdline ? "\"" : "");
+
+ kfree(cmdline);
+ kfree(pathname);
+}
+
+static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED);
+static struct super_block *pinned_root;
+static DEFINE_SPINLOCK(pinned_root_spinlock);
+
+#ifdef CONFIG_SYSCTL
+static int zero;
+static int one = 1;
+
+static struct ctl_path loadpin_sysctl_path[] = {
+ { .procname = "kernel", },
+ { .procname = "loadpin", },
+ { }
+};
+
+static struct ctl_table loadpin_sysctl_table[] = {
+ {
+ .procname = "enabled",
+ .data = &enabled,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ { }
+};
+
+/*
+ * This must be called after early kernel init, since then the rootdev
+ * is available.
+ */
+static void check_pinning_enforcement(struct super_block *mnt_sb)
+{
+ bool ro = false;
+
+ /*
+ * If load pinning is not enforced via a read-only block
+ * device, allow sysctl to change modes for testing.
+ */
+ if (mnt_sb->s_bdev) {
+ ro = bdev_read_only(mnt_sb->s_bdev);
+ pr_info("dev(%u,%u): %s\n",
+ MAJOR(mnt_sb->s_bdev->bd_dev),
+ MINOR(mnt_sb->s_bdev->bd_dev),
+ ro ? "read-only" : "writable");
+ } else
+ pr_info("mnt_sb lacks block device, treating as: writable\n");
+
+ if (!ro) {
+ if (!register_sysctl_paths(loadpin_sysctl_path,
+ loadpin_sysctl_table))
+ pr_notice("sysctl registration failed!\n");
+ else
+ pr_info("load pinning can be disabled.\n");
+ } else
+ pr_info("load pinning engaged.\n");
+}
+#else
+static void check_pinning_enforcement(struct super_block *mnt_sb)
+{
+ pr_info("load pinning engaged.\n");
+}
+#endif
+
+static void loadpin_sb_free_security(struct super_block *mnt_sb)
+{
+ /*
+ * When unmounting the filesystem we were using for load
+ * pinning, we acknowledge the superblock release, but make sure
+ * no other modules or firmware can be loaded.
+ */
+ if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) {
+ pinned_root = ERR_PTR(-EIO);
+ pr_info("umount pinned fs: refusing further loads\n");
+ }
+}
+
+static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
+{
+ struct super_block *load_root;
+ const char *origin = kernel_read_file_id_str(id);
+
+ /* This handles the older init_module API that has a NULL file. */
+ if (!file) {
+ if (!enabled) {
+ report_load(origin, NULL, "old-api-pinning-ignored");
+ return 0;
+ }
+
+ report_load(origin, NULL, "old-api-denied");
+ return -EPERM;
+ }
+
+ load_root = file->f_path.mnt->mnt_sb;
+
+ /* First loaded module/firmware defines the root for all others. */
+ spin_lock(&pinned_root_spinlock);
+ /*
+ * pinned_root is only NULL at startup. Otherwise, it is either
+ * a valid reference, or an ERR_PTR.
+ */
+ if (!pinned_root) {
+ pinned_root = load_root;
+ /*
+ * Unlock now since it's only pinned_root we care about.
+ * In the worst case, we will (correctly) report pinning
+ * failures before we have announced that pinning is
+ * enabled. This would be purely cosmetic.
+ */
+ spin_unlock(&pinned_root_spinlock);
+ check_pinning_enforcement(pinned_root);
+ report_load(origin, file, "pinned");
+ } else {
+ spin_unlock(&pinned_root_spinlock);
+ }
+
+ if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
+ if (unlikely(!enabled)) {
+ report_load(origin, file, "pinning-ignored");
+ return 0;
+ }
+
+ report_load(origin, file, "denied");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static struct security_hook_list loadpin_hooks[] = {
+ LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
+ LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
+};
+
+void __init loadpin_add_hooks(void)
+{
+ pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
+ security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
+}
+
+/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
+module_param(enabled, int, 0);
+MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");
diff --git a/security/security.c b/security/security.c
index d17e4a6d269c..709569305d32 100644
--- a/security/security.c
+++ b/security/security.c
@@ -60,6 +60,7 @@ int __init security_init(void)
*/
capability_add_hooks();
yama_add_hooks();
+ loadpin_add_hooks();
/*
* Load all the remaining security modules.
@@ -1848,7 +1849,6 @@ struct security_hook_heads security_hook_heads = {
.tun_dev_attach =
LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
.tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
- .skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by),
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
.xfrm_policy_alloc_security =
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3140efa76a75..a86d537eb79b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode,
might_sleep_if(may_sleep);
- if (isec->initialized == LABEL_INVALID) {
+ if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
if (!may_sleep)
return -ECHILD;
@@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode)
return inode->i_security;
}
+static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
+{
+ struct inode *inode = d_backing_inode(dentry);
+
+ return inode->i_security;
+}
+
/*
* Get the security label of a dentry's backing inode.
*/
@@ -687,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
struct dentry *root = sbsec->sb->s_root;
- struct inode_security_struct *root_isec = backing_inode_security(root);
+ struct inode_security_struct *root_isec;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
u32 defcontext_sid = 0;
char **mount_options = opts->mnt_opts;
@@ -730,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
&& (num_opts == 0))
goto out;
+ root_isec = backing_inode_security_novalidate(root);
+
/*
* parse the mount options, check if they are valid sids.
* also check if someone is trying to mount the same sb more
@@ -1623,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk,
/* Check whether a task is allowed to use a capability. */
static int cred_has_capability(const struct cred *cred,
- int cap, int audit)
+ int cap, int audit, bool initns)
{
struct common_audit_data ad;
struct av_decision avd;
@@ -1637,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred,
switch (CAP_TO_INDEX(cap)) {
case 0:
- sclass = SECCLASS_CAPABILITY;
+ sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;
break;
case 1:
- sclass = SECCLASS_CAPABILITY2;
+ sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;
break;
default:
printk(KERN_ERR
@@ -1782,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir,
u32 *_new_isid)
{
const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
- const struct inode_security_struct *dsec = inode_security(dir);
const struct task_security_struct *tsec = current_security();
if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1792,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir,
tsec->create_sid) {
*_new_isid = tsec->create_sid;
} else {
+ const struct inode_security_struct *dsec = inode_security(dir);
return security_transition_sid(tsec->sid, dsec->sid, tclass,
name, _new_isid);
}
@@ -2076,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
u32 sid = task_sid(to);
struct file_security_struct *fsec = file->f_security;
struct dentry *dentry = file->f_path.dentry;
- struct inode_security_struct *isec = backing_inode_security(dentry);
+ struct inode_security_struct *isec;
struct common_audit_data ad;
int rc;
@@ -2095,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
+ isec = backing_inode_security(dentry);
return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
&ad);
}
@@ -2143,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
- return cred_has_capability(cred, cap, audit);
+ return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
}
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -2221,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
int rc, cap_sys_admin = 0;
rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
- SECURITY_CAP_NOAUDIT);
+ SECURITY_CAP_NOAUDIT, true);
if (rc == 0)
cap_sys_admin = 1;
@@ -2230,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
+static u32 ptrace_parent_sid(struct task_struct *task)
+{
+ u32 sid = 0;
+ struct task_struct *tracer;
+
+ rcu_read_lock();
+ tracer = ptrace_parent(task);
+ if (tracer)
+ sid = task_sid(tracer);
+ rcu_read_unlock();
+
+ return sid;
+}
+
static int check_nnp_nosuid(const struct linux_binprm *bprm,
const struct task_security_struct *old_tsec,
const struct task_security_struct *new_tsec)
@@ -2351,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
* changes its SID has the appropriate permit */
if (bprm->unsafe &
(LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- struct task_struct *tracer;
- struct task_security_struct *sec;
- u32 ptsid = 0;
-
- rcu_read_lock();
- tracer = ptrace_parent(current);
- if (likely(tracer != NULL)) {
- sec = __task_cred(tracer)->security;
- ptsid = sec->sid;
- }
- rcu_read_unlock();
-
+ u32 ptsid = ptrace_parent_sid(current);
if (ptsid != 0) {
rc = avc_has_perm(ptsid, new_tsec->sid,
SECCLASS_PROCESS,
@@ -3046,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = d_backing_inode(dentry);
- struct inode_security_struct *isec = backing_inode_security(dentry);
+ struct inode_security_struct *isec;
struct superblock_security_struct *sbsec;
struct common_audit_data ad;
u32 newsid, sid = current_sid();
@@ -3065,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
+ isec = backing_inode_security(dentry);
rc = avc_has_perm(sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
@@ -3123,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
int flags)
{
struct inode *inode = d_backing_inode(dentry);
- struct inode_security_struct *isec = backing_inode_security(dentry);
+ struct inode_security_struct *isec;
u32 newsid;
int rc;
@@ -3140,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
return;
}
+ isec = backing_inode_security(dentry);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
@@ -3181,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
u32 size;
int error;
char *context = NULL;
- struct inode_security_struct *isec = inode_security(inode);
+ struct inode_security_struct *isec;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
@@ -3199,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
SECURITY_CAP_NOAUDIT);
if (!error)
error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
- SECURITY_CAP_NOAUDIT);
+ SECURITY_CAP_NOAUDIT, true);
+ isec = inode_security(inode);
if (!error)
error = security_sid_to_context_force(isec->sid, &context,
&size);
@@ -3220,7 +3236,7 @@ out_nofree:
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- struct inode_security_struct *isec = inode_security(inode);
+ struct inode_security_struct *isec = inode_security_novalidate(inode);
u32 newsid;
int rc;
@@ -3309,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
struct common_audit_data ad;
struct file_security_struct *fsec = file->f_security;
struct inode *inode = file_inode(file);
- struct inode_security_struct *isec = inode_security(inode);
+ struct inode_security_struct *isec;
struct lsm_ioctlop_audit ioctl;
u32 ssid = cred_sid(cred);
int rc;
@@ -3333,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
if (unlikely(IS_PRIVATE(inode)))
return 0;
+ isec = inode_security(inode);
rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
requested, driver, xperm, &ad);
out:
@@ -3374,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
case KDSKBENT:
case KDSKBSENT:
error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
- SECURITY_CAP_AUDIT);
+ SECURITY_CAP_AUDIT, true);
break;
/* default case assumes that the command will go
@@ -3463,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
vma->vm_end <= vma->vm_mm->brk) {
rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
} else if (!vma->vm_file &&
- vma->vm_start <= vma->vm_mm->start_stack &&
- vma->vm_end >= vma->vm_mm->start_stack) {
+ ((vma->vm_start <= vma->vm_mm->start_stack &&
+ vma->vm_end >= vma->vm_mm->start_stack) ||
+ vma_is_stack_for_task(vma, current))) {
rc = current_has_perm(current, PROCESS__EXECSTACK);
} else if (vma->vm_file && vma->anon_vma) {
/*
@@ -3720,6 +3738,52 @@ static int selinux_kernel_module_request(char *kmod_name)
SYSTEM__MODULE_REQUEST, &ad);
}
+static int selinux_kernel_module_from_file(struct file *file)
+{
+ struct common_audit_data ad;
+ struct inode_security_struct *isec;
+ struct file_security_struct *fsec;
+ u32 sid = current_sid();
+ int rc;
+
+ /* init_module */
+ if (file == NULL)
+ return avc_has_perm(sid, sid, SECCLASS_SYSTEM,
+ SYSTEM__MODULE_LOAD, NULL);
+
+ /* finit_module */
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = file->f_path;
+
+ fsec = file->f_security;
+ if (sid != fsec->sid) {
+ rc = avc_has_perm(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,
+ SYSTEM__MODULE_LOAD, &ad);
+}
+
+static int selinux_kernel_read_file(struct file *file,
+ enum kernel_read_file_id id)
+{
+ int rc = 0;
+
+ switch (id) {
+ case READING_MODULE:
+ rc = selinux_kernel_module_from_file(file);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
return current_has_perm(p, PROCESS__SETPGID);
@@ -4599,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
{
u32 peer_secid = SECSID_NULL;
u16 family;
+ struct inode_security_struct *isec;
if (skb && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
@@ -4609,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
else
goto out;
- if (sock && family == PF_UNIX)
- selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid);
- else if (skb)
+ if (sock && family == PF_UNIX) {
+ isec = inode_security_novalidate(SOCK_INODE(sock));
+ peer_secid = isec->sid;
+ } else if (skb)
selinux_skb_peerlbl_sid(skb, family, &peer_secid);
out:
@@ -5676,7 +5742,6 @@ static int selinux_setprocattr(struct task_struct *p,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
- struct task_struct *tracer;
struct cred *new;
u32 sid = 0, ptsid;
int error;
@@ -5783,14 +5848,8 @@ static int selinux_setprocattr(struct task_struct *p,
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
- ptsid = 0;
- rcu_read_lock();
- tracer = ptrace_parent(p);
- if (tracer)
- ptsid = task_sid(tracer);
- rcu_read_unlock();
-
- if (tracer) {
+ ptsid = ptrace_parent_sid(p);
+ if (ptsid != 0) {
error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
if (error)
@@ -6021,6 +6080,7 @@ static struct security_hook_list selinux_hooks[] = {
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),
+ LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file),
LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
LSM_HOOK_INIT(task_getsid, selinux_task_getsid),
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index ef83c4b85a33..1f1f4b2f6018 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -12,6 +12,18 @@
#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
"write", "associate", "unix_read", "unix_write"
+#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \
+ "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \
+ "linux_immutable", "net_bind_service", "net_broadcast", \
+ "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \
+ "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \
+ "sys_boot", "sys_nice", "sys_resource", "sys_time", \
+ "sys_tty_config", "mknod", "lease", "audit_write", \
+ "audit_control", "setfcap"
+
+#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
+ "wake_alarm", "block_suspend", "audit_read"
+
/*
* Note: The name for any socket class should be suffixed by "socket",
* and doesn't contain more than one substr of "socket".
@@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = {
"setsockcreate", NULL } },
{ "system",
{ "ipc_info", "syslog_read", "syslog_mod",
- "syslog_console", "module_request", NULL } },
+ "syslog_console", "module_request", "module_load", NULL } },
{ "capability",
- { "chown", "dac_override", "dac_read_search",
- "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap",
- "linux_immutable", "net_bind_service", "net_broadcast",
- "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module",
- "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin",
- "sys_boot", "sys_nice", "sys_resource", "sys_time",
- "sys_tty_config", "mknod", "lease", "audit_write",
- "audit_control", "setfcap", NULL } },
+ { COMMON_CAP_PERMS, NULL } },
{ "filesystem",
{ "mount", "remount", "unmount", "getattr",
"relabelfrom", "relabelto", "associate", "quotamod",
@@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = {
{ "memprotect", { "mmap_zero", NULL } },
{ "peer", { "recv", NULL } },
{ "capability2",
- { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend",
- "audit_read", NULL } },
+ { COMMON_CAP2_PERMS, NULL } },
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket",
{ COMMON_SOCK_PERMS, "attach_queue", NULL } },
{ "binder", { "impersonate", "call", "set_context_mgr", "transfer",
NULL } },
+ { "cap_userns",
+ { COMMON_CAP_PERMS, NULL } },
+ { "cap2_userns",
+ { COMMON_CAP2_PERMS, NULL } },
{ NULL }
};
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
index 67ce7a8d8301..ff4fddca9050 100644
--- a/security/selinux/include/conditional.h
+++ b/security/selinux/include/conditional.h
@@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values);
int security_set_bools(int len, int *values);
-int security_get_bool_value(int bool);
+int security_get_bool_value(int index);
#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index a2ae05414ba1..c21e135460a5 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -38,9 +38,8 @@ struct task_security_struct {
};
enum label_initialized {
- LABEL_MISSING, /* not initialized */
- LABEL_INITIALIZED, /* inizialized */
- LABEL_INVALID /* invalid */
+ LABEL_INVALID, /* invalid or not initialized */
+ LABEL_INITIALIZED /* initialized */
};
struct inode_security_struct {
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ebda97333f1b..89df64672b89 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2696,7 +2696,7 @@ out:
return rc;
}
-int security_get_bool_value(int bool)
+int security_get_bool_value(int index)
{
int rc;
int len;
@@ -2705,10 +2705,10 @@ int security_get_bool_value(int bool)
rc = -EFAULT;
len = policydb.p_bools.nprim;
- if (bool >= len)
+ if (index >= len)
goto out;
- rc = policydb.bool_val_to_struct[bool]->state;
+ rc = policydb.bool_val_to_struct[index]->state;
out:
read_unlock(&policy_rwlock);
return rc;
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index cb6ed10816d4..9b756b1f3dc5 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -18,6 +18,7 @@
#include <linux/prctl.h>
#include <linux/ratelimit.h>
#include <linux/workqueue.h>
+#include <linux/string_helpers.h>
#define YAMA_SCOPE_DISABLED 0
#define YAMA_SCOPE_RELATIONAL 1
@@ -41,6 +42,22 @@ static DEFINE_SPINLOCK(ptracer_relations_lock);
static void yama_relation_cleanup(struct work_struct *work);
static DECLARE_WORK(yama_relation_work, yama_relation_cleanup);
+static void report_access(const char *access, struct task_struct *target,
+ struct task_struct *agent)
+{
+ char *target_cmd, *agent_cmd;
+
+ target_cmd = kstrdup_quotable_cmdline(target, GFP_ATOMIC);
+ agent_cmd = kstrdup_quotable_cmdline(agent, GFP_ATOMIC);
+
+ pr_notice_ratelimited(
+ "ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n",
+ access, target_cmd, target->pid, agent_cmd, agent->pid);
+
+ kfree(agent_cmd);
+ kfree(target_cmd);
+}
+
/**
* yama_relation_cleanup - remove invalid entries from the relation list
*
@@ -307,11 +324,8 @@ static int yama_ptrace_access_check(struct task_struct *child,
}
}
- if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) {
- printk_ratelimited(KERN_NOTICE
- "ptrace of pid %d was attempted by: %s (pid %d)\n",
- child->pid, current->comm, current->pid);
- }
+ if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0)
+ report_access("attach", child, current);
return rc;
}
@@ -337,11 +351,8 @@ int yama_ptrace_traceme(struct task_struct *parent)
break;
}
- if (rc) {
- printk_ratelimited(KERN_NOTICE
- "ptraceme of pid %d was attempted by: %s (pid %d)\n",
- current->pid, parent->comm, parent->pid);
- }
+ if (rc)
+ report_access("traceme", current, parent);
return rc;
}