Brooklyn/grsecurity/grsec_sig.c
Scare Crowe 2a709f28fa Auto exploit mitigation feature
* 0day explit mitigation
* Memory corruption prevention
* Privilege escalation prevention
* Buffer over flow prevention
* File System corruption defense
* Thread escape prevention

This may very well be the most intensive inclusion to BrooklynR. This will not be part of an x86 suite nor it will be released as tool kit. The security core toolkit will remain part of kernel base.
2021-11-13 09:26:51 +05:00

249 lines
6.4 KiB
C

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/grsecurity.h>
#include <linux/grinternal.h>
#include <linux/hardirq.h>
#include <asm/pgtable.h>
char *signames[] = {
[SIGSEGV] = "Segmentation fault",
[SIGILL] = "Illegal instruction",
[SIGABRT] = "Abort",
[SIGBUS] = "Invalid alignment/Bus error"
};
void
gr_log_signal(const int sig, const void *addr, const struct task_struct *t)
{
#ifdef CONFIG_GRKERNSEC_SIGNAL
if (grsec_enable_signal && ((sig == SIGSEGV) || (sig == SIGILL) ||
(sig == SIGABRT) || (sig == SIGBUS))) {
if (task_pid_nr(t) == task_pid_nr(current)) {
gr_log_sig_addr(GR_DONT_AUDIT_GOOD, GR_UNISIGLOG_MSG, signames[sig], addr);
} else {
gr_log_sig_task(GR_DONT_AUDIT_GOOD, GR_DUALSIGLOG_MSG, t, sig);
}
}
#endif
return;
}
int
gr_handle_signal(const struct task_struct *p, const int sig)
{
#ifdef CONFIG_GRKERNSEC
/* ignore the 0 signal for protected task checks */
if (task_pid_nr(current) > 1 && sig && gr_check_protected_task(p)) {
gr_log_sig_task(GR_DONT_AUDIT, GR_SIG_ACL_MSG, p, sig);
return -EPERM;
} else if (gr_pid_is_chrooted((struct task_struct *)p)) {
return -EPERM;
}
#endif
return 0;
}
#ifdef CONFIG_GRKERNSEC
extern int specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t);
int gr_fake_force_sig(int sig, struct task_struct *t)
{
unsigned long int flags;
int ret, blocked, ignored;
struct k_sigaction *action;
spin_lock_irqsave(&t->sighand->siglock, flags);
action = &t->sighand->action[sig-1];
ignored = action->sa.sa_handler == SIG_IGN;
blocked = sigismember(&t->blocked, sig);
if (blocked || ignored) {
action->sa.sa_handler = SIG_DFL;
if (blocked) {
sigdelset(&t->blocked, sig);
recalc_sigpending_and_wake(t);
}
}
if (action->sa.sa_handler == SIG_DFL)
t->signal->flags &= ~SIGNAL_UNKILLABLE;
ret = specific_send_sig_info(sig, SEND_SIG_PRIV, t);
spin_unlock_irqrestore(&t->sighand->siglock, flags);
return ret;
}
#endif
#define GR_USER_BAN_TIME (15 * 60)
#define GR_DAEMON_BRUTE_TIME (30 * 60)
void gr_handle_brute_attach(int dumpable)
{
#ifdef CONFIG_GRKERNSEC_BRUTE
struct task_struct *p = current;
kuid_t uid = GLOBAL_ROOT_UID;
int is_priv = 0;
int daemon = 0;
if (!grsec_enable_brute)
return;
if (is_privileged_binary(p->mm->exe_file->f_path.dentry))
is_priv = 1;
rcu_read_lock();
read_lock(&tasklist_lock);
read_lock(&grsec_exec_file_lock);
if (!is_priv && p->real_parent && gr_is_same_file(p->real_parent->exec_file, p->exec_file)) {
p->real_parent->brute_expires = get_seconds() + GR_DAEMON_BRUTE_TIME;
p->real_parent->brute = 1;
daemon = 1;
} else {
const struct cred *cred = __task_cred(p), *cred2;
struct task_struct *tsk, *tsk2;
if (dumpable != SUID_DUMP_USER && gr_is_global_nonroot(cred->uid)) {
struct user_struct *user;
uid = cred->uid;
/* this is put upon execution past expiration */
user = find_user(uid);
if (user == NULL)
goto unlock;
user->sugid_banned = 1;
user->sugid_ban_expires = get_seconds() + GR_USER_BAN_TIME;
if (user->sugid_ban_expires == ~0UL)
user->sugid_ban_expires--;
/* only kill other threads of the same binary, from the same user */
do_each_thread(tsk2, tsk) {
cred2 = __task_cred(tsk);
if (tsk != p && uid_eq(cred2->uid, uid) && gr_is_same_file(tsk->exec_file, p->exec_file))
gr_fake_force_sig(SIGKILL, tsk);
} while_each_thread(tsk2, tsk);
}
}
unlock:
read_unlock(&grsec_exec_file_lock);
read_unlock(&tasklist_lock);
rcu_read_unlock();
if (gr_is_global_nonroot(uid))
gr_log_fs_int2(GR_DONT_AUDIT, GR_BRUTE_SUID_MSG, p->exec_file->f_path.dentry, p->exec_file->f_path.mnt, GR_GLOBAL_UID(uid), GR_USER_BAN_TIME / 60);
else if (daemon)
gr_log_noargs(GR_DONT_AUDIT, GR_BRUTE_DAEMON_MSG);
#endif
return;
}
void gr_handle_brute_check(void)
{
#ifdef CONFIG_GRKERNSEC_BRUTE
struct task_struct *p = current;
if (unlikely(p->brute)) {
if (!grsec_enable_brute)
p->brute = 0;
else if (time_before(get_seconds(), p->brute_expires))
msleep(30 * 1000);
}
#endif
return;
}
void gr_handle_kernel_exploit(void)
{
#ifdef CONFIG_GRKERNSEC_KERN_LOCKOUT
static unsigned int num_banned_users __read_only;
const struct cred *cred;
struct task_struct *tsk, *tsk2;
struct user_struct *user;
kuid_t uid;
if (in_irq() || in_serving_softirq() || in_nmi())
panic("grsec: halting the system due to suspicious kernel crash caused in interrupt context");
uid = current_uid();
if (gr_is_global_root(uid))
panic("grsec: halting the system due to suspicious kernel crash caused by root");
else {
pax_open_kernel();
num_banned_users++;
pax_close_kernel();
if (num_banned_users > 8)
panic("grsec: halting the system due to suspicious kernel crash caused by a large number of different users");
/* kill all the processes of this user, hold a reference
to their creds struct, and prevent them from creating
another process until system reset
*/
printk(KERN_ALERT "grsec: banning user with uid %u until system restart for suspicious kernel crash\n",
GR_GLOBAL_UID(uid));
/* we intentionally leak this ref */
user = get_uid(current->cred->user);
if (user)
user->kernel_banned = 1;
/* kill all processes of this user */
read_lock(&tasklist_lock);
do_each_thread(tsk2, tsk) {
cred = __task_cred(tsk);
if (uid_eq(cred->uid, uid))
gr_fake_force_sig(SIGKILL, tsk);
} while_each_thread(tsk2, tsk);
read_unlock(&tasklist_lock);
}
#endif
}
#ifdef CONFIG_GRKERNSEC_BRUTE
static bool sugid_ban_expired(struct user_struct *user)
{
if (user->sugid_ban_expires != ~0UL && time_after_eq(get_seconds(), user->sugid_ban_expires)) {
user->sugid_banned = 0;
user->sugid_ban_expires = 0;
free_uid(user);
return true;
}
return false;
}
#endif
int gr_process_kernel_exec_ban(void)
{
#ifdef CONFIG_GRKERNSEC_KERN_LOCKOUT
if (unlikely(current->cred->user->kernel_banned))
return -EPERM;
#endif
return 0;
}
int gr_process_kernel_setuid_ban(struct user_struct *user)
{
#ifdef CONFIG_GRKERNSEC_KERN_LOCKOUT
if (unlikely(user->kernel_banned))
gr_fake_force_sig(SIGKILL, current);
#endif
return 0;
}
int gr_process_sugid_exec_ban(const struct linux_binprm *bprm)
{
#ifdef CONFIG_GRKERNSEC_BRUTE
struct user_struct *user = current->cred->user;
if (unlikely(user->sugid_banned)) {
if (sugid_ban_expired(user))
return 0;
/* disallow execution of suid/sgid binaries only */
else if (is_privileged_binary(bprm->file->f_path.dentry))
return -EPERM;
}
#endif
return 0;
}