#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) #include #include #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; }