From b406237766e13f3ae66f175ba0add6d65e469165 Mon Sep 17 00:00:00 2001 From: Anan Jaser Date: Mon, 19 Dec 2022 02:00:32 +0400 Subject: [PATCH] fs: proc: backport PROC_AVC from N770F * Provides filtered AVC log at /proc/avc_msg * Replaces SEC_AVC_LOG --- fs/proc/Kconfig | 8 ++ fs/proc/Makefile | 1 + fs/proc/proc_avc.c | 170 ++++++++++++++++++++++++++++++++++++++ include/linux/proc_avc.h | 7 ++ include/linux/sec_debug.h | 5 -- kernel/audit.c | 60 ++++++-------- 6 files changed, 212 insertions(+), 39 deletions(-) create mode 100755 fs/proc/proc_avc.c create mode 100755 include/linux/proc_avc.h diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index a33aa90bc311..806151d732d2 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -72,6 +72,14 @@ config PROC_PAGE_MONITOR /proc/kpagecount, and /proc/kpageflags. Disabling these interfaces will reduce the size of the kernel by approximately 4kb. +config PROC_AVC + bool "support (/proc/avc_msg)" + default n + help + support logging audit avc message + When PROC_AVC config is turned on, proc/avc_msg path is created and then, + can use it to analyze the SEandroid denial. + config PROC_CHILDREN bool "Include /proc//task//children file" default n diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 6c97dcac266b..2797afeb3402 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -33,3 +33,4 @@ proc-$(CONFIG_PROC_KCORE) += kcore.o proc-$(CONFIG_PROC_VMCORE) += vmcore.o proc-$(CONFIG_PRINTK) += kmsg.o proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o +proc-$(CONFIG_PROC_AVC) += proc_avc.o diff --git a/fs/proc/proc_avc.c b/fs/proc/proc_avc.c new file mode 100755 index 000000000000..d84151e2c0d0 --- /dev/null +++ b/fs/proc/proc_avc.c @@ -0,0 +1,170 @@ +/* + * linux/fs/proc/proc_avc.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_PROC_AVC +#include +#endif + +#define LOG_MAGIC 0x4d474f4c /* "LOGM" */ + +static unsigned int *sec_avc_log_ptr; +static char *sec_avc_log_buf; +static unsigned int sec_avc_log_size; + +int __init sec_avc_log_init(void) +{ + unsigned int size = SZ_256K; + unsigned int *sec_avc_log_mag; + + sec_avc_log_size = size; + sec_avc_log_mag = kzalloc(sec_avc_log_size, GFP_NOWAIT); + pr_info("allocating %u bytes at %p (%llx physical) for avc log\n", + sec_avc_log_size, sec_avc_log_mag, __pa(sec_avc_log_buf)); + + sec_avc_log_ptr = sec_avc_log_mag + 4; + sec_avc_log_buf = (char *)(sec_avc_log_mag + 8); + + if (*sec_avc_log_mag != LOG_MAGIC) { + pr_info("%s: no old log found\n", __func__); + *sec_avc_log_ptr = 0; + *sec_avc_log_mag = LOG_MAGIC; + } + + return 1; +} + +#define BUF_SIZE 512 +void sec_avc_log(char *fmt, ...) +{ + va_list args; + char buf[BUF_SIZE]; + int len = 0; + unsigned int idx; + unsigned int size; + + /* In case of sec_avc_log_setup is failed */ + if (!sec_avc_log_size) + return; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + idx = *sec_avc_log_ptr; + size = strlen(buf); + + if (idx + (size * 2) > sec_avc_log_size - 1) { + len = scnprintf(&sec_avc_log_buf[0], size + 1, "%s\n", buf); + *sec_avc_log_ptr = len; + } else { + len = scnprintf(&sec_avc_log_buf[idx], size + 1, "%s\n", buf); + *sec_avc_log_ptr += len; + } +} + +static ssize_t sec_avc_log_write(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + char *page = NULL; + ssize_t ret; + int new_value; + + if (!sec_avc_log_buf) + return 0; + + ret = -ENOMEM; + if (count >= PAGE_SIZE) + return ret; + + ret = -ENOMEM; + page = (char *)get_zeroed_page(GFP_KERNEL); + if (!page) + return ret; + + ret = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + ret = -EINVAL; + if (sscanf(page, "%u", &new_value) != 1) { + pr_info("%s\n", page); + /* print avc_log to sec_avc_log_buf */ + sec_avc_log("%s", page); + } + ret = count; +out: + free_page((unsigned long)page); + return ret; +} +static ssize_t sec_avc_log_read(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + if (pos >= (*sec_avc_log_ptr & (sec_avc_log_size - 1))) + return 0; + + count = min(len, + (size_t)((*sec_avc_log_ptr & (sec_avc_log_size - 1)) - pos)); + if (copy_to_user(buf, sec_avc_log_buf + pos, count)) + return -EFAULT; + + *offset += count; + return count; +} + +static const struct file_operations avc_msg_file_ops = { + .owner = THIS_MODULE, + .read = sec_avc_log_read, + .write = sec_avc_log_write, + .llseek = generic_file_llseek, +}; + +static int __init sec_avc_log_late_init(void) +{ + struct proc_dir_entry *entry; + + if (sec_avc_log_buf == NULL) { + pr_err("%s: sec_avc_log_buf not initialized.\n", __func__); + return 0; + } + + entry = proc_create_data("avc_msg", S_IFREG | 0x444, NULL, &avc_msg_file_ops, NULL); + if (!entry) { + pr_err("%s: failed to create proc entry\n", __func__); + return 0; + } + + proc_set_size(entry, sec_avc_log_size); + return 0; +} + +late_initcall(sec_avc_log_late_init); + +int __init sec_log_init(void) +{ +#ifdef CONFIG_PROC_AVC + sec_avc_log_init(); +#endif + return 0; +} +fs_initcall(sec_log_init); diff --git a/include/linux/proc_avc.h b/include/linux/proc_avc.h new file mode 100755 index 000000000000..135782a357ae --- /dev/null +++ b/include/linux/proc_avc.h @@ -0,0 +1,7 @@ +/* + * linux/include/linux/proc_avc.h + * + */ + +extern int __init sec_avc_log_init(void); +extern void sec_avc_log(char *fmt, ...); diff --git a/include/linux/sec_debug.h b/include/linux/sec_debug.h index 4e4bf1851805..7ffecac00bc4 100644 --- a/include/linux/sec_debug.h +++ b/include/linux/sec_debug.h @@ -471,11 +471,6 @@ extern void sec_debug_save_last_kmsg(unsigned char *head_ptr, unsigned char *cur /* * Samsung TN Logging Options */ -#ifdef CONFIG_SEC_AVC_LOG -extern void sec_debug_avc_log(char *fmt, ...); -#else -#define sec_debug_avc_log(a, ...) do { } while (0) -#endif /* CONFIG_SEC_AVC_LOG */ /** * sec_debug_tsp_log : Leave tsp log in tsp_msg file. diff --git a/kernel/audit.c b/kernel/audit.c index 4c9a38b6a4e2..c66829adf27e 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -69,11 +69,11 @@ #include "audit.h" -// [ SEC_SELINUX_PORTING_EXYNOS -#ifdef CONFIG_SEC_AVC_LOG -#include +// [ SEC_SELINUX_PORTING_COMMON +#ifdef CONFIG_PROC_AVC +#include #endif -// ] SEC_SELINUX_PORTING_EXYNOS +// ] SEC_SELINUX_PORTING_COMMON /* No auditing will take place until audit_initialized == AUDIT_INITIALIZED. * (Initialization happens after skb_init is called.) */ @@ -85,13 +85,18 @@ static int audit_initialized; #define AUDIT_OFF 0 #define AUDIT_ON 1 #define AUDIT_LOCKED 2 -u32 audit_enabled = AUDIT_OFF; -u32 audit_ever_enabled = !!AUDIT_OFF; +// [ SEC_SELINUX_PORTING_COMMON +u32 audit_enabled = AUDIT_ON; +u32 audit_ever_enabled = !!AUDIT_ON; +// ] SEC_SELINUX_PORTING_COMMON EXPORT_SYMBOL_GPL(audit_enabled); /* Default state when kernel boots without any parameters. */ -static u32 audit_default = 1; +// [ SEC_SELINUX_PORTING_COMMON +// Samsung Change Value from AUDIT_OFF to AUDIT_ON +static u32 audit_default = AUDIT_ON; +// ] SEC_SELINUX_PORTING_COMMON /* If auditing cannot proceed, audit_failure selects what happens. */ static u32 audit_failure = AUDIT_FAIL_PRINTK; @@ -398,18 +403,20 @@ static void audit_printk_skb(struct sk_buff *skb) struct nlmsghdr *nlh = nlmsg_hdr(skb); char *data = nlmsg_data(nlh); - if (nlh->nlmsg_type != AUDIT_EOE) { -// [ SEC_SELINUX_PORTING_EXYNOS -#ifdef CONFIG_SEC_AVC_LOG - sec_debug_avc_log("type=%d %s\n", nlh->nlmsg_type, data); +// [ SEC_SELINUX_PORTING_COMMON +#ifdef CONFIG_PROC_AVC + if (nlh->nlmsg_type != AUDIT_EOE && nlh->nlmsg_type != AUDIT_NETFILTER_CFG) + sec_avc_log("%s\n", data); #else + if (nlh->nlmsg_type != AUDIT_EOE) { if (printk_ratelimit()) pr_notice("type=%d %s\n", nlh->nlmsg_type, data); else audit_log_lost("printk limit exceeded"); -#endif -// ] SEC_SELINUX_PORTING_EXYNOS } +#endif +// ] SEC_SELINUX_PORTING_COMMON + audit_hold_skb(skb); } @@ -446,20 +453,9 @@ restart: } /* we might get lucky and get this in the next auditd */ audit_hold_skb(skb); - } else { -// [ SEC_SELINUX_PORTING_EXYNOS -#ifdef CONFIG_SEC_AVC_LOG - struct nlmsghdr *nlh = nlmsg_hdr(skb); - char *data = NLMSG_DATA(nlh); - - if (nlh->nlmsg_type != AUDIT_EOE && nlh->nlmsg_type != AUDIT_NETFILTER_CFG) - sec_debug_avc_log("%s\n", data); -#endif -// ] SEC_SELINUX_PORTING_EXYNOS + } else /* drop the extra reference if sent ok */ consume_skb(skb); - } -// ] SEC_SELINUX_PORTING_EXYNOS } /* @@ -513,20 +509,18 @@ static void flush_hold_queue(void) { struct sk_buff *skb; -// [ SEC_SELINUX_PORTING_COMMON - if (!audit_default || !audit_pid || !audit_sock) + if (!audit_default || !audit_pid) return; -// ] SEC_SELINUX_PORTING_COMMON + skb = skb_dequeue(&audit_skb_hold_queue); if (likely(!skb)) return; -// [ SEC_SELINUX_PORTING_COMMON - while (skb && audit_pid && audit_sock) { + while (skb && audit_pid) { kauditd_send_skb(skb); skb = skb_dequeue(&audit_skb_hold_queue); } -// ] SEC_SELINUX_PORTING_COMMON + /* * if auditd just disappeared but we * dequeued an skb we need to drop ref @@ -548,10 +542,8 @@ static int kauditd_thread(void *dummy) if (!audit_backlog_limit || (skb_queue_len(&audit_skb_queue) <= audit_backlog_limit)) wake_up(&audit_backlog_wait); -// [ SEC_SELINUX_PORTING_COMMON - if (audit_pid && audit_sock) + if (audit_pid) kauditd_send_skb(skb); -// ] SEC_SELINUX_PORTING_COMMON else audit_printk_skb(skb); continue;