QortalOS Brooklyn for Raspberry Pi 4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

306 lines
6.2 KiB

#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/mman.h>
#include <net/sock.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/gracl.h>
#include <linux/grsecurity.h>
#include <linux/grinternal.h>
#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE)
#include <linux/magic.h>
#include <linux/pagemap.h>
#include "../fs/btrfs/async-thread.h"
#include "../fs/btrfs/ctree.h"
#include "../fs/btrfs/btrfs_inode.h"
#endif
static struct crash_uid *uid_set;
static unsigned short uid_used;
static DEFINE_SPINLOCK(gr_uid_lock);
extern rwlock_t gr_inode_lock;
extern struct acl_subject_label *
lookup_acl_subj_label(const u64 inode, const dev_t dev,
const struct acl_role_label *role);
int
gr_init_uidset(void)
{
uid_set =
kmalloc(GR_UIDTABLE_MAX * sizeof (struct crash_uid), GFP_KERNEL);
uid_used = 0;
return uid_set ? 1 : 0;
}
void
gr_free_uidset(void)
{
if (uid_set) {
struct crash_uid *tmpset;
spin_lock(&gr_uid_lock);
tmpset = uid_set;
uid_set = NULL;
uid_used = 0;
spin_unlock(&gr_uid_lock);
if (tmpset)
kfree(tmpset);
}
return;
}
int
gr_find_uid(const uid_t uid)
{
struct crash_uid *tmp = uid_set;
uid_t buid;
int low = 0, high = uid_used - 1, mid;
while (high >= low) {
mid = (low + high) >> 1;
buid = tmp[mid].uid;
if (buid == uid)
return mid;
if (buid > uid)
high = mid - 1;
if (buid < uid)
low = mid + 1;
}
return -1;
}
static void
gr_insertsort(void)
{
unsigned short i, j;
struct crash_uid index;
for (i = 1; i < uid_used; i++) {
index = uid_set[i];
j = i;
while ((j > 0) && uid_set[j - 1].uid > index.uid) {
uid_set[j] = uid_set[j - 1];
j--;
}
uid_set[j] = index;
}
return;
}
static void
gr_insert_uid(const kuid_t kuid, const unsigned long expires)
{
int loc;
uid_t uid = GR_GLOBAL_UID(kuid);
if (uid_used == GR_UIDTABLE_MAX)
return;
loc = gr_find_uid(uid);
if (loc >= 0) {
uid_set[loc].expires = expires;
return;
}
uid_set[uid_used].uid = uid;
uid_set[uid_used].expires = expires;
uid_used++;
gr_insertsort();
return;
}
void
gr_remove_uid(const unsigned short loc)
{
unsigned short i;
for (i = loc + 1; i < uid_used; i++)
uid_set[i - 1] = uid_set[i];
uid_used--;
return;
}
int gr_find_and_remove_uid(uid_t uid)
{
int loc;
spin_lock(&gr_uid_lock);
loc = gr_find_uid(uid);
if (loc >= 0)
gr_remove_uid(loc);
spin_unlock(&gr_uid_lock);
return loc >= 0 ? 1 : 0;
}
int
gr_check_crash_uid(const kuid_t kuid)
{
int loc;
int ret = 0;
uid_t uid;
if (unlikely(!gr_acl_is_enabled()))
return 0;
uid = GR_GLOBAL_UID(kuid);
spin_lock(&gr_uid_lock);
loc = gr_find_uid(uid);
if (loc < 0)
goto out_unlock;
if (time_before_eq(uid_set[loc].expires, get_seconds()))
gr_remove_uid(loc);
else
ret = 1;
out_unlock:
spin_unlock(&gr_uid_lock);
return ret;
}
extern int gr_fake_force_sig(int sig, struct task_struct *t);
void
gr_handle_crash(struct task_struct *task, const int sig)
{
struct acl_subject_label *curr;
struct task_struct *tsk, *tsk2;
const struct cred *cred;
const struct cred *cred2;
if (sig != SIGSEGV && sig != SIGKILL && sig != SIGBUS && sig != SIGILL)
return;
if (unlikely(!gr_acl_is_enabled()))
return;
curr = task->acl;
if (!(curr->resmask & (1U << GR_CRASH_RES)))
return;
if (time_before_eq(curr->expires, get_seconds())) {
curr->expires = 0;
curr->crashes = 0;
}
curr->crashes++;
if (!curr->expires)
curr->expires = get_seconds() + curr->res[GR_CRASH_RES].rlim_max;
if ((curr->crashes >= curr->res[GR_CRASH_RES].rlim_cur) &&
time_after(curr->expires, get_seconds())) {
int is_priv = is_privileged_binary(task->mm->exe_file->f_path.dentry);
rcu_read_lock();
cred = __task_cred(task);
if (gr_is_global_nonroot(cred->uid) && is_priv) {
gr_log_crash1(GR_DONT_AUDIT, GR_SEGVSTART_ACL_MSG, task, curr->res[GR_CRASH_RES].rlim_max);
spin_lock(&gr_uid_lock);
gr_insert_uid(cred->uid, curr->expires);
spin_unlock(&gr_uid_lock);
curr->expires = 0;
curr->crashes = 0;
read_lock(&tasklist_lock);
do_each_thread(tsk2, tsk) {
cred2 = __task_cred(tsk);
if (tsk != task && uid_eq(cred2->uid, cred->uid))
gr_fake_force_sig(SIGKILL, tsk);
} while_each_thread(tsk2, tsk);
read_unlock(&tasklist_lock);
} else {
gr_log_crash2(GR_DONT_AUDIT, GR_SEGVNOSUID_ACL_MSG, task, curr->res[GR_CRASH_RES].rlim_max);
read_lock(&tasklist_lock);
read_lock(&grsec_exec_file_lock);
do_each_thread(tsk2, tsk) {
if (likely(tsk != task)) {
// if this thread has the same subject as the one that triggered
// RES_CRASH and it's the same binary, kill it
if (tsk->acl == task->acl && gr_is_same_file(tsk->exec_file, task->exec_file))
gr_fake_force_sig(SIGKILL, tsk);
}
} while_each_thread(tsk2, tsk);
read_unlock(&grsec_exec_file_lock);
read_unlock(&tasklist_lock);
}
rcu_read_unlock();
}
return;
}
int
gr_check_crash_exec(const struct file *filp)
{
struct acl_subject_label *curr;
struct dentry *dentry;
if (unlikely(!gr_acl_is_enabled()))
return 0;
read_lock(&gr_inode_lock);
dentry = filp->f_path.dentry;
curr = lookup_acl_subj_label(gr_get_ino_from_dentry(dentry), gr_get_dev_from_dentry(dentry),
current->role);
read_unlock(&gr_inode_lock);
if (!curr || !(curr->resmask & (1U << GR_CRASH_RES)) ||
(!curr->crashes && !curr->expires))
return 0;
if ((curr->crashes >= curr->res[GR_CRASH_RES].rlim_cur) &&
time_after(curr->expires, get_seconds()))
return 1;
else if (time_before_eq(curr->expires, get_seconds())) {
curr->crashes = 0;
curr->expires = 0;
}
return 0;
}
void
gr_handle_alertkill(struct task_struct *task)
{
struct acl_subject_label *curracl;
__u32 curr_ip;
struct task_struct *p, *p2;
if (unlikely(!gr_acl_is_enabled()))
return;
curracl = task->acl;
curr_ip = task->signal->curr_ip;
if ((curracl->mode & GR_KILLIPPROC) && curr_ip) {
read_lock(&tasklist_lock);
do_each_thread(p2, p) {
if (p->signal->curr_ip == curr_ip)
gr_fake_force_sig(SIGKILL, p);
} while_each_thread(p2, p);
read_unlock(&tasklist_lock);
} else if (curracl->mode & GR_KILLPROC)
gr_fake_force_sig(SIGKILL, task);
return;
}