aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig.hardening76
-rw-r--r--security/ipe/audit.c19
-rw-r--r--security/ipe/fs.c25
-rw-r--r--security/ipe/policy.c17
-rw-r--r--security/ipe/policy_fs.c28
-rw-r--r--security/keys/gc.c4
-rw-r--r--security/keys/key.c5
-rw-r--r--security/selinux/ss/services.c16
-rw-r--r--security/selinux/xfrm.c2
9 files changed, 80 insertions, 112 deletions
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening
index c17366ce8224..fd1238753cad 100644
--- a/security/Kconfig.hardening
+++ b/security/Kconfig.hardening
@@ -1,22 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "Kernel hardening options"
-config GCC_PLUGIN_STRUCTLEAK
- bool
- help
- While the kernel is built with warnings enabled for any missed
- stack variable initializations, this warning is silenced for
- anything passed by reference to another function, under the
- occasionally misguided assumption that the function will do
- the initialization. As this regularly leads to exploitable
- flaws, this plugin is available to identify and zero-initialize
- such variables, depending on the chosen level of coverage.
-
- This plugin was originally ported from grsecurity/PaX. More
- information at:
- * https://grsecurity.net/
- * https://pax.grsecurity.net/
-
menu "Memory initialization"
config CC_HAS_AUTO_VAR_INIT_PATTERN
@@ -36,7 +20,6 @@ config CC_HAS_AUTO_VAR_INIT_ZERO
choice
prompt "Initialize kernel stack variables at function entry"
- default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL if COMPILE_TEST && GCC_PLUGINS
default INIT_STACK_ALL_PATTERN if COMPILE_TEST && CC_HAS_AUTO_VAR_INIT_PATTERN
default INIT_STACK_ALL_ZERO if CC_HAS_AUTO_VAR_INIT_ZERO
default INIT_STACK_NONE
@@ -60,55 +43,6 @@ choice
classes of uninitialized stack variable exploits
and information exposures.
- config GCC_PLUGIN_STRUCTLEAK_USER
- bool "zero-init structs marked for userspace (weak)"
- # Plugin can be removed once the kernel only supports GCC 12+
- depends on GCC_PLUGINS && !CC_HAS_AUTO_VAR_INIT_ZERO
- select GCC_PLUGIN_STRUCTLEAK
- help
- Zero-initialize any structures on the stack containing
- a __user attribute. This can prevent some classes of
- uninitialized stack variable exploits and information
- exposures, like CVE-2013-2141:
- https://git.kernel.org/linus/b9e146d8eb3b9eca
-
- config GCC_PLUGIN_STRUCTLEAK_BYREF
- bool "zero-init structs passed by reference (strong)"
- # Plugin can be removed once the kernel only supports GCC 12+
- depends on GCC_PLUGINS && !CC_HAS_AUTO_VAR_INIT_ZERO
- depends on !(KASAN && KASAN_STACK)
- select GCC_PLUGIN_STRUCTLEAK
- help
- Zero-initialize any structures on the stack that may
- be passed by reference and had not already been
- explicitly initialized. This can prevent most classes
- of uninitialized stack variable exploits and information
- exposures, like CVE-2017-1000410:
- https://git.kernel.org/linus/06e7e776ca4d3654
-
- As a side-effect, this keeps a lot of variables on the
- stack that can otherwise be optimized out, so combining
- this with CONFIG_KASAN_STACK can lead to a stack overflow
- and is disallowed.
-
- config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
- bool "zero-init everything passed by reference (very strong)"
- # Plugin can be removed once the kernel only supports GCC 12+
- depends on GCC_PLUGINS && !CC_HAS_AUTO_VAR_INIT_ZERO
- depends on !(KASAN && KASAN_STACK)
- select GCC_PLUGIN_STRUCTLEAK
- help
- Zero-initialize any stack variables that may be passed
- by reference and had not already been explicitly
- initialized. This is intended to eliminate all classes
- of uninitialized stack variable exploits and information
- exposures.
-
- As a side-effect, this keeps a lot of variables on the
- stack that can otherwise be optimized out, so combining
- this with CONFIG_KASAN_STACK can lead to a stack overflow
- and is disallowed.
-
config INIT_STACK_ALL_PATTERN
bool "pattern-init everything (strongest)"
depends on CC_HAS_AUTO_VAR_INIT_PATTERN
@@ -148,16 +82,6 @@ choice
endchoice
-config GCC_PLUGIN_STRUCTLEAK_VERBOSE
- bool "Report forcefully initialized variables"
- depends on GCC_PLUGIN_STRUCTLEAK
- depends on !COMPILE_TEST # too noisy
- help
- This option will cause a warning to be printed each time the
- structleak plugin finds a variable it thinks needs to be
- initialized. Since not all existing initializers are detected
- by the plugin, this can produce false positive warnings.
-
config GCC_PLUGIN_STACKLEAK
bool "Poison kernel stack before returning from syscalls"
depends on GCC_PLUGINS
diff --git a/security/ipe/audit.c b/security/ipe/audit.c
index f05f0caa4850..9668ecc5acd5 100644
--- a/security/ipe/audit.c
+++ b/security/ipe/audit.c
@@ -21,6 +21,8 @@
#define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\
"policy_digest=" IPE_AUDIT_HASH_ALG ":"
+#define AUDIT_POLICY_LOAD_NULL_FMT "policy_name=? policy_version=? "\
+ "policy_digest=?"
#define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\
"old_active_pol_version=%hu.%hu.%hu "\
"old_policy_digest=" IPE_AUDIT_HASH_ALG ":"
@@ -248,22 +250,29 @@ void ipe_audit_policy_activation(const struct ipe_policy *const op,
}
/**
- * ipe_audit_policy_load() - Audit a policy being loaded into the kernel.
- * @p: Supplies a pointer to the policy to audit.
+ * ipe_audit_policy_load() - Audit a policy loading event.
+ * @p: Supplies a pointer to the policy to audit or an error pointer.
*/
void ipe_audit_policy_load(const struct ipe_policy *const p)
{
struct audit_buffer *ab;
+ int err = 0;
ab = audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_IPE_POLICY_LOAD);
if (!ab)
return;
- audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p);
- audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1",
+ if (!IS_ERR(p)) {
+ audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p);
+ } else {
+ audit_log_format(ab, AUDIT_POLICY_LOAD_NULL_FMT);
+ err = PTR_ERR(p);
+ }
+
+ audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=%d errno=%d",
from_kuid(&init_user_ns, audit_get_loginuid(current)),
- audit_get_sessionid(current));
+ audit_get_sessionid(current), !err, err);
audit_log_end(ab);
}
diff --git a/security/ipe/fs.c b/security/ipe/fs.c
index 5b6d19fb844a..f40e50bfd2e7 100644
--- a/security/ipe/fs.c
+++ b/security/ipe/fs.c
@@ -133,6 +133,8 @@ static ssize_t getenforce(struct file *f, char __user *data,
* * %-ERANGE - Policy version number overflow
* * %-EINVAL - Policy version parsing error
* * %-EEXIST - Same name policy already deployed
+ * * %-ENOKEY - Policy signing key not found
+ * * %-EKEYREJECTED - Policy signature verification failed
*/
static ssize_t new_policy(struct file *f, const char __user *data,
size_t len, loff_t *offset)
@@ -141,12 +143,17 @@ static ssize_t new_policy(struct file *f, const char __user *data,
char *copy = NULL;
int rc = 0;
- if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
- return -EPERM;
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
copy = memdup_user_nul(data, len);
- if (IS_ERR(copy))
- return PTR_ERR(copy);
+ if (IS_ERR(copy)) {
+ rc = PTR_ERR(copy);
+ copy = NULL;
+ goto out;
+ }
p = ipe_new_policy(NULL, 0, copy, len);
if (IS_ERR(p)) {
@@ -158,12 +165,14 @@ static ssize_t new_policy(struct file *f, const char __user *data,
if (rc)
goto out;
- ipe_audit_policy_load(p);
-
out:
- if (rc < 0)
- ipe_free_policy(p);
kfree(copy);
+ if (rc < 0) {
+ ipe_free_policy(p);
+ ipe_audit_policy_load(ERR_PTR(rc));
+ } else {
+ ipe_audit_policy_load(p);
+ }
return (rc < 0) ? rc : len;
}
diff --git a/security/ipe/policy.c b/security/ipe/policy.c
index b628f696e32b..1c58c29886e8 100644
--- a/security/ipe/policy.c
+++ b/security/ipe/policy.c
@@ -84,8 +84,11 @@ static int set_pkcs7_data(void *ctx, const void *data, size_t len,
* ipe_new_policy.
*
* Context: Requires root->i_rwsem to be held.
- * Return: %0 on success. If an error occurs, the function will return
- * the -errno.
+ * Return:
+ * * %0 - Success
+ * * %-ENOENT - Policy was deleted while updating
+ * * %-EINVAL - Policy name mismatch
+ * * %-ESTALE - Policy version too old
*/
int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
const char *pkcs7, size_t pkcs7len)
@@ -146,10 +149,12 @@ err:
*
* Return:
* * a pointer to the ipe_policy structure - Success
- * * %-EBADMSG - Policy is invalid
- * * %-ENOMEM - Out of memory (OOM)
- * * %-ERANGE - Policy version number overflow
- * * %-EINVAL - Policy version parsing error
+ * * %-EBADMSG - Policy is invalid
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-ERANGE - Policy version number overflow
+ * * %-EINVAL - Policy version parsing error
+ * * %-ENOKEY - Policy signing key not found
+ * * %-EKEYREJECTED - Policy signature verification failed
*/
struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
const char *pkcs7, size_t pkcs7len)
diff --git a/security/ipe/policy_fs.c b/security/ipe/policy_fs.c
index 4cb4dd7f5236..db26032ccbe1 100644
--- a/security/ipe/policy_fs.c
+++ b/security/ipe/policy_fs.c
@@ -12,6 +12,7 @@
#include "policy.h"
#include "eval.h"
#include "fs.h"
+#include "audit.h"
#define MAX_VERSION_SIZE ARRAY_SIZE("65535.65535.65535")
@@ -286,8 +287,13 @@ static ssize_t getactive(struct file *f, char __user *data,
* On success this updates the policy represented by $name,
* in-place.
*
- * Return: Length of buffer written on success. If an error occurs,
- * the function will return the -errno.
+ * Return:
+ * * Length of buffer written - Success
+ * * %-EPERM - Insufficient permission
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-ENOENT - Policy was deleted while updating
+ * * %-EINVAL - Policy name mismatch
+ * * %-ESTALE - Policy version too old
*/
static ssize_t update_policy(struct file *f, const char __user *data,
size_t len, loff_t *offset)
@@ -296,21 +302,29 @@ static ssize_t update_policy(struct file *f, const char __user *data,
char *copy = NULL;
int rc = 0;
- if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
- return -EPERM;
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
copy = memdup_user(data, len);
- if (IS_ERR(copy))
- return PTR_ERR(copy);
+ if (IS_ERR(copy)) {
+ rc = PTR_ERR(copy);
+ copy = NULL;
+ goto out;
+ }
root = d_inode(f->f_path.dentry->d_parent);
inode_lock(root);
rc = ipe_update_policy(root, NULL, 0, copy, len);
inode_unlock(root);
+out:
kfree(copy);
- if (rc)
+ if (rc) {
+ ipe_audit_policy_load(ERR_PTR(rc));
return rc;
+ }
return len;
}
diff --git a/security/keys/gc.c b/security/keys/gc.c
index f27223ea4578..748e83818a76 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -218,8 +218,8 @@ continue_scanning:
key = rb_entry(cursor, struct key, serial_node);
cursor = rb_next(cursor);
- if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
- smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
+ if (!test_bit_acquire(KEY_FLAG_USER_ALIVE, &key->flags)) {
+ /* Clobber key->user after final put seen. */
goto found_unreferenced_key;
}
diff --git a/security/keys/key.c b/security/keys/key.c
index 7198cd2ac3a3..3bbdde778631 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -298,6 +298,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->restrict_link = restrict_link;
key->last_used_at = ktime_get_real_seconds();
+ key->flags |= 1 << KEY_FLAG_USER_ALIVE;
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
if (flags & KEY_ALLOC_BUILT_IN)
@@ -658,8 +659,8 @@ void key_put(struct key *key)
key->user->qnbytes -= key->quotalen;
spin_unlock_irqrestore(&key->user->lock, flags);
}
- smp_mb(); /* key->user before FINAL_PUT set. */
- set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
+ /* Mark key as safe for GC after key->user done. */
+ clear_bit_unlock(KEY_FLAG_USER_ALIVE, &key->flags);
schedule_work(&key_gc_work);
}
}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 7becf3808818..d185754c2786 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1909,11 +1909,17 @@ retry:
goto out_unlock;
}
/* Obtain the sid for the context. */
- rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
- if (rc == -ESTALE) {
- rcu_read_unlock();
- context_destroy(&newcontext);
- goto retry;
+ if (context_equal(scontext, &newcontext))
+ *out_sid = ssid;
+ else if (context_equal(tcontext, &newcontext))
+ *out_sid = tsid;
+ else {
+ rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
+ if (rc == -ESTALE) {
+ rcu_read_unlock();
+ context_destroy(&newcontext);
+ goto retry;
+ }
}
out_unlock:
rcu_read_unlock();
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 90ec4ef1b082..61d56b0c2be1 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -94,7 +94,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
ctx->ctx_doi = XFRM_SC_DOI_LSM;
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
- ctx->ctx_len = str_len;
+ ctx->ctx_len = str_len + 1;
memcpy(ctx->ctx_str, &uctx[1], str_len);
ctx->ctx_str[str_len] = '\0';
rc = security_context_to_sid(ctx->ctx_str, str_len,