diff mbox series

[v2,6/8] security/brute: Add prctls to enable/disable the fork attack detection

Message ID 20201025134540.3770-7-john.wood@gmx.com
State New
Headers show
Series Fork brute force attack mitigation | expand

Commit Message

John Wood Oct. 25, 2020, 1:45 p.m. UTC
To allow that a process can turn off or turn on the detection and
mitigation of a fork brute force attack when required, add two new
defines to the prctl interface.

All the arguments passed to the prctl system call are ignored for the
two new cases.

To enable the attack detection make the last crashes timestamps list not
empty. To disable the detection use the already created brute_disable()
function.

Signed-off-by: John Wood <john.wood@gmx.com>
---
 include/brute/brute.h      | 16 +++++++++
 include/uapi/linux/prctl.h |  4 +++
 kernel/sys.c               |  8 +++++
 security/brute/brute.c     | 71 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 99 insertions(+)
 create mode 100644 include/brute/brute.h

--
2.25.1
diff mbox series

Patch

diff --git a/include/brute/brute.h b/include/brute/brute.h
new file mode 100644
index 000000000000..da6fca04f16b
--- /dev/null
+++ b/include/brute/brute.h
@@ -0,0 +1,16 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _BRUTE_H_
+#define _BRUTE_H_
+
+#include <linux/errno.h>
+
+#ifdef CONFIG_SECURITY_FORK_BRUTE
+int brute_prctl_enable(void);
+int brute_prctl_disable(void);
+#else
+static inline int brute_prctl_enable(void) { return -EINVAL; }
+static inline int brute_prctl_disable(void) { return -EINVAL; }
+#endif
+
+#endif /* _BRUTE_H_ */
+
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 07b4f8131e36..01f5033210d0 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -238,4 +238,8 @@  struct prctl_mm_map {
 #define PR_SET_IO_FLUSHER		57
 #define PR_GET_IO_FLUSHER		58

+/* Enable/disable the detection and mitigation of a fork brute force attack */
+#define PR_SECURITY_FORK_BRUTE_ENABLE	59
+#define PR_SECURITY_FORK_BRUTE_DISABLE	60
+
 #endif /* _LINUX_PRCTL_H */
diff --git a/kernel/sys.c b/kernel/sys.c
index ab6c409b1159..35dae4e2f59a 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -72,6 +72,8 @@ 
 #include <asm/io.h>
 #include <asm/unistd.h>

+#include <brute/brute.h>
+
 #include "uid16.h"

 #ifndef SET_UNALIGN_CTL
@@ -2530,6 +2532,12 @@  SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,

 		error = (current->flags & PR_IO_FLUSHER) == PR_IO_FLUSHER;
 		break;
+	case PR_SECURITY_FORK_BRUTE_ENABLE:
+		error = brute_prctl_enable();
+		break;
+	case PR_SECURITY_FORK_BRUTE_DISABLE:
+		error = brute_prctl_disable();
+		break;
 	default:
 		error = -EINVAL;
 		break;
diff --git a/security/brute/brute.c b/security/brute/brute.c
index a1bdf25ffcf9..6f85e137553c 100644
--- a/security/brute/brute.c
+++ b/security/brute/brute.c
@@ -676,3 +676,74 @@  DEFINE_LSM(brute) = {
 	.blobs = &brute_blob_sizes,
 };

+/**
+ * brute_prctl_enable() - Enable the fork brute force attack detection.
+ *
+ * To enable the fork brute force attack detection the last crashes timestamps
+ * list must not be empty. So, if this list already contains entries nothing
+ * needs to be done. Otherwise, initialize the last crashes timestamps list with
+ * one entry set to now. This way, the application crash period can be computed
+ * at the next fault.
+ *
+ * It's mandatory to disable interrupts before acquiring the lock since the
+ * task_free hook can be called from an IRQ context during the execution of the
+ * prctl syscall.
+ *
+ * Return: -EFAULT if the current task doesn't have statistical data. -ENOMEM if
+ *         the allocation of the new timestamp structure fails. Zero otherwise.
+ */
+int brute_prctl_enable(void)
+{
+	struct brute_stats **stats;
+	struct brute_timestamp *timestamp;
+	unsigned long flags;
+
+	stats = brute_stats_ptr(current);
+	if (!*stats)
+		return -EFAULT;
+
+	timestamp = brute_new_timestamp();
+	if (!timestamp)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&(*stats)->lock, flags);
+
+	if (!list_empty(&(*stats)->timestamps)) {
+		kfree(timestamp);
+		goto unlock;
+	}
+
+	list_add_tail(&timestamp->node, &(*stats)->timestamps);
+	(*stats)->timestamps_size = 1;
+
+unlock:
+	spin_unlock_irqrestore(&(*stats)->lock, flags);
+	return 0;
+}
+
+/**
+ * brute_prctl_disable() - Disable the fork brute force attack detection.
+ *
+ * It's mandatory to disable interrupts before acquiring the lock since the
+ * task_free hook can be called from an IRQ context during the execution of the
+ * prctl syscall.
+ *
+ * Return: -EFAULT if the current task doesn't have statistical data. Zero
+ *         otherwise.
+ */
+int brute_prctl_disable(void)
+{
+	struct brute_stats **stats;
+	unsigned long flags;
+
+	stats = brute_stats_ptr(current);
+	if (!*stats)
+		return -EFAULT;
+
+	spin_lock_irqsave(&(*stats)->lock, flags);
+	brute_disable(*stats);
+	spin_unlock_irqrestore(&(*stats)->lock, flags);
+
+	return 0;
+}
+